JNI

作者: shuixingge | 来源:发表于2016-04-28 09:54 被阅读144次

    JNI:

    Java Native Interface ;Java本地调用

    JNI功能:

    可以实现Java函数和Native函数的相互调用。Native函数一般指C/C++函数。

    MediaScanner :

    Paste_Image.png

    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函数。

    Paste_Image.png
    JNINativeMethod: Java中native函数的方法名+签名信息+函数指针;
    2 通过AndoridRuntime.registerNativeMethods来完成注册工作,在其内部会通过JNIEnv.registerNatives来完成注册工作。
    3 注册函数调用:当Java层通过System.loadLibrary加载完JNI库后,会在该库查找JNI_Onload函数,然后调用它,动态注册函数,便是在这里注册完成。

    数据类型转换:###

    基本数据类型转换

    Paste_Image.png
    注意: char在native层占16位。

    引用数据类型转换

    Paste_Image.png
    注意:除class,String,Throwable以外其余的object类型都用jobject

    JNIEnv:

    Paste_Image.png
    概念:是一个与线程相关的代表JNI环境的结构体。内部存储函数指针。
    功能:提供一些JNI系统函数,通过这些函数可以做到
    调用Java函数
    操作jobject的很多事情

    调用Java函数:
    JNIEnv和JVM
    不能在一个线程中使用另外一个线程的JNIEnv
    调用JVM的attachCurrentThread,可以获得当前线程的JNIEnv结构体,通过这个结构体可以在后台线程回调Java函数。
    当后台线程退出后需要调用,用JVM的dettachCurrentThread
    通过JNIEnv操作jobject
    操作jobeject的实质是操作这个对象的属性和方法。
    jfieldID和jmethodID
    (1)可以通过JNIEnvGetFieldID()GetMethodID() 获取jfieldID和jmethodID

    Paste_Image.png
    (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函数。

    Paste_Image.png

    垃圾回收

    三种引用:
    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层抛出异常。

    相关文章

      网友评论

      本文标题:JNI

      本文链接:https://www.haomeiwen.com/subject/jcxhrttx.html