预报
后续可能会推更一个FFmpeg系列的入门博客,大略波及到FFmpeg解封装、FFmpeg编解码、FFmpeg进行音频重采样、应用FFMpeg将mp3转码成aac、应用FFmpeg合并拼接音视频等。
另外如果有工夫可能也会更新几篇对于ffplay的文章,敬请关注。
本文将作为JNI系列的一个结尾,上面是笔者在学习应用JNI的所记录的一些笔记与技巧。
JNIEnv的线程限度
一个JNIEnv指针仅在其相关联的线程中无效。你不能将这个指针从一个线程中传递给另一个线程,或者在多线程中缓存和应用它。Java虚拟机在同一个线程的间断调用中传递给本地办法雷同的JNIEnv指针,然而从不同线程中调用本地办法时传递的是不同的JNIEnv指针。该当防止缓存一个线程的JNIEnv指针并在另一个线程中应用指针的常见谬误。
本地援用(部分援用)仅在创立它的线程中无效。你不能将本地援用从一个线程中传递到另一个线程。每当有多个线程可能应用雷同援用的可能性时,应始终将本地援用转换为全局援用。
JNIEnv是用作线程部分存储。因而,使用者不能在多线程间共享一个JNIEnv变量。如果在一段代码中没有其它方法取得它的JNIEnv,使用者能够共享JavaVM对象,应用GetEnv来获得该线程下的JNIEnv。
如果你应用AttachCurrentThread连贯(attach)了Native过程,正在运行的代码在线程拆散(detach)之前绝不会主动开释部分援用。使用者创立的任何部分援用必须手动删除。通常,任何在循环中创立部分援用的Native代码可能都须要做一些手动删除。
全局获取JNIEnv
一个JNIEnv指针仅在与其相关联的线程中无效。对于本地办法,这通常不是问题,因为他们从虚拟机承受JNIEnv指针作为第一个参数。然而有时候可能不须要间接从虚拟机调用的本地代码来获取属于以后线程的JNIEnv接口指针。例如通过JNI在Native开启了一个子线程解决某些工作,在这些工作处理完毕后须要将处理结果回调给java层。
这种状况能够通过缓存JavaVM获取以后线程的JNIEnv而后进行java办法的回调。
当System加载一个本地库时,虚构机会在本地库中查找下述的导出的程序入口:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved);
此时咱们能够将JavaVM缓存下来,供当前获取JNIEnv应用。
上面是在任何地位获取JNIEnv的例子:
JavaVM *globalJVM = nullptr;JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { globalJVM = jvm; return JNI_VERSION_1_6;}JNIEnv *getCurrentEnv(int *attach) { if (globalJVM == nullptr) return nullptr; *attach = 0; JNIEnv *jni_env = nullptr; int result = globalJVM->GetEnv((void **) &jni_env, JNI_VERSION_1_6); if (result == JNI_EDETACHED || jni_env == nullptr) { result = globalJVM->AttachCurrentThread(&jni_env, nullptr); if (result < 0) { jni_env = nullptr; } else { *attach = 1; } } return jni_env;}
不要混同ID和援用
JNI将对象作为援用。类,字符串和数组是非凡类型的援用。JNI将办法和字段作为ID。一个ID不是一个参考。不要将类援用称为“类ID”,也不要将办法ID称为“办法援用”。
援用是能够由本地代码显式治理的虚拟机资源。例如,JNI函数DeleteLocalRef容许本地代码删除本地援用。相比之下,字段和办法ID由虚拟机治理并放弃无效直到其定义的类被卸载。在虚拟机卸载定义的类之前,本机代码不能显式删除字段或办法ID。
缓存字段和办法ID
本地代码通过将字段或办法的名称和类型描述符指定为字符串而后从虚拟机获取字段或办法ID。应用名称和类型字符串的字段和办法查找速度很慢。缓存这些ID通常是无利的,未能缓存字段和办法ID是本机代码中的常见性能问题。
缓存字段或办法ID倡议应用类的动态代码块的形式进行缓存。
防止适量创立本地援用
尽管说本地援用会在函数完结时主动开释,然而JNI对于本地援用的个数是有肯定的限度的,个别是限度到512个,因而须要留神一些调用链比拟长的函数或者是在循环体内返回的本地援用在应用结束后及时进行开释,以保障GC的失常工作和内存的稳固。
NDK谬误定位
在开发的过程中常常会呈现一些Native层的解体,而后在Logcat中又没有显示具体位置的,这时候能够应用NDK中的addr2line
工具包进行定位。
addr2line的命令应用形式如下:
addr2line的绝对路径 -C -f -e so文件的绝对路径 谬误内存地址
其中-C -f示意打印谬误行数所在的函数名称,-e示意打印谬误地址的对应门路及行数。
留神不同的CPU架构须要应用不同的addr2line,比方mac零碎的addr2line就存在于ndk目录/toolchains/llvm/prebuilt/darwin-x86_64/bin
那么怎么通过Logcat定位到解体的内存地址呢?例如有以下解体日志:
2022-03-29 22:28:58.462 22761-22761/? A/DEBUG: ABI: 'arm64'2022-03-29 22:28:58.462 22761-22761/? A/DEBUG: Timestamp: 2022-03-29 22:28:58+08002022-03-29 22:28:58.462 22761-22761/? A/DEBUG: pid: 22652, tid: 22733, name: Thread-2 >>> com.fly.jnitest <<<2022-03-29 22:28:58.462 22761-22761/? A/DEBUG: uid: 101472022-03-29 22:28:58.462 22761-22761/? A/DEBUG: signal 5 (SIGTRAP), code 1 (TRAP_BRKPT), fault addr 0x7984411f402022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x0 00000079de54d200 x1 0000007a73fd41c0 x2 0000000000000000 x3 00000079eec9fcda2022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x4 00000079837e5c08 x5 00000079eead6059 x6 0000000000000001 x7 00000079837e58382022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x8 0000000000000000 x9 b4175a2f4989bf75 x10 0000000000430000 x11 00000000000000012022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x12 0000000000000000 x13 0000000000000000 x14 0000000000000012 x15 00000000000000ff2022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x16 00000079ef1c8748 x17 0000007a73c62350 x18 00000079802a6000 x19 00000079837e5d502022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x20 0000007a700ee0dc x21 00000079837e5d50 x22 0000587c0000587c x23 00000079837e5dd82022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x24 00000079837e5d50 x25 00000079837e5d50 x26 00000079837e6020 x27 0000007a741480202022-03-29 22:28:58.462 22761-22761/? A/DEBUG: x28 0000007ffad6a430 x29 00000079837e5cf02022-03-29 22:28:58.462 22761-22761/? A/DEBUG: sp 00000079837e5ce0 lr 0000007984411f3c pc 0000007984411f402022-03-29 22:28:58.462 22761-22761/? A/DEBUG: backtrace:2022-03-29 22:28:58.463 22761-22761/? A/DEBUG: #00 pc 0000000000000f40 /data/app/com.fly.jnitest-X01T6VOuYKufX3tBWVg2vA==/lib/arm64/libjnitest.so (test(void*)+24) (BuildId: f06b5f684113a965be07abbcf0bb4e5488d31870)2022-03-29 22:28:58.463 22761-22761/? A/DEBUG: #01 pc 00000000000e1100 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) (BuildId: c042ffb4e195c9462700c20f99189c2b)2022-03-29 22:28:58.463 22761-22761/? A/DEBUG: #02 pc 0000000000083ab0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: c042ffb4e195c9462700c20f99189c2b)
那么 backtrace:
所在的下一行就是解体的内存地址,也就是说下面解体日志的谬误地址是0000000000000f40
。
参考资料
《JNI编程指南与标准》
举荐浏览
JNI根底简介
JNI之数组与字符串的应用
JNI之动静注册与动态注册
JNI之拜访java属性和办法
JNI之缓存与援用
JNI之异样解决
关注我,一起提高,人生不止coding!!!