自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

研究了一下Android JNI,有幾個(gè)知識(shí)點(diǎn)不太懂

移動(dòng)開(kāi)發(fā) Android
以前版本的Java線程不是OS線程,是JVM構(gòu)造的用戶(hù)態(tài)線程(Green Thread),不能充分利用CPU,后期已經(jīng)更改為使用OS線程實(shí)現(xiàn)。

[[437215]]

本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人 。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。

Java線程與Native(OS)線程的區(qū)別

聯(lián)系:Java線程其實(shí)是一層OS線程的封裝,本質(zhì)上就是OS線程?!疽郧鞍姹镜腏ava線程不是OS線程,是JVM構(gòu)造的用戶(hù)態(tài)線程(Green Thread),不能充分利用CPU,后期已經(jīng)更改為使用OS線程實(shí)現(xiàn)?!俊緟⒖糷ttps://mp.weixin.qq.com/s/Gxqnf5vjyaI8eSYejm7zeQ】

區(qū)別:

Java線程可以直接拿到JNIEnv,OS線程需要先attach到JVM,才可以拿到JNIEnv?!緜€(gè)人理解區(qū)別在于是否attach了JVM】

  1. jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args); 

Java線程可以FindClass成功,OS線程則FindClass失敗,原因是兩者的ClassLoader不同,OS線程AttachCurrentThread后持有的ClassLoader是系統(tǒng)的ClassLoader,如果想要FindClass成功,需要在JNI_Onload時(shí)獲取一份當(dāng)前庫(kù)的ClassLoader保存起來(lái),下次FindClass時(shí)使用此ClassLoader去操作。

  1. static jobject g_class_loader = NULL
  2. static jmethodID g_find_class_method = NULL
  3. void on_load() { 
  4.     JNIEnv *env = get_jni_env(); 
  5.     if (!env) { 
  6.         return
  7.     } 
  8.     jclass capture_class = (*env)->FindClass(env, "com/captureandroid/BMMCaptureEngine"); 
  9.     jclass class_class = (*env)->GetObjectClass(env, capture_class); 
  10.     jclass class_loader_class = (*env)->FindClass(env, "java/lang/ClassLoader"); 
  11.     jmethodID class_loader_mid = (*env)->GetMethodID(env, class_class, "getClassLoader""()Ljava/lang/ClassLoader;"); 
  12.     jobject local_class_loader = (*env)->CallObjectMethod(env, capture_class, class_loader_mid); 
  13.     g_class_loader = (*env)->NewGlobalRef(env, local_class_loader); 
  14.     g_find_class_method = 
  15.         (*env)->GetMethodID(env, class_loader_class, "findClass""(Ljava/lang/String;)Ljava/lang/Class;"); 
  16.  
  17. jclass find_class(const char *name) { 
  18.     JNIEnv *env = bmm_util_get_jni_env(); 
  19.     if (!env) { 
  20.         return NULL
  21.     } 
  22.     jclass ret = (*env)->FindClass(env, name); 
  23.     jthrowable exception = (*env)->ExceptionOccurred(env); 
  24.     if (exception) { 
  25.         (*env)->ExceptionClear(env); 
  26.         jstring name_str = (*env)->NewStringUTF(env, name); 
  27.         ret = (jclass)(*env)->CallObjectMethod(env, g_class_loader, g_find_class_method, name_str); 
  28.         (*env)->DeleteLocalRef(env, name_str); 
  29.     } 
  30.     return ret; 

JNI的作用

貼出別人翻譯的【官方文檔https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html#wp16696】的一段話:

JNI最重要的設(shè)計(jì)目標(biāo)就是在不同操作系統(tǒng)上的JVM之間提供二進(jìn)制兼容,做到一個(gè)本地庫(kù)不需要重新編譯就可以運(yùn)行不同的系統(tǒng)的JVM上面。為了達(dá)到這一點(diǎn)兒,JNI設(shè)計(jì)時(shí)不能關(guān)心JVM的內(nèi)部實(shí)現(xiàn),因?yàn)镴VM的內(nèi)部實(shí)現(xiàn)機(jī)制在不斷地變,而我們必須保持JNI接口的穩(wěn)定。JNI的第二個(gè)設(shè)計(jì)目標(biāo)就是高效。我們可能會(huì)看到,有時(shí)為了滿(mǎn)足第一個(gè)目標(biāo),可能需要犧牲一點(diǎn)兒效率,因此,我們需要在平臺(tái)無(wú)關(guān)和效率之間做一些選擇。最后,JNI必須是一個(gè)完整的體系。它必須提供足夠多的JVM功能讓本地程序完成一些有用的任務(wù)。JNI不能只針對(duì)一款特定的JVM,而是要提供一系列標(biāo)準(zhǔn)的接口讓程序員可以把他們的本地代碼庫(kù)加載到不同的JVM中去。有時(shí),調(diào)用特定JVM下實(shí)現(xiàn)的接口可以提供效率,但更多的情況下,我們需要用更通用的接口來(lái)解決問(wèn)題。

JNIEnv和JavaVM

就是個(gè)函數(shù)指針。

下圖是JNIEnv的指針結(jié)構(gòu):

JNIEnv其實(shí)是一個(gè)指向本地線程數(shù)據(jù)的接口指針,指針里面包含指向函數(shù)接口的指針,每一個(gè)接口函數(shù)在這表中都有一個(gè)預(yù)定義的偏移位置,類(lèi)似C++虛函數(shù)表。

代碼如下:

  1. typedef const struct JNINativeInterface *JNIEnv;  
  2.  
  3. struct JNINativeInterface { 
  4.     void*       reserved0; 
  5.     void*       reserved1; 
  6.     void*       reserved2; 
  7.     void*       reserved3; 
  8.  
  9.     jint        (*GetVersion)(JNIEnv *); 
  10.  
  11.     jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, 
  12.                         jsize); 
  13.     jclass      (*FindClass)(JNIEnv*, const char*); 
  14.     jobject     (*AllocObject)(JNIEnv*, jclass); 
  15.     jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...); 
  16.     jobject     (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list); 
  17.     jobject     (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*); 
  18.     ... 
  19. }; 
  20. JavaVM類(lèi)似 
  21. struct JNIInvokeInterface { 
  22.     void*       reserved0; 
  23.     void*       reserved1; 
  24.     void*       reserved2; 
  25.  
  26.     jint        (*DestroyJavaVM)(JavaVM*); 
  27.     jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*); 
  28.     jint        (*DetachCurrentThread)(JavaVM*); 
  29.     jint        (*GetEnv)(JavaVM*, void**, jint); 
  30.     jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*); 
  31. }; 
  32.  
  33. typedef const struct JNIInvokeInterface* JavaVM; 

