Android JNI學習-線程操做

Android Native中支持的線程標準是 POSIX 線程。POSIX 線程也被簡稱爲Pthreads,是一個線程的POSIX 標準,它爲建立和處理線程定義了一個通用的API。java

POSIX Thread 的Android實現是Bionic標準庫的一部分,在編譯的時候不須要連接任何其餘的庫,只須要包含一個頭文件。c++

#include <pthread.h>
複製代碼

建立線程

線程建立函數:多線程

int pthread_create(
	pthread_t* thread, 
	pthread_attr_t const* attr, 
	void* (*start_routine)(void*), 
	void* arg);

複製代碼
  • thread:指向 pthread_t 類型變量的指針,用它表明返回線程的句柄ionic

  • attr:指向 pthread_attr_t 結構的指針形式存在的新線程屬性,能夠經過該結構來指定新線程的一些屬性,好比棧大小、調度優先級等,具體看 pthread_attr_t 結構的內容。若是沒有特殊要求,可以使用默認值,把該變量取值爲 NULL 。函數

  • 第三個參數是指向啓動函數的函數指針,它的函數簽名格式以下:spa

    void* start_routine(void* args) 複製代碼

    啓動程序將線程參數當作 void 指針,返回 void 指針類型結果。線程

  • 線程啓動程序的參數,也就是函數的參數,若是不須要傳遞參數,它能夠爲 NULL 。指針

pthread_create 函數若是執行成功了則返回 0 ,若是返回其餘錯誤代碼。code

void sayHello(void *){
    LOGE("say %s","hello");
}

JNIEXPORT jint JNICALL Java_com_david_JNIController_sayhello (JNIEnv *jniEnv, jobject instance) {
    pthread_t handles; // 線程句柄
    int ret = pthread_create(&handles, NULL, sayHello, NULL);
    if (ret != 0) {
        LOGE("create thread failed");
    } else {
        LOGD("create thread success");
    }
}
複製代碼

調用函數就能夠在線程執行打印say hello了。同步

附着在Java虛擬機上

建立了線程後,只能作一些簡單的Native操做,若是想要對Java層作一些操做就不行了,由於它沒有Java虛擬機環境,這個時候爲了和Java空間進行交互,就要把POSIX 線程附着在Java虛擬機上,而後就能夠得到當前線程的 JNIEnv 指針了。

經過 AttachCurrentThread 方法能夠將當前線程附着到 Java 虛擬機上,而且能夠得到 JNIEnv 指針。而AttachCurrentThread 方法是由 JavaVM 指針調用的,能夠在JNI_OnLoad函數中將JavaVM 保存爲全局變量。

static JavaVM *jVm = NULL;
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    jVm = vm;
    return JNI_VERSION_1_6;
}

複製代碼

如上一個例子,咱們想要在sayHello函數中調用一個Java層的函數javaSayHello()

private void javaSayHello() {
    Log.e(TAG,"java say hello");
}
複製代碼
void sayHello(void *){
    LOGE("say %s","hello");
     JNIEnv *env = NULL;
    // 將當前線程添加到 Java 虛擬機上
    if (jVm->AttachCurrentThread(&env, NULL) == 0) {
        ......
        env->CallVoidMethod(Obj, javaSayHello);
        // 從 Java 虛擬機上分離當前線程
        jVm->DetachCurrentThread();  
    }
    return NULL;
}
複製代碼

這樣就在 Native 線程中調用 Java 相關的函數了。

等待線程返回結果

前面提到的方法是新線程運行後,該方法也就當即返回退出,執行完了。咱們也能夠經過另外一個函數能夠在等待線程執行完畢後,拿到線程執行完的結果以後再退出。

int pthread_join(pthread_t pthread, void** ret_value);
複製代碼
  • pthread 表明建立線程的句柄
  • ret_value表明線程運行函數返回的結果
pthread_t* handles = new pthread_t[10];
	
	for (int i = 0; i < 10; ++i) {
        pthread_t pthread;
        // 建立線程,
        int result = pthread_create(&handles[i], NULL, run, NULL;
        }
    }
    for (int i = 0; i < 10; ++i) {
        void *result = NULL; // 線程執行返回結果
        // 等待線程執行結束
        if (pthread_join(handles[i], &result) != 0) {
            env->ThrowNew(env, runtimeException, "Unable to join thread");
        } else {
	        LOGD("return value is %d",result);
        }
    }

複製代碼

pthread_join 返回爲 0 表明執行成功,非 0 則執行失敗。

同步代碼塊

在Java中,JDK爲咱們提供了synchronized來處理多線程同步代碼塊。

synchronized (object.class) {
        // 業務處理
    }
複製代碼

本地代碼中,JNI提供了兩個函數來完成上面的同步:

(1)MonitorEnter:進入同步代碼塊

(2)MonitorExit:退出同步代碼塊

if(env->MonitorEnter(obj)!= JNI_OK){
    // 錯誤處理
}

// 同步代碼塊

// 出現錯誤釋放代碼塊
if(env->ExceptionCheck()){
    if(env->MonitorExit(obj)!= JNI_OK);
       return;
}

if(env->MonitorExit(obj)!= JNI_OK){
    // 錯誤處理
}
複製代碼

能夠發如今本地代碼中處理同步代碼塊要比Java中複雜的多,因此,儘可能用Java來作同步吧,把與同步相關的代碼都移到Java中去。

相關文章
相關標籤/搜索