动态注册JNI

 

动态注册JNI

分类: JNI/NDK 5572人阅读 评论(6) 收藏 举报

在纯java中使用JNI文章中可以看到,javanative方法与C/C++代码函数是通过Java_<包名>_<类名>_<方法名>这种方式对应的,即它是静态注册的。当需要使用现有的C/C++代码函数时,需要以这种形式定义包装函数,在包装函数中调用现有C/C++代码函数;而且这样的函数名也非常长,不适合管理。使用动态注册,可以不受上述命名的限制。

运行下面示例需要安装NDK及搭建环境,请看另一篇文章使用NDK与环境搭建

下面我将Android NDK中的samples\hello-jni示例,由原来的静态注册改为动态注册,只需要改JNI部分。

samples\hello-jni\jni\hello-jni.c的原代码如下

  1. #include <string.h>  
  2. #include <jni.h>  
  3.   
  4. /* This is a trivial JNI example where we use a native method 
  5.  * to return a new VM String. See the corresponding Java source 
  6.  * file located at: 
  7.  * 
  8.  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java 
  9.  */  
  10. jstring  
  11. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  
  12.                                                   jobject thiz )  
  13. {  
  14.     return (*env)->NewStringUTF(env, "Hello from JNI !");  
  15. }  
#include <string.h>
#include <jni.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}


将其改为

  1. #include <stdlib.h>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <jni.h>  
  5. #include <assert.h>  
  6.   
  7. /* This is a trivial JNI example where we use a native method 
  8.  * to return a new VM String. See the corresponding Java source 
  9.  * file located at: 
  10.  * 
  11.  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java 
  12.  */  
  13. jstring native_hello(JNIEnv* env, jobject thiz)  
  14. {  
  15.     return (*env)->NewStringUTF(env, "动态注册JNI");  
  16. }  
  17.   
  18. /** 
  19. * 方法对应表 
  20. */  
  21. static JNINativeMethod gMethods[] = {  
  22.     {"stringFromJNI""()Ljava/lang/String;", (void*)native_hello},//绑定  
  23. };  
  24.   
  25. /* 
  26. * 为某一个类注册本地方法 
  27. */  
  28. static int registerNativeMethods(JNIEnv* env  
  29.         , const char* className  
  30.         , JNINativeMethod* gMethods, int numMethods) {  
  31.     jclass clazz;  
  32.     clazz = (*env)->FindClass(env, className);  
  33.     if (clazz == NULL) {  
  34.         return JNI_FALSE;  
  35.     }  
  36.     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {  
  37.         return JNI_FALSE;  
  38.     }  
  39.   
  40.     return JNI_TRUE;  
  41. }  
  42.   
  43.   
  44. /* 
  45. * 为所有类注册本地方法 
  46. */  
  47. static int registerNatives(JNIEnv* env) {  
  48.     const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类  
  49.     return registerNativeMethods(env, kClassName, gMethods,  
  50.             sizeof(gMethods) / sizeof(gMethods[0]));  
  51. }  
  52.   
  53. /* 
  54. * System.loadLibrary("lib")时调用 
  55. * 如果成功返回JNI版本, 失败返回-1 
  56. */  
  57. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  58.     JNIEnv* env = NULL;  
  59.     jint result = -1;  
  60.   
  61.     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  62.         return -1;  
  63.     }  
  64.     assert(env != NULL);  
  65.   
  66.     if (!registerNatives(env)) {//注册  
  67.         return -1;  
  68.     }  
  69.     //成功  
  70.     result = JNI_VERSION_1_4;  
  71.   
  72.     return result;  
  73. }  
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring native_hello(JNIEnv* env, jobject thiz)
{
    return (*env)->NewStringUTF(env, "动态注册JNI");
}

/**
* 方法对应表
*/
static JNINativeMethod gMethods[] = {
	{"stringFromJNI", "()Ljava/lang/String;", (void*)native_hello},//绑定
};

/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env
		, const char* className
		, JNINativeMethod* gMethods, int numMethods) {
	jclass clazz;
	clazz = (*env)->FindClass(env, className);
	if (clazz == NULL) {
		return JNI_FALSE;
	}
	if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
		return JNI_FALSE;
	}

	return JNI_TRUE;
}


