(以Hello-jni为例)
1.原生方法声明
stringFromJNI方法声明中含有关键字native以通知java编译器,它用另一种语言提供该方法的具体实现。因为原生方法没有方法体,方法声明以语句终结符分号结尾。
public native String stringFromJNI();
尽管虚拟机知道该方法被原生实现,但是仍然不知道到哪去找方法的实现。
2.加载共享库
原生方法被编译成一个共享库。需要先加载该共享库以便于虚拟机能够找到原生方法实现。java.lang.System类提供了两个静态方法。load和loadLibrary用于在运行时加载共享库。
因为想加载原生代码实现,正如类首次加载和初始化一样,所以在静态上下文中调用loadLibary方法。
Java技术的设计目标是平台独立,作为Java框架API的一部分,loadLibrary也要保持平台独立性。尽管Android NDK生成的实际共享库被命名libhello-jni.so,但是loadlibrary方法只采用hello-jni这个库名,在按照所使用的具体操作系统要求加上必要的前缀和后缀。
loadLibrary的参数也不包含共享库的位置。Java库路径,也就是系统属性java.library.path保存loadlibrary方法在共享库搜索的目录列表,Android上的java库路径包含/vendor/lib和system/lib。
需要强调的是,loadlibrary在扫描java库路径时,一旦发现同名的库,立即加载共享的库。因为Java库路径的第一组目录是Android系统的目录,为了避免与系统库命名冲突,强烈建议Android开发人员为每个共享库选择唯一的名字。
3.实现原生方法
hello.c源文件以jni.h头文件包含语句开头,这个头文件中包含JNI数据类型和函数定义。
原生方法stringFromJNI用一个名为Java_com_example_hellojni_stringFromJNI的完全限定的函数来声明,这种显示的函数命名让虚拟机在加载的共享库中自动查找原生函数。
1使用C/C++头文件生成器 javah
2方法声明
JNIEXPORT jstring JNICALL Java_com_example_hellojni_stringFromJNI(JNIEnv*,jobject);
第一个参数JNIENV是只想可用JNI函数表的接口指针;第二个参数jobject是HelloJni类实例的Java对象引用。
(1)JNIEnv接口指针
原生代码通过JNIEnv接口指针提供的各种函数来使用虚拟机的功能。JNIEnv是一个指向县城-局部数据的指针,而县城-局部数据中包含指向函数表 的指针。实现原生方法的函数将JNIEnv接口指针作为他们的第一参数。
(2)实例方法与静态方法
Java程序设计语言有两类方法:实例方法和静态方法。实例方法与类实例相关,他们只能在类实例中调用。静态方法不与类实力相关,他们可以在静态上下文直接调用。他们可以在静态上下文直接调用。静态方法 和实例方法均可以声明为原生的,可以通过JNI技术以原生代码的形式提供他们的实现。原生实例方法通过第二个参数获取实例引用,该参数是jobject类型的。
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv*,jobject this);
因为静态方法没有与实力绑定,因此通过第二个参数获取类引用而不是实力引用,第二个参数是jclass值类型。
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv*,jclass this);
正如在方法定义中看到的,JNI提供了自己的数据类型从而让原生代码了解Java数据类型。
源自《Android C++高级编程–使用NDK》书中内容