JNI 中的線程

相關結論

  • JNIEnv 無法跨線程, 可以跨函數.
  • jobject 無法跨線程, 無法跨函數.
  • JavaVM 可以跨線程,跨行數.

在實際開發中, 很有可能在 JNI 中做一些耗時操作, 就需要在 JNI 中用到子線程. 下面用一個栗子🌰來學習.

Java 代碼

public class MainActivity extends AppCompatActivity {

    // Used to load the 'myapplication' library on application startup.
    static {
        System.loadLibrary("myapplication");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        //JNI 調用
        nativeThreadInvokeJava();
    }

    //在 JNI 中耗時操作完畢後,調用 java 層的此方法
    public void updateUI(){
        if(Looper.myLooper() == Looper.getMainLooper()) {
            Toast.makeText(this, "主線程更新UI", Toast.LENGTH_SHORT).show();
        }else{
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "已切換到主線程更新UI", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    //定義 JNI 方法.
    native void nativeThreadInvokeJava();
}

JNI 代碼

#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h> //導入線程需要的包

JavaVM *_vm;

#define _MY_LOGE(...) __android_log_print(ANDROID_LOG_DEBUG, "JNI", __VA_ARGS__);

struct myContext{
    jobject instance;
   //JNIEnv *env; 無法在子線程使用 JNIEnv
};

int JNI_OnLoad(JavaVM *vm, void *re){
    _vm = vm;
    return  JNI_VERSION_1_6;
}

//在子線程要執行的方法
void * myThreadTask(void *arges){
    //將 native 線程附加到 java 虛擬機, 獲得一個當前子線程專用的 jniEnv
    JNIEnv * env = nullptr;
    jint i = _vm->AttachCurrentThread(&env, nullptr);
    if(i != JNI_OK) {
        return 0;
    }
    //執行耗時操作.需要通知 java 狀態更新
    myContext * context = static_cast<myContext *>(arges);
    jclass mainActivityObj = env->GetObjectClass(context->instance);
    jmethodID methodId = env->GetMethodID(mainActivityObj, "updateUI", "()V"); //獲得方法ID
    env->CallVoidMethod(context->instance, methodId);
    delete(context);
    context = 0;
    env->DeleteGlobalRef(context->instance);//刪除全局引用
    _vm->DetachCurrentThread(); //附加完, 之後完成後,需要分離
    return 0;
}

extern "C"
JNIEXPORT void JNICALL Java_com_aaatest_myapplication_MainActivity_nativeThreadInvokeJava(JNIEnv *env, jobject instance) {
    myContext *context = new myContext;
    //context->env = env; //在子線程中, 無法使用這裏傳遞過去的 JNIEnv; JNIEnv 不能跨線程.
    context->instance = env->NewGlobalRef(instance); //將上下文對象傳入到結構體
    pthread_t  pid; //創建一個句柄
    pthread_create(&pid, 0, myThreadTask ,context); //啓動線程
    pthread_join(pid, nullptr);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章