知識(shí)點(diǎn)1:為什么使用函數(shù)表而不是寫(xiě)死某些函數(shù)項(xiàng)?

可將JNI命名空間與本地代碼分離,一個(gè)虛擬機(jī)可以提供多個(gè)版本的JNI函數(shù)表,用于不同場(chǎng)景。例如,虛擬機(jī)可支持兩種JNI函數(shù)表:

一個(gè)用于調(diào)試,做較多的錯(cuò)誤檢查。

一個(gè)用于發(fā)布,做較少的錯(cuò)誤檢查,更高效。

知識(shí)點(diǎn)2:JNIEnv是thread-local,只在當(dāng)前線程有效,Native方法不能將JNIenv從當(dāng)前線程傳遞到另一個(gè)線程。不能跨線程使用JNIEnv【至于JNIEnv為什么設(shè)計(jì)成thread-local,沒(méi)搞明白】。

知識(shí)點(diǎn)3:線程間雖然不共享JNIEnv,但是共享JavaVM,然后可以通過(guò)GetEnv獲取到當(dāng)前線程的JNIEnv。

jint GetEnv(JavaVM *vm, void **env, jint version);

知識(shí)點(diǎn)4:Native方法接收J(rèn)NI接口指針作為參數(shù)。虛擬機(jī)保證在同一個(gè)線程傳入Native方法的是相同的JNIEnv。如果不同線程調(diào)用Native方法,傳入他們的JNIEnv不同。但JNIEnv間接指向的函數(shù)表在多個(gè)線程間是共享的。