/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
	const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类
	return registerNativeMethods(env, kClassName, gMethods,
			sizeof(gMethods) / sizeof(gMethods[0]));
}

/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	JNIEnv* env = NULL;
	jint result = -1;

	if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		return -1;
	}
	assert(env != NULL);

	if (!registerNatives(env)) {//注册
		return -1;
	}
	//成功
	result = JNI_VERSION_1_4;

	return result;
}

改成动态注册后,当调用HelloJni类的public native String  stringFromJNI()方法时,会找到动态注册的native_hello函数。


上面的代码没什么难点,唯一看不明白的可能是方法对应表,下面来讲讲

JNINativeMethod是JNI机制定义的一个结构体

  1. typedef struct {    
  2. const char* name;  //Java中函数的名字  
  3. const char* signature;  //用字符串描述的函数的参数和返回值  
  4. void* fnPtr;  //指向C函数的函数指针  
  5. } JNINativeMethod;   
typedef struct {  
const char* name;  //Java中函数的名字
const char* signature;  //用字符串描述的函数的参数和返回值
void* fnPtr;  //指向C函数的函数指针
} JNINativeMethod; 

比较难以理解的是第二个参数,例如

"()V"

"(II)V"

"(Ljava/lang/String;Ljava/lang/String;)V"


实际上这些字符是与函数的参数类型一一对应的。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);


具体的每一个字符的对应关系如下

字符 Java类型 C类型

V      void            void
Z       jboolean     boolean
I        jint              int
J       jlong            long
D      jdouble       double
F      jfloat            float
B      jbyte            byte
C      jchar           char
S      jshort          short


数组则以"["开始,用两个字符表示

