[卷I]第2章 深入理解JNI

JAVA世界 MediaScanner MediaScanner.java

native_init();

processFile();

JNI层 libmedia_jni.so android_media_mediaScanner.cpp android_media_MediaScanner_native_init
Natvie世界 libmedia.so    

关注:native_init 与 android_media_MediaScanner_native_init是如何对应的?注册。

注册有静态注册和动态注册两种:

1、静态注册,编写MediaScanner.java java -o javah -o 生成android_media_MediaScanner.h

JNIEXPORT void JNICALL Java_android_media_MediaScanner_native_linit

JNIEXPORT void JNICALL Java_android_media_MediaScanner_processFile

2、动态注册

由结构体JNINativeMethod保存对应关系

由register_android_media_MediaScanner(JNIEnv *env) 调用注册函数

而register_android_media_MediaScanner由JNI_OnLoad调用(JNI_OnLoad需要自己实现)

因为:当JAVA层通过System.loadLibrary加载完JNI动态库后,紧接着就会查找库中的JNI_OnLoad函数。

2.4.3 JNIEnv介绍

JNIEnv 与线程相关的代表JNI环境的结构体。

JNIEnv一般都是native函数转换成JNI层函数后由虚拟机传进来的,可以直接使用。

但如果是后台线程收到一个网络消息,而又需要由native层函数主动回调Java层函数时,JNIEnv从何而来?

注意:

jint JNI_OnLoad(JavaVM* vm, void* reserved)

一个进程对应一个JavaVM,即JavaVM是与进程相关的。

调用JavaVM的AttachCurrentThread函数,就可得到这个线程的JNIEnv结构体。另外,在后台线程退出前,需要调用JavaVM的DetachCurrentThread函数来释放对应的资源。

2.4.4 通过JNIEnv操作jobject

先写一下体会:

在JNI层,先通过GetFieldID,取得在Java层定义的成员变量,如mNativeContext(在MediaScanner.java中private int mNativeContext;)

给jfieldID:fields.context = env->GetFieldID(clazz, "mNativeContext", "I");

且fields是文件中的全局变量。

在Java层会通过native函数调用,到JNI层调用设置来设置Java层的mNativeContext。(呵呵,这是用下层的方法设置自己层的变量啊)。

MediaScanner.java native_setup();

android_media_MediaScanner.cpp android_media_MediaScanner_native_setup env->SetIntField(thiz, fields.context, (int)mp);

当然,设置好后,JNI层可以通过(MediaScanner *)env->GetIntField(thiz, fields.context);直接使用。

在MediaScanner.java中,代码逻辑非常明确:

加载完jni库后调用native_init,会让jni层取得mNativeContext

创建MediaScanner对象时,会调用native_setup,将StagefrightMediaScanner对象指针给mNativeContext;

对象销毁时,调用native_finalize,将mNativeContext设为0。

------------------------------------------------------------------------------------------------------------

JNI层会用jobject来表示对象的数据类型。

在JNI规则中,用jfiledID和jmethodID来表示Java类的成员变量和成员函数,可通过JNIEnv的下面两个函数得到:

jfieldID GetField(jclass clazz, const char * name, const char *sig);

jmethodID GetMethodID(jclass clazz, const char * name, const char *sig);

jclass代表java类,name表示成员函数或成员变量的名字,sig为这个函数和变量的签名信息。

调用 GetMethodID 保存为成员变量,为了程序运行效率。

        mScanFileMethodID = env->GetMethodID(
                                    mediaScannerClientInterface,
                                    "scanFile",
                                    "(Ljava/lang/String;JJZZ)V");

取得Java对象的方法scanFile。

        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
                fileSize, isDirectory, noMedia);

CallVoidMethod是调用Java对象的函数scanFile。

2.4.5 jstring介绍

调用JNIEnv的NewString(JNIEnv *env, const jchar* unicodeChars, jsize len),可以从Native的字符串得到一个jstring对象。

调用JNIEnv的NewStringUTF将根据Native的一个UTF-8字符串得到一个jstring对象。

GetStringChars GetStringUTFChars 可以将Java String对象转换成本地字符串。

需要调用ReleaseStringChars ReleaseStringUTFChars释放资源, 否则会导致JVM内存泄露。

2.4.7 垃圾回收

JNI技术一共提供三种类型的引用:

Local Reference 本地引用,一旦JNI层函数返回,就可能被垃圾回收。

Global Reference 全局引用,不主动释放,永远不会被回收。

Weak Global Reference 弱全局引用,一种特殊的Global Reference,在运行中可能被回收,所以在使用之前需要调用JNIEnv的IsSameObject判断。

根据Local Reference的说明,函数返回后,对象就会被回收,看起来调用DeleteLocalRef是多余的,其实,调用DeleteLocalRef是立即回收。

所以,没有及时回收Local Reference或许是进程占用内存过多的一个原因。

原文地址:https://www.cnblogs.com/jimwind/p/3414484.html