日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

【Android NDK 开发】JNI 线程 ( JNI 线程创建 | 线程执行函数 | 非 JNI 方法获取 JNIEnv 与 Java 对象 | 线程获取 JNIEnv | 全局变量设置 )

發(fā)布時(shí)間:2025/6/17 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Android NDK 开发】JNI 线程 ( JNI 线程创建 | 线程执行函数 | 非 JNI 方法获取 JNIEnv 与 Java 对象 | 线程获取 JNIEnv | 全局变量设置 ) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

        • I . JNI 線程創(chuàng)建
        • II . 線程執(zhí)行函數(shù)
        • III . 線程方法獲取 Java 對(duì)象
        • IV . 線程方法獲取 JNIEnv
        • V . JNI 線程 完整代碼示例



I . JNI 線程創(chuàng)建



1. 線程創(chuàng)建方法函數(shù)原型 :

int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void*)(*start_rtn)(void*), void *arg)`;

2. pthread_create 方法的 4 個(gè)參數(shù) ;

  • 參數(shù) 1 ( pthread_t *tidp ) : 線程標(biāo)識(shí)符指針 , 該指針指向線程標(biāo)識(shí)符 ;
  • 參數(shù) 2 ( const pthread_attr_t *attr ) : 線程屬性指針 ;
  • 參數(shù) 3 ( (void*)(*start_rtn)(void*) ) : 線程運(yùn)行函數(shù)指針 , start_rtn 是一個(gè)函數(shù)指針 , 其參數(shù)和返回值類型是 void* 類型 ;
  • 參數(shù) 4 ( void *arg ) : 參數(shù) 3 中的線程運(yùn)行函數(shù)的參數(shù) ;

3. 返回值說明 :

  • 線程創(chuàng)建成功 , 返回 0 ;
  • 線程創(chuàng)建失敗 , 返回 錯(cuò)誤代碼 ;

4. 關(guān)于函數(shù)指針參數(shù)的說明 : C++ 中函數(shù)指針類型是 void *(PTW32_CDECL *start) (void *)

  • 函數(shù)的參數(shù)類型是 void* 指針 ;
  • 函數(shù)的返回值類型 void* 指針 ;

5. 函數(shù)多參數(shù)方案 : 如果線程執(zhí)行的函數(shù)有多個(gè)參數(shù) , 可以使用結(jié)構(gòu)體 , 類進(jìn)行封裝 ;


6. 線程屬性 : 創(chuàng)建線程時(shí) , 給線程指定屬性 pthread_attr_t 是結(jié)構(gòu)體類型 ;


7. 代碼示例 :

/*線程創(chuàng)建方法函數(shù)原型 : int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, (void*)(*start_rtn)(void*), void *arg);該方法需要提供四個(gè)參數(shù) ;參數(shù) 1 ( pthread_t *tidp ) :線程標(biāo)識(shí)符指針 , 該指針指向線程標(biāo)識(shí)符 ;參數(shù) 2 ( const pthread_attr_t *attr ) : 線程屬性指針 ;參數(shù) 3 ( (void*)(*start_rtn)(void*) ) : 線程運(yùn)行函數(shù)指針 , start_rtn 是一個(gè)函數(shù)指針 , 其參數(shù)和返回值類型是 void* 類型參數(shù) 4 ( void *arg ) : 參數(shù) 3 中的線程運(yùn)行函數(shù)的參數(shù) ;返回值 :線程創(chuàng)建成功 , 返回 0 ;線程創(chuàng)建失敗 , 返回 錯(cuò)誤代碼 ;關(guān)于函數(shù)指針參數(shù) : C++ 中函數(shù)指針類型是 void *(PTW32_CDECL *start) (void *) ,函數(shù)的參數(shù)類型是 void* 指針函數(shù)的返回值類型 void* 指針函數(shù)多參數(shù)方案 : 如果線程執(zhí)行的函數(shù)有多個(gè)參數(shù) , 可以使用結(jié)構(gòu)體 , 類進(jìn)行封裝線程屬性 : 創(chuàng)建線程時(shí) , 給線程指定屬性 pthread_attr_t 是結(jié)構(gòu)體類型*///函數(shù)指針 函數(shù)名 和 &函數(shù)名 都可以作為函數(shù)指針pthread_create( &pid , 0 , threadRun, 0 );

II . 線程執(zhí)行函數(shù)



1. 線程執(zhí)行函數(shù)的要求 : C++ 中規(guī)定線程執(zhí)行函數(shù)的函數(shù)指針類型是 void *(PTW32_CDECL *start) (void *) ;


2. 函數(shù)作用 : 將該函數(shù)的指針作為線程創(chuàng)建方法 pthread_create 的第三個(gè)參數(shù) ;


3. 參數(shù)處理 : 在線程創(chuàng)建時(shí) , 傳入?yún)?shù) , 將該參數(shù)轉(zhuǎn)為 char* 字符串指針類型 , 將其打印出來 ;


4. 代碼示例 :

/*定義線程中要執(zhí)行的方法將該函數(shù)的指針作為線程創(chuàng)建方法 pthread_create 的第三個(gè)參數(shù)C++ 中規(guī)定線程執(zhí)行函數(shù)的函數(shù)指針類型是 void *(PTW32_CDECL *start) (void *) */ void* pthread_function(void* args) {//延遲 100 ms 執(zhí)行//_sleep(100);//指針類型轉(zhuǎn)換 : 將 void* 轉(zhuǎn)為 char*// 使用 static_cast 類型轉(zhuǎn)換標(biāo)識(shí)符char* hello = static_cast<char*>(args);//打印參數(shù)cout << "pthread_function 線程方法 執(zhí)行 參數(shù) : " << hello << endl;return 0; }

III . 線程方法獲取 Java 對(duì)象



線程方法獲取 Java 對(duì)象步驟 :


① 定義全局變量 jobject obj : 使用該全局變量存儲(chǔ) Java 對(duì)象 ;

//JNI 方法參數(shù)中的第二個(gè)參數(shù) , 需要先將局部變量轉(zhuǎn)為全局變量 , 然后再其它方法中調(diào)用 jobject obj;

② JNI 方法處理 : 將 jobject instance 參數(shù) ( 此時(shí)是局部變量 ) 轉(zhuǎn)為 全局變量 , 調(diào)用 NewGlobalRef 方法實(shí)現(xiàn) ;

void threadDemoC(JNIEnv *env, jobject instance){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadDemoC");//保存全局變量 , 先將局部變量轉(zhuǎn)為全局變量 , 然后再賦值給全局的 obj 變量// 使用域作用符訪問全局的 ::obj 變量::obj = env->NewGlobalRef(instance);... }

這樣就可以在其它方法或其它線程中使用該 Java 對(duì)象了 ;



IV . 線程方法獲取 JNIEnv



線程中獲取 JNIEnv * env 步驟 :


① JNIEnv 無法跨線程 : JNI 方法參數(shù)中的 JNIEnv 指針是不能跨線程使用的 , 在 主線程中調(diào)用 JNI 方法 , 其 JNIEnv 指針不能在子線程中使用 ;


② 獲取途徑 : 如果在子線程中使用 JNIEnv 指針 , 需要使用 JavaVM 獲取 指定線程的 JNIEnv 指針 ;


③ 綁定線程 : 調(diào)用 JavaVM 的 AttachCurrentThread 方法 , 可以綁定線程 , 其傳入一個(gè) JNIEnv ** 二維指針 , 會(huì)返回該線程對(duì)應(yīng)的 JNIEnv 指針 ;


④ 剝離線程 : 注意使用完 JNIEnv 后 , 解綁線程 , 調(diào)用 JavaVM 的 DetachCurrentThread 方法 解綁線程 ;


2 . 代碼示例 :

/*線程執(zhí)行的方法如果在 Native 層執(zhí)行耗時(shí)操作 , 如下載文件 , 需要在線程中處理JNI 方法參數(shù)中的 JNIEnv 指針是不能跨線程使用的 , 在 主線程中調(diào)用 JNI 方法 , 其 JNIEnv 指針不能在子線程中使用如果在子線程中使用 JNIEnv 指針 , 需要使用 JavaVM 獲取 指定線程的 JNIEnv 指針調(diào)用 JavaVM 的 AttachCurrentThread 可以獲取本線程的 JNIEnv 指針注意最后還要將線程從 Java 虛擬機(jī)中剝離關(guān)于參數(shù)傳遞 :傳遞 int 類型 和 int * 類型 , 傳遞指針可以在 方法中修改 int 變量值 ;傳遞 int * 類型 和 int ** 類型 , 傳遞二維指針 可以在方法中修改 int * 一維指針值因此有些參數(shù)需要在方法中修改, 并且需要保存該修改狀態(tài) , 就需要將該變量的地址當(dāng)做參數(shù)傳入原來的普通變量 變成 指針變量 , 一維指針 變 二維指針*/ void* threadRun(void *args){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadRun");//JNIEnv 不能跨線程使用 , 這里需要先獲取本線程的 JNIEnvJNIEnv *env;//將線程附加到 Java 虛擬機(jī)中 ( 注意后面對(duì)應(yīng)剝離線程操作 )// 如果成功返回 0 , 如果失敗 , 直接退出int attachResult = _vm->AttachCurrentThread(&env, 0);...//將線程從 Java 虛擬機(jī)中剝離_vm->DetachCurrentThread();//注意這里一定要返回 0 , 否則執(zhí)行到結(jié)尾會(huì)崩潰return 0;}

V . JNI 線程 完整代碼示例



1 . Java 層代碼 :

package kim.hsl.thread;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.util.Log; import android.widget.TextView; import android.widget.Toast;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);threadDemoJava();}/*** JNI 線程 Demo*/public native void threadDemoJava();/*** 打印當(dāng)前線程信息*/public void logThread(){Log.i("JNI_TAG", Thread.currentThread().toString());}}

2 . Native 層代碼 :

#include <jni.h> #include <string> #include <android/log.h>//導(dǎo)入線程頭文件 #include <pthread.h>//Java 虛擬機(jī)指針 , 在 JNI_OnLoad 方法中設(shè)置該值 JavaVM *_vm;//JNI 方法參數(shù)中的第二個(gè)參數(shù) , 需要先將局部變量轉(zhuǎn)為全局變量 , 然后再其它方法中調(diào)用 jobject obj;/*線程執(zhí)行的方法如果在 Native 層執(zhí)行耗時(shí)操作 , 如下載文件 , 需要在線程中處理JNI 方法參數(shù)中的 JNIEnv 指針是不能跨線程使用的 , 在 主線程中調(diào)用 JNI 方法 , 其 JNIEnv 指針不能在子線程中使用如果在子線程中使用 JNIEnv 指針 , 需要使用 JavaVM 獲取 指定線程的 JNIEnv 指針調(diào)用 JavaVM 的 AttachCurrentThread 可以獲取本線程的 JNIEnv 指針注意最后還要將線程從 Java 虛擬機(jī)中剝離關(guān)于參數(shù)傳遞 :傳遞 int 類型 和 int * 類型 , 傳遞指針可以在 方法中修改 int 變量值 ;傳遞 int * 類型 和 int ** 類型 , 傳遞二維指針 可以在方法中修改 int * 一維指針值因此有些參數(shù)需要在方法中修改, 并且需要保存該修改狀態(tài) , 就需要將該變量的地址當(dāng)做參數(shù)傳入原來的普通變量 變成 指針變量 , 一維指針 變 二維指針 */ void* threadRun(void *args){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadRun");//JNIEnv 不能跨線程使用 , 這里需要先獲取本線程的 JNIEnvJNIEnv *env;//將線程附加到 Java 虛擬機(jī)中 ( 注意后面對(duì)應(yīng)剝離線程操作 )// 如果成功返回 0 , 如果失敗 , 直接退出int attachResult = _vm->AttachCurrentThread(&env, 0);//獲取 MainActivity 對(duì)應(yīng)的 jclass 對(duì)象jclass clazz = env->GetObjectClass( obj );//反射獲取 logThread 方法jmethodID logThreadID = env->GetMethodID(clazz, "logThread", "()V");//調(diào)用 logThread 方法env->CallVoidMethod(obj, logThreadID);//釋放相關(guān)的局部變量env->DeleteLocalRef(clazz);//將線程從 Java 虛擬機(jī)中剝離_vm->DetachCurrentThread();//注意這里一定要返回 0 , 否則執(zhí)行到結(jié)尾會(huì)崩潰return 0;}void threadDemoC(JNIEnv *env, jobject instance){__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "threadDemoC");//保存全局變量 , 先將局部變量轉(zhuǎn)為全局變量 , 然后再賦值給全局的 obj 變量// 使用域作用符訪問全局的 ::obj 變量::obj = env->NewGlobalRef(instance);//代表一個(gè)線程的句柄pthread_t pid;//創(chuàng)建線程并執(zhí)行pthread_create( &pid , 0 , threadRun, 0 ); }//下面的代碼是動(dòng)態(tài)注冊(cè)內(nèi)容static const JNINativeMethod methods[] = {{"threadDemoJava", "()V", (void *)threadDemoC} };static const char* className = "kim/hsl/thread/MainActivity";int JNI_OnLoad(JavaVM *vm , void* reserved){// 1 . 記錄 Java 虛擬機(jī)指針_vm = vm;// 2 . 動(dòng)態(tài)注冊(cè)方法//獲取 JNIEnv 指針JNIEnv *env = nullptr;int registerResult = vm->GetEnv( (void **) &env, JNI_VERSION_1_6 );if(registerResult != JNI_OK){return -1;}//進(jìn)行動(dòng)態(tài)注冊(cè)jclass jclazz = env->FindClass(className);env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod));return JNI_VERSION_1_6; }

3 . 執(zhí)行結(jié)果 :

2020-02-08 23:47:58.253 25293-25293/? I/JNI_TAG: threadDemoC 2020-02-08 23:47:58.253 25293-25316/? I/JNI_TAG: threadRun 2020-02-08 23:47:58.253 25293-25316/? I/JNI_TAG: Thread[Thread-2,10,main]

總結(jié)

以上是生活随笔為你收集整理的【Android NDK 开发】JNI 线程 ( JNI 线程创建 | 线程执行函数 | 非 JNI 方法获取 JNIEnv 与 Java 对象 | 线程获取 JNIEnv | 全局变量设置 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。