[I       jintArray      int[]
[F     jfloatArray    float[]
[B     jbyteArray    byte[]
[C    jcharArray    char[]
[S    jshortArray   short[]
[D    jdoubleArray double[]
[J     jlongArray     long[]
[Z    jbooleanArray boolean[]


上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject


如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"



在纯java中使用JNI文章中可以看到,javanative方法与C/C++代码函数是通过Java_<包名>_<类名>_<方法名>这种方式对应的,即它是静态注册的。当需要使用现有的C/C++代码函数时,需要以这种形式定义包装函数,在包装函数中调用现有C/C++代码函数;而且这样的函数名也非常长,不适合管理。使用动态注册,可以不受上述命名的限制。

运行下面示例需要安装NDK及搭建环境,请看另一篇文章使用NDK与环境搭建

下面我将Android NDK中的samples\hello-jni示例,由原来的静态注册改为动态注册,只需要改JNI部分。

samples\hello-jni\jni\hello-jni.c的原代码如下

  1. #include <string.h>  
  2. #include <jni.h>  
  3.   
  4. /* This is a trivial JNI example where we use a native method 
  5.  * to return a new VM String. See the corresponding Java source 
  6.  * file located at: 
  7.  * 
  8.  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java 
  9.  */  
  10. jstring  
  11. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  
  12.                                                   jobject thiz )  
  13. {  
  14.     return (*env)->NewStringUTF(env, "Hello from JNI !");  
  15. }  
#include <string.h>
#include <jni.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}


将其改为

  1. #include <stdlib.h>  
  2. #include <string.h>  
  3. #include <stdio.h>  
  4. #include <jni.h>  
  5. #include <assert.h>  
  6.   
  7. /* This is a trivial JNI example where we use a native method 
  8.  * to return a new VM String. See the corresponding Java source 
  9.  * file located at: 
  10.  * 
  11.  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java 
  12.  */  
  13. jstring native_hello(JNIEnv* env, jobject thiz)  
  14. {  
  15.     return (*env)->NewStringUTF(env, "动态注册JNI");  
  16. }  
  17.   
  18. /** 
  19. * 方法对应表 
  20. */  
  21. static JNINativeMethod gMethods[] = {  
  22.     {"stringFromJNI""()Ljava/lang/String;", (void*)native_hello},//绑定  
  23. };  
  24.   
  25. /* 
  26. * 为某一个类注册本地方法 
  27. */  
  28. static int registerNativeMethods(JNIEnv* env  
  29.         , const char* className  
  30.         , JNINativeMethod* gMethods, int numMethods) {  
  31.     jclass clazz;  
  32.     clazz = (*env)->FindClass(env, className);  
  33.     if (clazz == NULL) {  
  34.         return JNI_FALSE;  
  35.     }  
  36.     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {  
  37.         return JNI_FALSE;  
  38.     }  
  39.   
  40.     return JNI_TRUE;  
  41. }  
  42.   
  43.   
  44. /* 
  45. * 为所有类注册本地方法 
  46. */  
  47. static int registerNatives(JNIEnv* env) {  
  48.     const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类  
  49.     return registerNativeMethods(env, kClassName, gMethods,  
  50.             sizeof(gMethods) / sizeof(gMethods[0]));  
  51. }  
  52.   
  53. /* 
  54. * System.loadLibrary("lib")时调用 
  55. * 如果成功返回JNI版本, 失败返回-1 
  56. */  
  57. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {  
  58.     JNIEnv* env = NULL;  
  59.     jint result = -1;  
  60.   
  61.     if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  62.         return -1;  
  63.     }  
  64.     assert(env != NULL);  
  65.   
  66.     if (!registerNatives(env)) {//注册  
  67.         return -1;  
  68.     }  
  69.     //成功  
  70.     result = JNI_VERSION_1_4;  
  71.   
  72.     return result;  
  73. }  
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */
jstring native_hello(JNIEnv* env, jobject thiz)
{
    return (*env)->NewStringUTF(env, "动态注册JNI");
}

/**
* 方法对应表
*/
static JNINativeMethod gMethods[] = {
	{"stringFromJNI", "()Ljava/lang/String;", (void*)native_hello},//绑定
};

/*
* 为某一个类注册本地方法
*/
static int registerNativeMethods(JNIEnv* env
		, const char* className
		, JNINativeMethod* gMethods, int numMethods) {
	jclass clazz;
	clazz = (*env)->FindClass(env, className);
	if (clazz == NULL) {
		return JNI_FALSE;
	}
	if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
		return JNI_FALSE;
	}

	return JNI_TRUE;
}


/*
* 为所有类注册本地方法
*/
static int registerNatives(JNIEnv* env) {
	const char* kClassName = "com/example/hellojni/HelloJni";//指定要注册的类
	return registerNativeMethods(env, kClassName, gMethods,
			sizeof(gMethods) / sizeof(gMethods[0]));
}

/*
* System.loadLibrary("lib")时调用
* 如果成功返回JNI版本, 失败返回-1
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
	JNIEnv* env = NULL;
	jint result = -1;

	if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		return -1;
	}
	assert(env != NULL);

	if (!registerNatives(env)) {//注册
		return -1;
	}
	//成功
	result = JNI_VERSION_1_4;

	return result;
}

改成动态注册后,当调用HelloJni类的public native String  stringFromJNI()方法时,会找到动态注册的native_hello函数。


上面的代码没什么难点,唯一看不明白的可能是方法对应表,下面来讲讲

JNINativeMethod是JNI机制定义的一个结构体

  1. typedef struct {    
  2. const char* name;  //Java中函数的名字  
  3. const char* signature;  //用字符串描述的函数的参数和返回值  
  4. void* fnPtr;  //指向C函数的函数指针  
  5. } JNINativeMethod;   
typedef struct {  
const char* name;  //Java中函数的名字
const char* signature;  //用字符串描述的函数的参数和返回值
void* fnPtr;  //指向C函数的函数指针
} JNINativeMethod; 

比较难以理解的是第二个参数,例如

"()V"

"(II)V"

"(Ljava/lang/String;Ljava/lang/String;)V"


实际上这些字符是与函数的参数类型一一对应的。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);


具体的每一个字符的对应关系如下

字符 Java类型 C类型

V      void            void
Z       jboolean     boolean
I        jint              int
J       jlong            long
D      jdouble       double
F      jfloat            float
B      jbyte            byte
C      jchar           char
S      jshort          short


数组则以"["开始,用两个字符表示

[I       jintArray      int[]
[F     jfloatArray    float[]
[B     jbyteArray    byte[]
[C    jcharArray    char[]
[S    jshortArray   short[]
[D    jdoubleArray double[]
[J     jlongArray     long[]
[Z    jbooleanArray boolean[]


上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject


如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"

猜你喜欢

转载自blog.csdn.net/xyzzf/article/details/45678239
今日推荐