知識(shí)點(diǎn)5:為什么在C語(yǔ)言中調(diào)用Native方法需要將JNIEnv當(dāng)作參數(shù)傳遞,而C++中卻不需要?

  1. // C語(yǔ)言 
  2. jstring model_path = (*env)->NewStringUTF(env, path); 
  3. // C++ 
  4. jstring model_path = env->NewStringUTF(path); 

前面列出的JNIEnv是C語(yǔ)言形式,Java還單獨(dú)為C++封裝了一層JNIEnv,簡(jiǎn)化版代碼:

  1. struct _JNIEnv { 
  2.     /* do not rename this; it does not seem to be entirely opaque */ 
  3.     const struct JNINativeInterface* functions; 
  4.  
  5. #if defined(__cplusplus) 
  6.  
  7.     jint GetVersion() 
  8. return functions->GetVersion(this); } 
  9.  
  10.     jclass FindClass(const charname
  11. return functions->FindClass(this, name); } 
  12. #endif 

其實(shí)本質(zhì)上還是調(diào)用的C語(yǔ)言那種形式的接口。

JNI中數(shù)據(jù)如何傳遞

這里不詳細(xì)介紹了,大體就是int,float這種基本類(lèi)型采用拷貝,對(duì)象和byte數(shù)組等使用引用形式,所以其實(shí)Java層的byte字節(jié)流數(shù)據(jù)傳到Native層基本不耗時(shí),不會(huì)發(fā)生拷貝【但是Native層如果想使用持有這塊數(shù)據(jù),那就得自己拷貝一份了】。

還有些GlobalReference、LocalReference以及為什么要Delete LocalReference的這類(lèi)知識(shí)點(diǎn),這些比較基礎(chǔ),就不介紹了,估計(jì)大家也都懂。

推薦閱讀

https://www.cnblogs.com/kexinxin/p/11689641.html

ndk官方文檔

https://developer.android.com/ndk/guides

參考資料

http://luori366.github.io/JNI_doc/jni_design_theory.html

https://www.cnblogs.com/kexinxin/p/11689641.html

 

https://developer.android.com/ndk/guides

 

責(zé)任編輯:武曉燕 來(lái)源: 程序喵大人
相關(guān)推薦

2020-03-27 08:43:20

Vueprops開(kāi)發(fā)

2018-01-29 15:23:14

網(wǎng)絡(luò)知識(shí)點(diǎn)軟件測(cè)試

2012-04-23 15:49:04

2021-02-26 22:34:28

Webpack 前端項(xiàng)目

2024-03-05 08:33:52

OptionsAPIcomuted

2021-08-11 08:16:02

springboot 動(dòng)態(tài)注冊(cè)項(xiàng)目

2010-08-30 19:42:45

DHCP服務(wù)器

2021-06-11 11:42:57

Swift 函數(shù)生成器結(jié)果生成器

2009-04-01 11:39:39

視圖DB2

2016-05-30 17:31:34

Spring框架

2010-08-17 14:56:00

HCNE認(rèn)證

2011-04-15 12:25:21

BGP路由

2021-06-29 15:56:39

MYSQL開(kāi)發(fā)數(shù)據(jù)庫(kù)

2022-08-03 08:03:03

前端APIjavascript

2019-12-03 08:13:06

BDRDR路由器

2017-12-08 14:26:19

Android面試知識(shí)點(diǎn)總結(jié)

2017-07-04 13:59:28

Android模塊化

2021-06-17 06:51:32

Java泛型Java編程

2020-04-15 11:21:49

QQ騰訊

2022-02-07 00:10:28

Docker容器開(kāi)發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)