廢話不多說请琳,直接開始
Demo傳送門
注意:無論是動靜態(tài)注冊還是動態(tài)注冊,Java端代碼都是一樣的疙赠,所謂的靜動態(tài)注冊是指在C/C++里面的操作交掏,下面會具體講
靜態(tài)注冊
java代碼:
public class JniTest {
static {
System.loadLibrary("jniTest");
}
public static native String stringFromJNI();
public static native String stringFromJniWithStr(Object context,String value);
}
C/C++代碼:
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_mirkowu_jintest_jni_JniTest_stringFromJNI(JNIEnv *env, jclass clazz) {
std::string hello = "Hello from C++ by 靜態(tài)注冊";
return env->NewStringUTF(hello.c_str());
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_mirkowu_jintest_jni_JniTest_stringFromJniWithStr(JNIEnv *env, jclass clazz,
jobject context, jstring value) {
const char *str = env->GetStringUTFChars(value, JNI_FALSE);
return env->NewStringUTF(str);
}
動態(tài)注冊
動態(tài)注冊基本流程
- 編寫Java端的相關native方法
- 編寫C/C++代碼, 實現(xiàn)JNI_Onload()方法
- 將Java 方法和 C/C++方法通過簽名信息一一對應起來
- 通過JavaVM獲取JNIEnv, JNIEnv主要用于獲取Java類和調用一些JNI提供的方法
- 使用類名和對應起來的方法作為參數(shù), 調用JNI提供的函數(shù)RegisterNatives()注冊方法
所謂的動態(tài)注冊 是指,動態(tài)注冊JAVA的Native方法刨肃,使得c/c++里面方法名 可以和 java 的Native方法名可以不同古拴, 動態(tài)注冊是將將二者方法名關聯(lián)起來,以后在修改Native方法名時真友,只需修改動態(tài)注冊關聯(lián)的方法名稱即可黄痪。 System.loadLibrary("xxx"); 這個方法還是必須要調用的,不管動態(tài)還是靜態(tài)盔然。
java代碼:
public class JniDynamicRegisterTest {
static {
System.loadLibrary("dynamic_register");
}
public static native String a();
public static native String b(Object context,String value);
}
C/C++代碼:
#include <jni.h>
#include <string>
#include <assert.h>
// ------------------------------- 以下是動態(tài)注冊 --------------------------------
jstring aaa(JNIEnv *env, jclass clazz) {
std::string hello = "Hello from C++ by 動態(tài)注冊";
return env->NewStringUTF(hello.c_str());
}
jstring getStringWithDynamicReg(JNIEnv *env, jclass clazz, jobject context, jstring value) {
const char *str = env->GetStringUTFChars(value, JNI_FALSE);
return env->NewStringUTF(str);
}
/**
* 所謂的動態(tài)注冊 是指满力,動態(tài)注冊JAVA的Native方法,使得c/c++里面方法名 可以和 java 的Native方法名可以不同轻纪,
* 動態(tài)注冊是將將二者方法名關聯(lián)起來油额,以后在修改Native方法名時,只需修改動態(tài)注冊關聯(lián)的方法名稱即可
* System.loadLibrary("xxx"); 這個方法還是必須要調用的刻帚,不管動態(tài)還是靜態(tài)
*/
#define JNIREG_CLASS "com/mirkowu/jintest/jni/JniDynamicRegisterTest" //Java類的路徑:包名+類名
#define NUM_METHOES(x) ((int) (sizeof(x) / sizeof((x)[0]))) //獲取方法的數(shù)量
static JNINativeMethod method_table[] = {
// 第一個參數(shù)a 是java native方法名潦嘶,
// 第二個參數(shù) 是native方法參數(shù),括號里面是傳入?yún)⒌念愋停膺叺氖欠祷刂殿愋停? // 第三個參數(shù) 是c/c++方法參數(shù),括號里面是返回值類型崇众,
{"a", "()Ljava/lang/String;", (jstring *) aaa},
{"b", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;", (jstring *) getStringWithDynamicReg},
};
static int registerMethods(JNIEnv *env, const char *className,
JNINativeMethod *gMethods, int numMethods) {
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
//注冊native方法
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
assert(env != NULL);
// 注冊native方法
if (!registerMethods(env, JNIREG_CLASS, method_table, NUM_METHOES(method_table))) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
簽名
JNI 簽名
動態(tài)注冊中 JNINativeMethod 結構體中第二個參數(shù)需注意
括號內代表傳入?yún)?shù)的簽名符號掂僵,為空可以不寫航厚,括號外代表返回參數(shù)的簽名符號,為空填寫 V,對應關系入下表
簽名符號 | C/C++ | java |
---|---|---|
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 |
[Z | jbooleanArray | boolean[] |
[I | jintArray | int[] |
[J | jlongArray | long[] |
[D | jdoubleArray | double[] |
[F | jfloatArray | float[] |
[B | jbyteArray | byte[] |
[C | jcharArray | char[] |
[S | jshortArray | short[] |
特殊的String |
||
Ljava/lang/String; | jstring | String |
L完整包名加類名; | jobject | class |
舉個例子:
傳入的java參數(shù)有兩個 分別是 int 和 long[] 函數(shù)返回值為 String
即函數(shù)的定義為:String getString(int a ,long[] b)
簽名就應該是 :"(I[J)Ljava/lang/String;"(不要漏掉英文分號)
如果有內部類 則用 FileStatus;
總結
靜態(tài)注冊
- 優(yōu)點: 理解和使用方式簡單, 屬于傻瓜式操作, 使用相關工具按流程操作就行, 出錯率低
- 缺點: 當需要更改類名,包名或者方法時, 需要按照之前方法重新生成頭文件, 靈活性不高
動態(tài)注冊
- 優(yōu)點: 靈活性高, 更改類名,包名或方法時, 只需對更改模塊進行少量修改, 效率高
- 缺點: 對新手來說稍微有點難理解, 同時會由于搞錯簽名, 方法, 導致注冊失敗