美文网首页
3-ndk学习之jni基础篇(3)

3-ndk学习之jni基础篇(3)

作者: ftd黑马 | 来源:发表于2020-03-19 16:41 被阅读0次

    jni多线程操作

    这里的效果是在jni中开启子线程,然后在子线程直接调用Activity的方法
    jni中,jvm是跨线程的,但是env不可以跨线程使用,所以在子线程中需要新创建一个env,创建env需要使用到jvm,所有需要重写JIN_Onload方法,这个方法是System.loadLibrary("native-lib")加载so库后调用的,可以拿到jvm对象。
    直接撸码:
    MainActivity中:

    static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            testThread();
        }
        private native void testThread();
    
    @Override
        protected void onDestroy() {
            super.onDestroy();
            unThread();
        }
    
        public native void unThread();
    
        // AndroidUI操作,让C++线程里面来调用
        public void updateUI() {
            if (Looper.getMainLooper() == Looper.myLooper()) {
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("UI")
                        .setMessage("updateUI run ...")
                        .setPositiveButton("知道了", null)
                        .show();
            } else {
                Log.d("MainActivity", "updateUI: 所属与子线程.......只能打印日志了");
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        new AlertDialog.Builder(MainActivity.this)
                                .setTitle("UI")
                                .setMessage("updateUI run ...")
                                .setPositiveButton("知道了", null)
                                .show();
                    }
                });
            }
        }
    

    native-lib中:

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    #include <pthread.h>
    #include <android/log.h>
    
    #define TAG "ftd"
    
    #define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)
    
    JavaVM * jvm;
    //此方法是MainActivity中        System.loadLibrary("native-lib");后调这的
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * javaVm, void * pVoid) {
        //jvm是跨平台的,env不能跨平台,所以需要重写这个方法获取到jvm对象,通过jvm对象获取的新的env使用
        jvm = javaVm;
        return JNI_VERSION_1_6;
    }
    
    
    //全部引用,这里的jobject1实际上就是MainActivity
    jobject jobject1;
    void * customThread(void * pVoid){
        JNIEnv * env = nullptr; // 全新的env
        int result = jvm->AttachCurrentThread(&env, 0); // 把native的线程,附加到JVM
        if (result != 0) {
            return 0;
        }
        jclass mainActivityClass = env->GetObjectClass(jobject1);
    
        // 拿到MainActivity的updateUI
        const char * sig = "()V";
        jmethodID updateUI = env->GetMethodID(mainActivityClass, "updateUI", sig);
    
        env->CallVoidMethod(jobject1, updateUI);
        // 解除附加到JVM 的native线程,如果不解除是会崩溃的
        jvm->DetachCurrentThread();
        return 0;
    }
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_example_myapplication_MainActivity_testThread(JNIEnv *env, jobject instance) {
        pthread_t pthreadId;
        // 全局的,就不会被释放,所以可以在线程里面用
        jobject1 = env->NewGlobalRef(instance);
        pthread_create(&pthreadId,0,customThread,instance);
        pthread_join(pthreadId,0);
    }
    /*
     * 释放全局引用
     */
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_example_myapplication_MainActivity_unThread(JNIEnv *env, jobject instance) {
        if(instance!=NULL){
            env->DeleteGlobalRef(jobject1);
            jobject1 = NULL;
    
        }
    }
    

    动态注册

    每次在activity中写好native方法,alt+回车自动生成的jni方法,类似于这样的(
    Java_com_example_myapplication_MainActivity_registerJava01),这些都是静态注册,简单,命名规则就是全包名+类名+方法名,缺点就是不安全,同理,动态注册,优点就是安全,自己起名字,缺点就是很麻烦。
    我认为它很绕,很不好理解,但是都是一些固定写法,写多了就熟了
    MainActivity中:

     static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            registerJava01("李AAAAAAAAAAAAAA");
        }
    
        // 动态注册的函数
        public native void registerJava01(String text);
    

    native-lib中:

    /**
     * 随意取名字了,
     * @param env
     * @param instance
     * @param text
     */
    void register01(JNIEnv * env, jobject instance, jstring text) {
        const char * textValue = env->GetStringUTFChars(text, NULL);
       LOGD("动态注册的函数执行了 %s", textValue);
        env->ReleaseStringUTFChars(text, textValue);
    }
    
    /**
     * 通过查看源码可以得知是一个结构体
     */
    static const JNINativeMethod jniNativeMethod[] = {
            {"registerJava01", "(Ljava/lang/String;)V", (void *)(register01)}
    };
    
    //此方法是MainActivity中        System.loadLibrary("native-lib");后调这的
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * javaVm, void * pVoid) {
        // 通过虚拟机 创建全新的 evn
        JNIEnv * jniEnv = nullptr;
        jint result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv), JNI_VERSION_1_6); // 参数2:是JNI的版本 NDK 1.6   JavaJni 1.8
        if (result != JNI_OK) {
            return -1; // 主动报错
        }
    
        const char * mainActivityClassStr = "com/example/myapplication/MainActivity";
        jclass mainActivityClass = jniEnv->FindClass(mainActivityClassStr);
    
        jniEnv->RegisterNatives(mainActivityClass, jniNativeMethod, sizeof(jniNativeMethod) / sizeof(JNINativeMethod)); // 参数三:到底要动态注册几个
    
        return JNI_VERSION_1_6;
    }
    

    如果有不对的地方,希望大家在评论区多多指正,共同学习,谢谢大家。

    相关文章

      网友评论

          本文标题:3-ndk学习之jni基础篇(3)

          本文链接:https://www.haomeiwen.com/subject/aqtlyhtx.html