栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android NDK开发之JNI的注册以及子线程的使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android NDK开发之JNI的注册以及子线程的使用

一、静态注册

在jni开发默认的情况下,用的就是静态注册,静态注册是最简单的方式,在NDK开发过程中,基本上使用静态注册,静态注册比动态注册要简单,但是在诸多的系统源码中,会发现会有大量都是采用动态注册,因为动态注册虽然麻烦,但是比静态注册安全性高,不用暴露包名类名等。

静态注册的优点:

(1)开发简单;

(2)使用方便;

(3)便于阅读。

缺点:

(1)、JNI的函数名过长;

(2)、捆绑上层的包名和类名;

(3)、运行期才会去匹配JNI函数,性能上低于动态注册。

例子:

1、MainActivity

public native String stringFromJNI(); // 默认生成的,其实也是静态注册方式 
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Log.d(TAG, "onCreate stringFromJNI:" + stringFromJNI());
}

2、native-lib.cpp

#include 
#define TAG "JNISTUDY"
// __VA_ARGS__ 代表 ...的可变参数
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#include 
#include 
#include  // 在AS上 pthread不需要额外配置,默认就有



extern "C" JNIEXPORT jstring JNICALL
Java_com_derry_as_1jni_1project_MainActivity_stringFromJNI(
 JNIEnv *env,
 jobject ) {
 std::string hello = "默认就是静态注册哦";
 return env->NewStringUTF(hello.c_str());
}
二、动态注册

在系统源码中,基本上都会采用动态注册,动态注册比静态注册麻烦,但是效率高,在被编译后安全性更高,并且在native中调用,函数名简洁,便于用户阅读。

1、MainActivity.java

 public native void dynamicJavaMethod01(); // 动态注册1
    public native int dynamicJavaMethod02(String valueStr); // 动态注册2
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void dynamic01(View view) {
        dynamicJavaMethod01();
    }

    public void dynamic02(View view) {
        dynamicJavaMethod02("你好,JNI!");
    }

2、native-lib.cpp

JavaVM *jVm = nullptr;//定义全局的javavm变量
const char  *mainActivityClassName = "com/myapplication/MainActivity";//指定java的类名

//无参类型动态注册函数
void dynamicMethod01(){
    LOGD("第一个动态注册的函数!")
}

//有参类型动态注册函数
int dynamicMethod02(JNIEnv *env,jobject mainthis,jstring jstr){
    const char *text = env->GetStringUTFChars(jstr, nullptr);
    LOGD("得到了java层的值:%s",text)
    env->ReleaseStringUTFChars(jstr,text);
    return 200;
}


static const JNINativeMethod jniNativeMethod[] = {
        {"dynamicJavaMethod01","()V",(void *)(dynamicMethod01)},
        {"dynamicJavaMethod02","(Ljava/lang/String;)I",(int *)dynamicMethod02}
};
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *unused) {//必须重写默认的构造函数
    ::jVm = vm;
    JNIEnv *jniEnv = nullptr;
    int result = vm->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
    //result等于0就是成功
    if (result != JNI_OK){
        return -1;
    }
    //找到java层的类型
    jclass  mainActivityClass = jniEnv->FindClass(mainActivityClassName);
    //进行动态注册
    jniEnv->RegisterNatives(mainActivityClass,jniNativeMethod,sizeof(jniNativeMethod)/sizeof(JNINativeMethod));
    return JNI_VERSION_1_6;
}
三、JNI子线程的使用

1、MainActivity.java

 public native void naitveThread(); // Java层 调用 Native层 的函数,完成JNI线程
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void nativeCallJava(View view) {
        naitveThread();
    }

    
    public void updateActivityUI() {
        Log.d("java", "恭喜您,开启子线程成功!");
        runOnUiThread(new Runnable() { // 哪怕是异步线程 UI操作 正常下去 runOn
            @Override
            public void run() {
                // 可以在子线程里面 操作UI
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("子线程")
                        .setMessage("我是子线程")
                        .setPositiveButton("确定", null)
                        .show();
            }
        });
    }

2、native-lib.cpp

//由于不能跨线程,所以必须定义一个全局的JNIEnv和jobject
class MyContext{
public:
    JNIEnv *jniEnv = nullptr;
    jobject object = nullptr;
};

//执行异步线程调用java层的方法
void *myThreadTashAction(void *pVoid){
    LOGE("myThreadTashAction");
    MyContext * myContext = static_cast(pVoid);
    //安卓进程中只有一个JavaVM,是全局的,可以跨线程
    JNIEnv * jniEnv  = nullptr;//全新的jnieNV异步线程里面操作
    jint attachResult = ::jVm->AttachCurrentThread(&jniEnv, nullptr);//绑定当前线程
    if (attachResult != JNI_OK){
        return 0;
    }
    //调用java层的函数
    //1、拿到class
    jclass mainActivityClass = jniEnv->GetObjectClass(myContext->object);
    //2、拿到方法
    jmethodID updateActivityUI = jniEnv->GetMethodID(mainActivityClass,"updateActivityUI","()V");
    //3、调用
    jniEnv->CallVoidMethod(myContext->object,updateActivityUI);
    ::jVm->DetachCurrentThread();//必须解除绑定当前线程,否则报错
    return nullptr;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_myapplication_MainActivity_naitveThread(JNIEnv *env, jobject thiz) {
       MyContext * myContext = new MyContext;
       myContext->jniEnv = env;
       myContext->object = env->NewGlobalRef(thiz);//提升全局引用

       //开启子线程
       pthread_t  pid;
       pthread_create(&pid, nullptr,myThreadTashAction,myContext);
       pthread_join(pid, nullptr);
}

3、jni线程总结:

(1)jni的线程与java的一样,默认是在主线程执行的,子线程需要另外开启;

(2)JavaVM对整个jni层来说是全局的,可以跨程线,也就是在任何地方都可以绑定当前线程,并且只有一个地址;

(3)JNIEnv是局步变量,不能跨线程,主线程与子线程绑定地址不一样;

(4)jobject不能跨线程,java层中谁调用了JNI函数,谁的实例就会给jobject;

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/870926.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号