阅读 627

Android JNI和NDK学习(三):动态注册

概述

当执行java的native方法时,虚拟机怎么知道要调用so中那个方法呢?这个就需要注册,通过注册把java的方法和so的方法绑定在一起,这样就可以找到对应的方法了,此篇文章仅作为笔记,以防以后忘记

有俩种注册的方式即 静态注册动态注册

静态注册

我们之前自动生成的项目就是静态注册的,我们看下代码

extern "C" JNIEXPORT jstring JNICALL
Java_com_text_ndk1_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
复制代码

当第一次调用调用java的native方法时,虚拟机会搜索对应的native函数,如果存在就会建立一个关联,以后再次调用,这部分操作就会由虚拟机完成

对应的规则

Java+包名+类名+方法名

其中用下划线进行分割,包名也用下划线进行分割

这样做会有点缺点:

  • 名字太长
  • 第一次调用需要搜索,影响效率

动态注册

  • 需要我们手动建立联系,增加了代码量但提高效率
  • 允许自己定义函数名字

加载动态库

java层通过System.loadLibrary()方法可以加载一个动态库,此时虚拟机就会调用jni库中的JNI_OnLoad()函数

jint JNI_OnLoad(JavaVM* vm, void* reserved);
复制代码

返回值代表,动态库需要的jni版本,如果虚拟机不能识别这个版本,那么就不可以加载这个动态库

目前的返回值有,JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。

如果动态库没有提供 JNI_OnLoad()函数会默认使用JNI_VERSION_1_1版本,但是这个版本太老,很多新函数没有,最好返回版本

注册函数

JNI_OnLoad()函数经常用来做一些初始化操作,动态注册就是在这里进行的

动态注册通过_JNIEnvRegisterNatives()函数来完成

函数原型为

jint RegisterNatives(JNIEnv *env, jclass clazz, 
        const JNINativeMethod *methods, jint nMethods);
复制代码
  • 第一个参数:JNIEnv *指针
  • 第二个参数:代表一个java类
  • 第三个参数:代表JNINativeMethod结构体数组,JNINativeMethod定义了java层函数和native层函数的映射关系
  • 第四个参数:代表第三个参数methods数组的大小

返回值 0代表成功,负值代表失败

JNINativeMethod结构体

RegisterNatives函数最重要的就是JNINativeMethod结构体,我们看下这个结构体

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
复制代码
  • name:代表java的native方法的名字
  • signature:表示方法的签名
  • fnPtr:是一个函数指针,指向jni层的一个函数,也就是java层和native层建立联系的函数

实践

现在我们有一个java类,里面有俩个native方法,下面我们来完成这俩个方法的动态注册

public class TextJni {

    static {
        System.loadLibrary("textjni_lib");
    }

    native int text(String message);

    static native int static_text(String message);
}
复制代码

首先手动书写C的对应的方法

jint native_text(JNIEnv *env, jobject jobject1, jstring msg) 

jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) 
复制代码

native_text对应java层的text方法,native_staic_text对应java层的static_text方法

那么是如何写出来的的呢?

  • native方法名,这个可以随便起
  • 返回值和java中的对应,java中是int,jni就是jint
  • 第一个参数是固定的是JNIEnv指针
  • 第二个参数目前我理解为固定jobject
  • 后面参数就是java的参数转化过来的

然后需要填充JNINativeMethod

static const JNINativeMethod nativeMethod[] = {
        {"text",        "(Ljava/lang/String;)I", (void *) native_text},
        {"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};
复制代码

这个上面已经讲过了

然后开始注册

static int registNativeMethod(JNIEnv *env) {
    int result = -1;
    //找到native方法所在的class
    jclass class_text = env->FindClass("com.text.ndk1.TextJni");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
        result = 0;
    }
    return result;
}
复制代码

这样就动态注册成功

看下完整代码

java代码

public class TextJni {

    static {
        System.loadLibrary("textjni_lib");
    }

    native int text(String message);

    static native int static_text(String message);
}
复制代码

C代码

#include <jni.h>
#include <string>
#include <android/log.h>

jint native_text(JNIEnv *env, jobject jobject1, jstring msg) {
    const char *p_msg = env->GetStringUTFChars(msg, JNI_FALSE);
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);

    return 0;
}

jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) {
    const char *p_msg = env->GetStringUTFChars(meg, JNI_FALSE);
    __android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);

    return 0;
}


static const JNINativeMethod nativeMethod[] = {
        {"text",        "(Ljava/lang/String;)I", (void *) native_text},
        {"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};

static int registNativeMethod(JNIEnv *env) {
    int result = -1;

    jclass class_text = env->FindClass("com.text.ndk1.TextJni");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
        result = 0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    int result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
        if (registNativeMethod(env) == JNI_OK) {
            result = JNI_VERSION_1_6;
        }
        return result;
    }
}
复制代码

调用

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextJni.static_text("我是静态方法,哈哈");
        new TextJni().text("我是普通方法,哈哈");
    }

   
}
复制代码

看下打印信息

mmm: method = native_staic_text, msg = 我是静态方法,哈哈
mmm: method = native_text, msg = 我是普通方法,哈哈
复制代码

参考

juejin.im/post/5d1f16…

blog.csdn.net/afei__/arti…

www.jianshu.com/p/b71aeb4ed…

关注下面的标签,发现更多相似文章
评论