JNI--使用RegisterNatives注册本地方法


RegisterNatives

jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
向clazz参数指定的类注册本地方法。methods参数将指定JNINativeMethod结构的数组,其中包含本地方法的名称、签名和函数指针。nMethods参数将指定数组中的本地方法数。JNINativeMethod 结构定义如下所示:
typedef struct {
    char *name;	//java方法名称
    char *signature;//java方法签名,通过javap-s-private类名打印出方法的签名
    void *fnPtr;//c/c++的函数指针 ,指向c/c++中的实现方法
} JNINativeMethod;
函数指针通常必须有下列签名:
ReturnType (*fnPtr)(JNIEnv *env, jobject objectOrClass, ...);

参数: 
env:JNI接口指针。 
clazz:Java类对象。 
methods:类中的本地方法。 
nMethods:类中的本地方法数。

返回值: 
成功时返回 “0”;失败时返回负数。


UnregisterNatives

jint UnregisterNatives(JNIEnv *env, jclass clazz);

取消注册类的本地方法。类将返回到链接或注册了本地方法函数前的状态。该函数不应在常规平台相关代码中使用。相反,它可以为某些程序提供一种重新加载和重新链接本地库的途径。

参数: 
env:JNI接口指针。 
clazz:Java类对象。

返回值: 
成功时返回“0”;失败时返回负数


在Android开发中使用本地代码时,传统方式的步骤是:

1.编写带有native方法的Java类;
2.使用javah命令生成.h头文件;
3.编写代码实现头文件中的方法,
这种方式的缺点是必须遵循特定的命名方式

RegisterNative方法相对于这种方式的优点有:(1)更有效率的查找函数(2)可在执行期间进行抽换

JNI组件在加载和卸载时,会进行函数回调,当VM执行到System.loadLibrary(xxx)函数时,首先会去执行JNI组件中的JNI_OnLoad()函数,而当VM释放该组件时会呼叫JNI_OnUnload()函数。
 onLoad方法,在System.loadLibrary()执行时被调用  
     jint JNI_OnLoad(JavaVM* vm, void* reserved){  
         LOGI("JNI_OnLoad startup~~!");  
  	 return JNI_VERSION_1_4;  
      }   
 onUnLoad方法,在JNI组件被释放时调用  
      void JNI_OnUnload(JavaVM* vm, void* reserved){  
          LOGE("call JNI_OnUnload ~~!!");  
      } 

JNI_OnLoad()有两个重要的作用:
(1)指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,而新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer, 就必须藉由JNI_OnLoad()函数来告知VM。
(2)初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

通过JNIEnv接口java可以访问本地代码中的变量和函数,本地代码也能访问java类的成员和成员方法。
为了能够拿到JNIEnv接口,我们可以通过javaVM指针获取,获取javaVM指针有下面两种方式:
(1)在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* jvm, void* reserved)第一个参数是传入JavaVM指针。
(2)在native code中调用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)可以得到JavaVM指针。
拿到了javaVm指针后我们就能获取到JNIEnv接口:
(*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL)
(*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6)。
JNI_VERSION_1_6表示的是JNI的版本号

使用RegisterNatives流程大致如下:


(1)在java类中定义Native方法:

public class OpenNIEx {
    static {
        System.loadLibrary("Jni");//加载库
    }
    public native static int Init(int W, int H, int W, int H);
    public static native int GetData(int[] Data);
}

(2)在本地代码中添加上面类的路径和定义映射关系

#define  JNIREG_CLASS "com/test/jni/OpenNIEx"
JNINativeMethod jniMethods[] = {
{ "Init", "(IIII)I", (void*)&Init},
{ "GetData", "([I)I", (void*)&GetData},}
"Init"是OpenNIEx定义的native方法,"(IIII)I"是Init方法的签名,可以通过javap -s -p 类名获取方法的签名,(void*)&Init的意思是指向本地代码中定义的Init方法GetData()方法同理

本地的的Init和GetData方法实现如下:
jint Init(JNIEnv *env ,jobject cls, jint imageW, jint imageH, jint depthW, jint depthH){
   方法业务实现处
    return 0;
}
jint GetDepthData(JNIEnv* env, jobject obj, jintArray data){
    jint *array = env->GetIntArrayElements(data, 0);
    if(array == NULL){
        env->ReleaseIntArrayElements(data,array,0);
        return -1;
    }
    方法业务实现处 
    env->ReleaseIntArrayElements(data,array,0);
    return 0;
}

(3)在JNI_OnLoad(JavaVM* vm, void* reserved)方法中调用RegisterNatives方法

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env;
    jclass gCallbackClass = NULL;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    jclass clz     = env ->FindClass(JNIREG_CLASS);
   // gCallbackClass = (jclass)env ->NewGlobalRef(clz);
    env ->RegisterNatives(clz, jniMethods, sizeof(jniMethods) / sizeof(JNINativeMethod));
   // env ->DeleteLocalRef(clz);
    return JNI_VERSION_1_6;
}

上面就是RegisterNatives注册本地方法的大致流程,编译jni的步骤未作介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值