JNI:
Java Native Interface ;Java本地调用
JNI功能:
可以实现Java函数和Native函数的相互调用。Native函数一般指C/C++函数。
MediaScanner :
Java层的MediaScanner分析 :
(1)加载JNI库:Java要调用Native函数,就必须通过一个位于JNI层的动态库才可以完成。
(2)动态库:运行时才加载的库。Linux平台so文件,Window平台dll文件。
(3)加载时机:原则上是在调用Native函数之前,通常是在一个static块中进行加载,通过调用System.loadLibrary(media_jni)。系统会根据不同的平台拓展成为真实的库,linux平台上是libmedia_jni.so库,window平台上是media_jni.dll库。
JNI层的MediaScanner分析 :
注册:将Java层的native函数和JNI层对应的实现函数关联起来,有了这种关联,调用Java层的native函数时,就能顺利转到JNI层对应的函数执行了。
android.media.MediaScanner.native_init()
android_media_MediaScanner_native_init()
注册JNI函数 :静态注册,动态注册。
静态注册:根据函数名来找到对应得JNI函数。
(1)先把java文件编译成.class文件
(2)利用javah生成.h头文件,android_media_MediaScanner.h
Java层 native函数查找JNI函数的过程。
当Java层调用native_init()函数,它会从对应得JNI库中寻找Java_android_media_MediaScanner_native_linit函数, 如果没找到,就会报错,如果找到,则会为这个native_init()函数和
Java_android_media_mediaScanner_native_linit函数建立一个关联关系,其实就是保存JNI层函数的指针。以后调用这个native_init()函数的时候,直接使用这个函数指针即可。
缺陷:
(1)需要编译所有声明了native函数的Java类,每个生成的class文件都需要用javah来编译。
(2) 初次调用的时候要根据函数名来搜索对应的JNI层函数来建立关联关系,这样会影响执行效率。
动态注册:
1 通过JNINativeMethod这种结构(结构体)来保存Java native函数和JNI函数。
JNINativeMethod: Java中native函数的方法名+签名信息+函数指针;
2 通过AndoridRuntime.registerNativeMethods来完成注册工作,在其内部会通过JNIEnv.registerNatives来完成注册工作。
3 注册函数调用:当Java层通过System.loadLibrary加载完JNI库后,会在该库查找JNI_Onload函数,然后调用它,动态注册函数,便是在这里注册完成。
数据类型转换:###
基本数据类型转换
注意: char在native层占16位。
引用数据类型转换
注意:除class,String,Throwable以外其余的object类型都用jobject
JNIEnv:
概念:是一个与线程相关的代表JNI环境的结构体。内部存储函数指针。
功能:提供一些JNI系统函数,通过这些函数可以做到
调用Java函数
操作jobject的很多事情
调用Java函数:
JNIEnv和JVM
不能在一个线程中使用另外一个线程的JNIEnv
调用JVM的attachCurrentThread,可以获得当前线程的JNIEnv结构体,通过这个结构体可以在后台线程回调Java函数。
当后台线程退出后需要调用,用JVM的dettachCurrentThread
通过JNIEnv操作jobject
操作jobeject的实质是操作这个对象的属性和方法。
jfieldID和jmethodID
(1)可以通过JNIEnv的GetFieldID() 和 GetMethodID() 获取jfieldID和jmethodID
(2)获取之后,传入JNIEnv的CallVoidMethod 函数取调用Java方法。
(3)通过JNIEnv的get<TYPE>Field和set<TYPE>Field来获取jobject的属性和设置属性
jstring
(1) 调用JNIEnv的NewString()把一个本地字符串转换成jstring对象,Unicode字符串。
(2) 调用JNIEnv的NewUTFString()把一个本地的UTF-8字符串转换成jstring对象。
(3)getStringChars和getStringUTFChars可以把一个jstring对象转换成本地字符串。
(4)需要调用ReleaseStringChars和ReleaseStringUTFchars来释放对应的资源。
JNI签名
(1)参数值+返回值共同组成
(2)因为重载,所以仅根据函数名是无法找到相应的函数的,所以需要把参数值和返回值组合起来,这样才能顺利找到Java函数。
垃圾回收
三种引用:
Local Reference:除了Global Reference,函数调用时传入的,或者在函数中创建jobject,一旦JNI函数返回,就有可能被回收。
Global Reference: 全局引用,如果不主动释放,不会被回收,当JNI层想要保存某个Java层的对象时,就可以使用Global Reference,要记住释放。
Weak Global Reference: 弱全局引用,在使用的过程中,可能被回收,所以在使用前,需要先检查JNIEnv.IsSameObject判断是否被回收了。
JNI中的异常处理
不会中断本地函数的执行,直到从JNI层返回时,才会在Java层抛出异常。
JNIEnv的三个函数:
(1)ExceptionOccured函数,用来判断是否发生异常。
(2)ExceptionClear函数,用来清理JNI层的异常。
(3)ExceptionThrow用来向Java层抛出异常。