来来了解一下JNI吧,也许工作中并不一定用得着,但是也得知道一下大概内容呀。
总结安卓中关于JNI的基础知识,有以下三个部分。
(第一部分)
A) 创建一个类(HelloWorld.java)或者在原来的类com.example.hellojni.HelloJNI中声明本地方法。
(1)使用关键字native声明本地方法,表明这两个函数需要通过本地代码C/C++实现。
public native String stringFromJNI();
(2)加载动态库libHelloJNI.so
static{
System.loadLibrary(“HelloJNI”);
}
B) 使用javac编译源文件HelloWorld.java,生成HelloWorld.class。
(1)通过Eclipse编译代码,生成.class文件。
C) 使用javah –jni来生成头文件,这个头文件里面包含了本地方法的函数原型。
(1)在新建成的工程根目录下,键入以下命令:
$javah –classpath bin/classes –d jni com.example.hellojni.HelloJNI
(2)运行完之后,会在本地生成一个名为jni的目录,里面有一个名为com_example_hellojni_HelloJNI.h的头文件,这个文件就是我们需要的头文件,它包含了我们先前声明的两个函数,还加入了相应的包名作为前缀。
D) 编写C/C++代码(HelloWorld.c)来实现头文件中的函数原型。
按照com_example_hellojni_HelloJNI.h中声明的函数名,在jni目录下建立一个HelloJNI.c,实现其函数体。
E) 将HelloWorld.c编译成一个动态库,生成HelloWorld.so或HelloWorld.dll。
(1) 下载ndk,在jni目录下输入ndk-build命令。
(2) 将会在HelloJNI/libs/armeabi目录下生成一个名为libHelloJNI.so的动态库,生成apk时会把这个库一起打包。
(第二部分)
A)JNI的入口函数,当执行到System.loadLibray(“HelloJNI”)时,会在第一次使用该类的时候加载动态库libHelloJNI.so,当Android的VM执行到System.loadLibrary()这个函数时,首先会执行JNI_OnLoad()这个函数,执行这个函数的意图包括:
(1)告诉VM此C组件使用哪一个JNI版本。如果动态库中没有提供JNI_onLoad(),VM默认使用最老的JNI 1.1版本。
(2)初始化,例如预先申请资源等等。
B)使用ndk进行编译。
C)使用registerNativeMethods方法
(1)在jni目录下新建HelloJNI.c:
jstring stringFromJNI(JNIEnv* env, jobject this)
static JNINativeMethod gMethods[] = …
int register_location_methods(JNIEnv *env)
jint JNI_Onload(JavaVm* vm, void *reserved);
void JNI_Onload(JavaVm* vm, void *reserved);
(2)其中的JNI_OnLoad()函数包括以下步骤:获取JNI环境对象,登记本地方法,最后返回JNI版本。
(3)声明实例:
Static JNINativeMethod gMethods[] = {
{ “stringFromJNI”,“()Ljava/lang/String”,(void*)stringFromJNI}
}
D)使用registerNativeMethods的好处:
(1)不需要使用长方法名。
(2)提高效率。当Java类通过VM呼叫到本地函数时,通常是依靠VM去动态寻找动态库中的本地函数(因此才需要特定规则的命名格式),如果某方法需要连续呼叫许多次,则每次都要寻找一遍,所以使用registerNativeMethods将本地函数向VM登记,可以更有效率地找到函数。
(3)运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次调用registerNativeMethods方法,并传入不同的映射表参数即可。
(第三部分:优缺)
(1)JNI的强大特性使我们在使用Java平台的同时,还可以重用原来的本地代码,作为虚拟机实现的一部分,JNI允许Java和本地代码间的双向交互,你可以使用JNI来实现本地方法,并在Java程序中调用它们。
(2)JNI的副作用
A)应用不再跨平台,要想跨平台,必须在不同的系统环境下重新编译本地语言部分。
B)程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。