1. JNI, Java Native Interface,即 Java本地调用。
作用:JNI层作为连接Java世界与Native世界的桥梁,使Java程序中函数可以调用Native(C/C++)编写的函数,Native(C/C++)程序中可以调用Java的函数。
2. Java要调用Native函数,需要完成以下两项工作:
(1)加载对应的JNI动态库。通常在Java类的static语句中调用System.loadLibrary方法动态加载JNI库。
(2)声明由关键字native修饰的函数。native为Java关键字,表示它由JNI层完成。
3. 注册JNI函数,即将Java层的native函数和JNI层对应的实现函数关联起来。注册方式有两种:
(1)静态注册:根据函数名来建立Java函数与JNI函数之间的关联关系。
步骤:i)先编写Java代码,然后生成编译生成.class文件。
ii)使用Java工具程序javah,如 javah -o output packagename.classname,生成一个名叫output.h的JNI层头文件,其中packagename.classname为Java代码编译后的class文件。在output.h中声明了对应的JNI层函数,只要实现里面的函数即可。
(2)动态注册:在JNI层代码中调用JNIEnv的RegisterNatives函数实现注册关联关系。
动态注册的时机:Java层通过System.loadLibrary()加载完JNI动态库后,会查找该库中的JNI_OnLoad函数,如果找到,就调用,因此动态注册的工作可以放在这个函数中执行。
4. JNIEnv
(1)JNIEnv是一个与线程相关的代表JNI环境的结构体,提供了一些JNI系统函数,通过这些函数可以:
- 调用Java的函数。
- 操作jobject对象等事情。
(2)JavaVM与JNIEnv的关系
整个进程只有一个JavaVM,在线程中通过JavaVM的AttachCurrentThread()函数,就可以得到该线程的JNIEnv结构体,从而可以在后台线程中回调Java函数了。在后台线程退出前,要调用JavaVM的DetachCurrentThread()函数来释放对应资源。
5. 垃圾回收
JNI的三种引用类型:
(1) Local Reference:本地引用。 JNI层函数中使用的非全局引用对象都是Local Reference,包括函数调用时传入的jobject和在JNI层函数中创建的jobject。一旦JNI层函数返回,jobject就可能被垃圾回收。
(2) Global Reference: 全局引用。这种对象若不主动释放,永远不会被垃圾回收。
(3) Weak Global Reference: 弱全局引用。在运行过程中有可能会被垃圾回收,在使用它之前,需要调用JNIEnv的IsSameObject判断它是否被回收。
由于Java层的对象有可能被垃圾回收了,若JNI层中将Java层的对象存为Local Reference,则有可能出现异常。所以,JNI层中要保存Java层的对象时,使用Global Reference,使用完之后进行释放。