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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章