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.编写代码实现头文件中的方法,
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)。
(*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;
}