動態註冊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"


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章