说明

java如果需要调用c 或者 c++写的库(windwos下为dll的文件,linux下为so的文件),需要用到jna或者jni技术。

调用方式

一.jni(Java Native Interface)

1.说明

  • jni是Java SDK中的一部分,提供一种Java字节码调用C/C++的解决方案,JNI描述的是一种技术。
  • jni使用比较麻烦,使用jni的前提是动态库内部需要按java的方式把每个API封装一次。
  • jni的调用效率要比jna高得多,如果一个操作涉及到java虚拟机与外部动态库的频繁调用,且对程序执行效率有要求的话,建议使用jni。

2.使用

//完整类样例:
public class Mp3DecoderJni {
    //类加载时加载库
    static {
        System.loadLibrary("mp3decoder");
    }

    //库中对应的方法
    public native void addMp3Frame(byte[] val,int length);

    //库里面回调这个方法,里面写具体逻辑,前提是库里面有调这个方法
    public int onDecodeCall(byte[] data , int length , int samplingrate){
        return 0;
    }
}

3.语法

  • 其中System.loadLibrary的第一个参数为动态库的路径,相对/绝对,.so/.dll后缀可加可不加,建议不加,跨平台就不用改缀名了。
  • System.loadLibrary加载的动态库是需要含有jni语法层的。

二.jna(Java Native Access)

1.说明

  • jna使用需要引入第三方库。
  • jna是jni的更高封装,jna使用方便些,不需要动态库内部按按java的方式把每个API封装一次。
  • jna基于jni技术开发,主要在上层作了类型自动转换的封装,无需jni那样的繁杂的接口层开发。

2.引入

maven
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.3.1</version>
</dependency>

3.使用

//完整类样例1:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public class HelloWorld {
    public interface CLibrary extends Library {
        CLibrary INSTANCE = Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);

        void printf(String format, Object... args);
    }

    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
    }
}
//完整类样例2:

//CLibrary.java
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public interface CLibrary extends Library {
        CLibrary INSTANCE = Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class);
        void printf(String format, Object... args);
}

//Test.java
public class Test {
    public static void main(String[] args) {
        CLibrary.INSTANCE.printf("Hello, World\n");
    }
}

//通过此demo可以看出调用时CLibrary的INSTANCE,并不需要声明是public static的。
//printf是msvcrt.dll库提供的方法。

4.语法

  • 使用jna,需要创建一个接口继承Library,然后在创建一个 Native.load创建一个自己的实例。然后在接口编码库中对应的方法和入参即可,并不需要跟jni一样加native标识符。
  • 其中Native.load的第一个参数为动态库的路径,相对/绝对,.so/.dll后缀可加可不加,建议不加,跨平台就不用改缀名了。
  • 其中"msvcrt"在项目中相对路径是没对应dll,为什么还是可以执行?因为msvcrt为windwos自带的dll,这样写也是可以正常使用的,我们可以直接在C:\Windows\System32找到msvcrt.dll文件。
  • demo中的msvcrt,msvcrt.dll是微软在windows操作系统中提供的C语言运行库执行文件(Microsoft Visual C Runtime Library),其中提供了printf,malloc,strcpy等C语言库函数的具体运行实现,并且为使用C/C++(Vc)编译的程序提供了初始化(如获取命令行参数)以及退出等功能;demo中的c,c.so应该是linux中自带的类似msvcrt的库。

5.回调

回调用来实现c调用java的函数。

//完整类样例1:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.*;

public interface JnaTest extends Library{
    JnaTest INSTANCE = Native.load("main",JnaTest.class);

	//1.与动态库中对应的方法
    int test_callback(addCallback f);

	//2.创建一个接口继承Callback/StdCallCallback
    public interface myCallback extends Callback{
        public int callback(int x,int y);
    }

	//3.实现2中的接口
    public static class addCallback implements myCallback{
        @Override
        public int callback(int x,int y){
            return x + y;	//返回值根据实际业务传入相关的值
        }
    }
    
    //4.调用1中对应方法传入3中回调方法
    public static void main(String[] args) {
        JnaTest.INSTANCE.test_callback(new JnaTest.addCallback());
    }
}

参考:
JNA回调 Java C语言