细说JNI与NDK(四)动态和静态注册

时间:2021-7-3 作者:qvyue

细说JNI与NDK专题目录:

细说JNI与NDK(一) 初体验
细说JNI与NDK(二)基本操作)
细说JNI与NDK(三)ndk 配置说明
细说JNI与NDK(四)动态和静态注册
细说JNI与NDK(五)JNI 线程
细说JNI与NDK(六)静态缓存,异常捕获,内置函数
细说JNI与NDK(七)Parcel底层JNI思想与OpenCV简单对比

Android Framework源代码中的动态注册

细说JNI与NDK(四)动态和静态注册
2021-04-19 12.34.19.png

静态注册和动态注册

静态注册

默认情况下,就是静态注册,静态注册是最简单的方式,NDK开发过程中,基本上使用静态注册。前面的知识都是静态注册的方式。

优点: 开发简单
缺点:

  1. JNI函数名非常长
  2. 捆绑 上层 包名 + 类名
  3. 运行期 才会去 匹配JNI函数,性能上 低于 动态注册

动态注册

再看Android Framework源代码的Native层,Android 系统的C++源码:基本上都是动态注册。

动态注册是怎么玩转的?

明白一个简单的道理,Java中我们new 类,默认会调用构造函数,重写了构造函数,就会调用我们自己的。NDK中Jni函数也是这样,默认会有JNI_OnLoad 一系列函数,我们重写JNI_OnLoad来加载我们自己的逻辑。

当我们调用,System.loadLiberary(“xxxxx”);

实际上自动调用了JNI_OnLoad 做动态注册

先看下示例代码
➜ Java 部分


public native void dynamicJavaM01();
public native int dynamicJavaM01(String value);
调用
case R.id.jni_regist_1:
                dynamicJavaM01();
                break;
 case R.id.jni_regist_2:
                dynamicJavaM01("JNI动态注册,JNI传参");
                break;

➜ C++ 部分


#include "include/common_head.h"
//JNIEnv *env, jobject thiz,  默认这两个参数是可以省略,如果不用的话
//void dynamicM01(JNIEnv *env, jobject thiz)
void dynamicM01() {
    LOGD("我是动态注册的函数 dynamicM01...");
}

int dynamicM02(JNIEnv *env, jobject thiz, jstring value) {
    const char * text = env->GetStringUTFChars(value, nullptr);
    LOGD("我是动态注册的函数 dynamicM02...%s",text);
    env->ReleaseStringUTFChars(value,text);
    return 200;
}

JavaVM *javaVm;
const char *class_name = "top/zcwfeng/jni/JavaJNIActivity";
//name,signature,*
static const JNINativeMethod methods[] = {
        {"dynamicJavaM01", "()V",                   (void *) (dynamicM01)},
        {"dynamicJavaM02", "(Ljava/lang/String;)I", (int *) (dynamicM02)},
};

jint JNI_OnLoad(JavaVM *vm, void *unused) {
    ::javaVm = vm;
    JNIEnv *jniEnv = nullptr;
    int result = javaVm->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
    //result 等与0 成功,默认不成文规则,封装库都是成功就是0【如ffmpeg库等】
    if (result != JNI_OK) {
        return -1;
    }
    LOGE("System.loadLibrary --->JNI Load init success");

    jclass clazz = jniEnv->FindClass(class_name);

    //RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
    jniEnv->RegisterNatives(clazz, methods,sizeof(methods)/sizeof(JNINativeMethod));

    LOGE("动态 注册 dynamic success");
    return JNI_VERSION_1_6;// AS的JDK在JNI默认最高1.6  Java的JDKJNI 1.8
}

  • JNI_OnLoad 中赋值我们用了域的方式,如:this.a = a —> ::javaVm = vm
  • 默认不成文规则,C 中封装库都是成功就是0
  • AS的JDK目前JNI默认最高1.6 和 Java的JDK的JNI 1.8不同
  • JNI_OK 就是JNI提供的一些宏定义方便使用。
  • 动态注册的方法参数,JNIEnv *env 和 jobject thiz,如果没有用到是可以不放到参数列表中,没有影像。
  • 动态注册核心RegisterNatives

① 重写JNI_OnLoad
② JavaVM 初始化获取JNIEnv,并获取到jclass
③ 注册函数

//RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
    jniEnv->RegisterNatives(clazz, methods,sizeof(methods)/sizeof(JNINativeMethod));

参数需要提供,所在的类,数组(包含需要动态注册的方法),数组的大小
小技巧,传入JNINativeMethod* 以为指针相当于我们定义一个数组

如何选择看个人喜好和代码的设计
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。