<pre mdtype="fences" cid="n72" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">jstring MyNewString(JNIEnv env, jchar chars,jint len){staticjclassstringClass = NULL; //static 不能保存一个局部引用jmethodIDcid;jcharArrayelemArr;jstringresult;if(stringClass== NULL) {stringClass =env->FindClass("java/lang/String"); // 局部引用if(stringClass == NULL) {return NULL; / exception thrown / } } / 本地代码中创建的字符串为局部引用,当函数返回后字符串有可能被gc回收 / cid =env->GetMethodID(stringClass,"<init>","([C)V"); result=env->NewStringUTF(stringClass, cid, “Hello World”); returnresult;}</pre>
尽管局部引用会在本地代码运行之后自己主动释放,可是有下列情况时;要手动释放:● 本地代码訪问一个非常大的Java对象时,在使用完该对象后; 本地代码要去运行比較复杂耗时的运算时,由于本地代码还没有返回。Java收集器无法释放该本地引用的对象,这时,应该手动释放掉该引用对象● 本地代码创建了大量局部引用; 这可能会导致JNI局部引用表溢出,此时有必要及时地删除那些不再被使用的局部引用比方:在本地代码里创建一个非常大的对象数组jni.h头文件里定义了JNI本地方法与Java方法映射关系结构体JNINativeMethod● 创建的工具函数,它会被未知的代码调用,在工具函数里使用完的引用要及时释放● 不返回的本地函数比如,一个可能进入无限事件分发的循环中的方法此时在循环中释放局部引用,是至关重要的,这样才干不会无限期地累积;进而导致内存泄露局部引用仅仅在创建它们的线程里有效;本地代码不能将局部引用在多线程间传递。一个线程想要调用还有一个线程创建的局部引用是不被同意的将一个局部引用保存到全局变量中,然后在其他线程中使用它,这是一种错误的编程全局引用在一个本地方法被多次调用时,能够使用一个全局引用跨越它们一个全局引用能够跨越多个线程,而且在被程序猿手动释放之前,一直有效; 和局部引用一样,全局引用保证了所引用的对象不会被垃圾回收JNI同意程序猿通过局部引用来创建全局引用, 全局引用仅仅能由NewGlobalRef函数创建以下是一个使用全局引用样例:<pre mdtype="fences" cid="n94" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">jstringMyNewString(JNIEnv env, jchar chars,jint len){ staticjclassstringClass = NULL; ...省略部分代码 if(stringClass == NULL) { jclasslocalRefCls =env->FindClass("java/lang/String"); if(localRefCls == NULL) { return NULL; } /创建全局引用并指向局部引用 / stringClass = env->NewGlobalRef(localRefCls); /删除局部引用 / env->DeleteLocalRef(localRefCls); /推断全局引用是否创建成功 / if(stringClass == NULL) { return NULL; / out of memory exception thrown / } }}</pre>
在native代码不再须要訪问一个全局引用的时候,应该调用DeleteGlobalRef来释放它。假设调用这个函数失败。Java VM将不会回收相应的对象Native方法注册静态注册新建 xx.java 也就是 JNI 对应的 Java层的类其中定义好要使用的方法 在xx.java目录下执行下面两条命令<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n115" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">javac com/example/xx.javajavah com.example.xx</pre>
第二条命令会在当前目录下生成 com_example_xx.h 文件,其中会生成对应的C++ 层方法,命名格式为 包名+类名+方法名的格式,用"_"分割 静态注册就是根据方法名,将Java方法和JNI函数建立关联,但它有以下缺点: 1、JNI层的函数名称过长; 2、声明Native方法类需要用javah生成头文件 3、初次调用Native方法时需要建立关联,影像效率 (2) 动态注册JNI中有一种结构用来记录 Java 的 Native 方法和JNI 方法的关联关系,它就是 JNINativeMethod,它在 jni.h 中被定义<pre mdtype="fences" cid="n108" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">typedef struct{ const char name;//Java方法的名字 const char signature;//Java方法的签名信息 void fnPtr; //JNI中对应的方法指针}JNINativeMethod;</pre>
系统的 MediaRecorder采用的就是动态注册,我们来看它的JNI层是怎么做的定义 JNINativeMethod类型的数组 gMethods,里面存储的就是 MediaRecorder 的 Native 方法与 JNI 层函数的对应关系<pre mdtype="fences" cid="n122" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">//frameworks/base/media/jni/android_media_MediaRecorder.cppstatic const JNINativeMethod gMethods[] = { {"start", "()V", (Void)android_media_MediaRecord_Start}, ...}</pre>
其中start 是 java 层的Native方法,对应的JNI 层的函数为 android_media_MediaRecord_start; ()V是start方法的签名信息; 定义完该数组后还需注册它,注册的函数为 register_android_media_MediaRecorderJNI_OnLoad 函数会在调用 System.LoadLibrary 函数后调用; 因此,注册函数就被统一定义在android_media_MediaPlayer.cpp 的 JNI_OnLoad函数中在register_android_media_MediaRecorder 方法中返回了 AndroidRuntime 的 registerNativeMethods 函数,也就是会调用该方法在Android Runtime的registerNativeMethods函数中又返回了jniRegisterNativeMethods函数,它被定义在 JNI 帮助类 JNIHelp.cpp 中jniRegisterNativeMethods 方法中最终通过调用的 JNIEnv 的 RegisterNatives 函数来完成JNI的注册数据类型的转换、基本数据类型的转换; 基本数据类型转换除了最后一行的 void,其他的数据类型只需要在前面加上 “j” 就可以了,第三列的 Signature 代表签名格式(图片来源网络,侵删)
0 评论