JNI RegisterNatives注册原生方法

除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI。和传统方法相比,使用RegisterNatives的好处有三点:

  • C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式;
  • 效率高。传统方式下,Java类call本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),而使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数;
  • 运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次call RegisterNatives()方法,并传入不同的映射表参数即可。
    为了使用RegisterNatives,我们需要了解JNI_OnLoad和JNI_OnUnload函数。JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:
    • 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
    • 初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,RegisterNatives也在这里进行。

JNI_OnUnload()当VM释放该组件时被调用,JNI_OnUnload()函数的作用与JNI_OnLoad()对应,因此在该方法中进行善后清理,资源释放的动作最为合适。

JavaVM 和 JNIEnv

注册navtive方法之前我们需要了解JavaVM, JNIEnv:

JavaVM 和 JNIEnv 是JNI提供的结构体.

JavaVM 提供了允许你创建和销毁JavaVM的“invokation interface”。理论上在每个进程中你可以穿件多个JavaVM, 但是Android只允许创造一个。

JNIEnv 提供了大部分JNI中的方法。在你的Native方法中的第一个参数就是JNIEnv.

JNIEnv 用于线程内部存储。 因此, 不能多个线程共享一个JNIEnv. 在一段代码中如果无法获取JNIEnv, 你可以通过共享JavaVM并调用GetEnv()方法获取。
 

JNI_OnLoad()方法

当Java层代码中执行:

System.loadLibrary("NativeLib"); //NativeLib 为native模块名称

Native 中的 JNI_OnLoad(JavaVM *vm, void *reserved) 方法会被调用。此时可以注册对应于Java层调用的navtive方法。

JNINativeMethod结构体

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

以上代码为jni.h中的源码, 可见JNINativeMethod包含三个元素: 方法名, 方法签名, native函数指针。

该结构体用于描述需要注册的方法信息。

RegisterNatives方法

jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
        jint nMethods)

该方法是JNI环境提供的用于注册Native方法的方法。

hello-jni.c中

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

#define TAG "HELLO"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , TAG, __VA_ARGS__)

# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

jstring native_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}
static const char *classPathName = "com/example/hellojni/HelloJni";

static JNINativeMethod methods[] = {
        {"stringFromJNI", "()Ljava/lang/String;", (void*)native_stringFromJNI},
};

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jclass clazz;
    //获取JNI环境对象
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        return JNI_ERR;
    }
    //注册本地方法.Load 目标类
    clazz = (*env)->FindClass(env,classPathName);
    if (clazz == NULL)
    {
        LOGE("Native registration unable to find class '%s'", classPathName);
        return JNI_ERR;
    }
    //注册本地native方法
    if((*env)->RegisterNatives(env, clazz, methods, NELEM(methods)) < 0)
    {
        LOGE("ERROR: MediaPlayer native registration failed\n");
           return JNI_ERR;
    }

    /* success -- return valid version number */
    return JNI_VERSION_1_4;
}
  1. System.loadLibrary("hello-jni"); 时会调用 JNI_OnLoad
  2. RegisterNatives的第二个参数clazz包含了类的路径
  3. 定义JNINativeMethod时,第二个参数
        ()中的字符表示参数,后面表示返回值,
        "()V"----> void func()
        "(II)V"----> void func(int, int)   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值