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

2020-03-19  本文已影响0人  ftd黑马

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;
}

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

上一篇 下一篇

猜你喜欢

热点阅读