新四季網

jni開發規範(什麼是JNI為什麼會有Native層)

2023-09-17 19:06:05 1

什麼是JNI?

JNI是Java Native Interface的縮寫,通過使用 Java本地接口書寫程序,可以確保代碼在不同的平臺上方便移植;從Java1.1開始,JNI標準成為java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互

本地代碼與 Java 虛擬機之間是通過 JNI 函數實現相互操作的;JNI 函數通過接口指針來獲得,本地方法將 JNI 接口指針當作參數來接受;虛擬機保證在從相同的 Java 線程中對本地方法進行多次調用時,傳遞給本地方法的接口指針是相同的,本地方法被不同的 Java 線程調用時,它接受不同的 JNI接口指針

使用JNI和算法進行交互,主要是為了提高算法的性能,最大化的利用機器硬體資源

Android 中的語言層

可以將語言層分成Java層和Native層;為什麼會有Native層呢?是因為在Java出現之前,很多的功能和系統都是由Native語言寫的,所以Java出現之後就不必重複造輪子,直接調用這些方法就行了,而且Native層更接近彙編語言,所以性能更優

所以代碼中就出現了Java層調用Native層的方法,而這個跨語言的調用過程就是 JNI

當Java語言無法處理一些任務的時候,就可以使用JNI來完成

下面是幾個 JNI 的應用場景:

需要調用Java語言不支持的依賴於作業系統平臺特性的一些功能

● 需要調用當前UNIX系統的某個功能,而Java不支持這個功能的時候,就要用到JNI

● 在程序對時間敏感或對性能要求特別高時,有必要用到更底層的語言來提高運行效率

● 音視頻開發涉及到的音視頻編解碼需要更快的處理速度,這就需要用到JNI

● 為了整合一些以前的非Java語言開發的系統

● 需要用到早期實現的C/C 語言開發的一些功能或者系統,將這些功能整合到當前的系統或者新的版本中

JNI是完善Java的一個重要功能,它讓Java更加全面、封裝了各個平臺的差異性

jni異常處理

在android ndk開發過程中,調用java對象方法可能會拋異常,如果在ndk層中不做任何處理,那麼程序就會直接崩潰;例如我們要在jni層獲取apk的籤名,代碼如下:

**jclass native_class = env->GetObjectClass(const_cast(contextObject)); jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager", "Landroid/content/pm/PackageManager;"); jobject pm_obj = env->CallObjectMethod(const_cast(contextObject), pm_id); jclass pm_clazz = env->GetObjectClass(pm_obj); jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); // 獲得應用包的信息 jobject pi_obj = NULL; //GET_SIGNATURES 64 pi_obj = env->CallObjectMethod(pm_obj, package_info_id, package_name, 64); ...**

上述代碼如果package_name查找不到就會報如下異常

捕獲異常

釋放資源

Native 提供了 ExceptionOccurred 和 ExceptionCheck 方法來檢測是否有異常發生,前者返回的是 jthrowable 類型,後者返回的是 jboolean 類型;如果有異常,會通過 ExceptionDescribe 方法來列印異常信息,方便我們在 LogCat 中看到對應的信息

而 ExceptionClear 方法則是關鍵的不會讓應用直接崩潰的方法,類似於 Java 的 catch 捕獲異常處理,它會消除這次異常;這樣就把由 Native 調用 Java 時的一個異常進行了處理,當處理完異常之後,別忘了釋放對應的資源

不過,我們這樣僅僅是消除了這次異常,還應該讓調用者有異常的發生,那麼就需要通過 Native 來拋出一個異常告訴 Java 調用者了

void throwByName(JNIEnv *env, const char *name, const char *msg) { jclass cls = env->FindClass(name); if (cls != NULL) { env->ThrowNew(cls, msg); } env->DeleteLocalRef(cls);}// 調用拋出異常extern "C"JNIEXPORT void JNICALLJava_com_glumes_cppso_jnioperations_ExceptionOps_nativeThrowException(JNIEnv *env, jobject instance) { throwByName(env, "java/lang/IllegalArgumentException", "native throw exception");}

jni錯誤日誌分析

ndk發生崩潰的時候會在/data/tombstones文件夾下生成一個墓碑文件,形如tombstone_xx。谷歌也提供了addr2line/ objdump/ndk-stack工具

一個典型的墓碑文件內容如下

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***Build fingerprint: 'Android-x86/android_x86/x86:5.1.1/LMY48W/woshijpf04211939:eng/test-keys'Revision: '0'ABI: 'x86'pid: 1019, tid: 1019, name: surfaceflinger >>> /system/bin/surfaceflinger <<<signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4 eax a6265c06 ebx b7467d88 ecx b7631a22 edx a6265c06 esi 00000000 edi b6867140 xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007b eip b745a639 ebp bfcfc1e8 esp bfcfc150 flags 00010282backtrace: #00 pc 00006639 /system/lib/libui.so (android::Fence::waitForever(char const*) 41) #01 pc 00034b86 /system/lib/libsurfaceflinger.so #02 pc 0003229e /system/lib/libsurfaceflinger.so #03 pc 0002cb9c /system/lib/libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&) 652) #04 pc 000342f4 /system/lib/libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*) 2580) #05 pc 0004eafb /system/lib/libgui.so (android::Surface::queueBuffer(ANativeWindowBuffer*, int) 411) #06 pc 0004ce06 /system/lib/libgui.so (android::Surface::hook_queueBuffer(ANativeWindow*, ANativeWindowBuffer*, int) 38) #07 pc 00014bc6 /system/lib/egl/libGLES_android.so #08 pc 00017f73 /system/lib/egl/libGLES_android.so (eglSwapBuffers 163) #09 pc 00015fdb /system/lib/libEGL.so (eglSwapBuffers 203) #10 pc 000013ea /system/lib/hw/hwcomposer.x86.so #11 pc 00034730 /system/lib/libsurfaceflinger.so #12 pc 000256d4 /system/lib/libsurfaceflinger.so #13 pc 00024bf4 /system/lib/libsurfaceflinger.so #14 pc 000236fb /system/lib/libsurfaceflinger.so #15 pc 0002338a /system/lib/libsurfaceflinger.so #16 pc 0001e0ff /system/lib/libsurfaceflinger.so #17 pc 0001d9ce /system/lib/libutils.so (android::Looper::pollInner(int) 926) #18 pc 0001db73 /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**) 67) #19 pc 0001e561 /system/lib/libsurfaceflinger.so #20 pc 00022ce7 /system/lib/libsurfaceflinger.so (android::SurfaceFlinger::run 39) #21 pc 00000ca3 /system/bin/surfaceflinger #22 pc 0001365a /system/lib/libc.so (__libc_init 106) #23 pc 00000da8 /system/bin/surfaceflingerstack: bfcfc110 00000000 bfcfc114 b6839270 bfcfc118 00000000 bfcfc11c 00000000 bfcfc120 b68394e0 bfcfc124 00000002 bfcfc128 00000002 bfcfc12c b75d8185 /system/lib/libutils.so (android::RefBase::incStrong(void const*) const 53) bfcfc130 b6839270 bfcfc134 bfcfc1e8 [stack] bfcfc138 00000002 bfcfc13c a6265c06 bfcfc140 b7467d88 /system/lib/libui.so bfcfc144 00000000 bfcfc148 b6867140 bfcfc14c b745a639 /system/lib/libui.so (android::Fence::waitForever(char const*) 41) #00 bfcfc150 b683af18 bfcfc154 bfcfc1e8 [stack] bfcfc158 00000000 bfcfc15c 00000000 bfcfc160 00000000 bfcfc164 b683af18 bfcfc168 b75ec9c4 /system/lib/libutils.so bfcfc16c b75d8285 /system/lib/libutils.so (android::RefBase::weakref_type::decWeak(void const*) 37) bfcfc170 00000000 bfcfc174 00000000 bfcfc178 00000000 bfcfc17c 00000000 bfcfc180 b7642968 /system/lib/libsurfaceflinger.so bfcfc184 bfcfc1e8 [stack] bfcfc188 b6867140 bfcfc18c b7622b87 /system/lib/libsurfaceflinger.so

文件結構

定位工具addr2line

qinqundeMacBook-Pro% ./aarch64-linux-android-addr2line -C -f -e ~/Documents/DProtect2/mylibrary/build/intermediates/cmake/debug/obj/arm64-v8a/libnative-lib.so 0000000000157efcJava_com_dofun_dprotect_lib_LocalWork_start/Users/qinqun/Documents/DProtect2/mylibrary/src/main/cpp/native-lib.cpp:147

ndk-stack

有需要完整代碼的同學 可以 私信 發送 「底層源碼」 即可 免費獲取

現在發送還可以獲得更多《Android 學習筆記+源碼解析+面試視頻》

JNI 特點:

二進位兼容

● 本地方法庫與同一平臺上所有Java 虛擬機之間實現二進位兼容,即對於給定平臺開發人員只需要維護一種版本的本地方法庫

效率高

為了實現實時系統,JNI 在效率與虛擬機無關性之間進行了優化,以保障高效運行

功能強

● JNI 提供了大量的函數及接口讓本地方法與Java 虛擬機內核相互操作,增強兩者的功能

本地代碼與 Java 虛擬機之間是通過 JNI 函數實現相互操作的;JNI 函數通過接口指針來獲得,本地方法將 JNI 接口指針當作參數來接受;虛擬機保證在從相同的 Java 線程中對本地方法進行多次調用時,傳遞給本地方法的接口指針是相同的,本地方法被不同的 Java 線程調用時,它接受不同的 JNI接口指針

尾述

有需要完整代碼的同學 可以 私信 發送 「底層源碼」 即可 免費獲取

現在發送還可以獲得更多《Android 學習筆記+源碼解析+面試視頻》

技術是無止境的,你需要對自己提交的每一行代碼、使用的每一個工具負責,不斷挖掘其底層原理,才能使自己的技術升華到更高的層面

Android 架構師之路還很漫長,與君共勉

PS:有問題歡迎指正,可以在評論區留下你的建議和感受;

歡迎大家點讚評論,覺得內容可以的話,可以轉發分享一下

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了