1.概述
在上一次的筆記《JNI學(xué)習(xí)筆記》 中介紹了Native程序與Java程序的互相調(diào)用庸推。其中Java調(diào)用Nativie方法通常的步驟是:
- 聲明native方法:
private native void sayHello();
- 聲明native方法:
- 通過
javah
生成native程序的頭文件HelloJNI.h
- 通過
- 實(shí)現(xiàn)對應(yīng)的navtive方法
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
這種方法每次增加方法時(shí)讲冠, native方法名稱都需要帶上長長的一坨JNIEXPORT void JNICALL Java_HelloJNI_
枕赵, 包名很長的話更是不能忍剿吻。除此之外對于Native方法的方法名也不能修改譬猫。
如果你想擺脫這種冗長的規(guī)則拾弃, 不妨嘗試在JNI的JNI_OnLoad()
方法中通過RegisterNatives注冊Java需要調(diào)用的Native方法值桩。
2. JavaVM 和 JNIEnv
注冊navtive方法之前我們需要了解JavaVM, JNIEnv:
JavaVM
和 JNIEnv
是JNI提供的結(jié)構(gòu)體.
JavaVM
提供了允許你創(chuàng)建和銷毀JavaVM的
"invokation interface"。理論上在每個(gè)進(jìn)程中你可以穿件多個(gè)JavaVM
豪椿, 但是Android只允許創(chuàng)造一個(gè)奔坟。
JNIEnv
提供了大部分JNI中的方法。在你的Native方法中的第一個(gè)參數(shù)就是JNIEnv.
JNIEnv
用于線程內(nèi)部存儲搭盾。 因此咳秉, 不能多個(gè)線程共享一個(gè)JNIEnv. 在一段代碼中如果無法獲取JNIEnv, 你可以通過共享JavaVM
并調(diào)用GetEnv()
方法獲取鸯隅。
3. JNI_OnLoad()方法
當(dāng)Java層代碼中執(zhí)行:
System.loadLibrary("NativeLib"); //NativeLib 為native模塊名稱
Native 中的 JNI_OnLoad(JavaVM *vm, void *reserved)
方法會被調(diào)用澜建。此時(shí)可以注冊對應(yīng)于Java層調(diào)用的navtive方法。
4. JNINativeMethod結(jié)構(gòu)體
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
以上代碼為jni.h中的源碼滋迈, 可見JNINativeMethod包含三個(gè)元素: 方法名霎奢, 方法簽名, native函數(shù)指針饼灿。
該結(jié)構(gòu)體用于描述需要注冊的方法信息幕侠。
5. RegisterNatives方法
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)
該方法是JNI環(huán)境提供的用于注冊Native方法的方法。
6. 完整實(shí)現(xiàn)
6.1. Java代碼實(shí)現(xiàn)
Java層代碼不需要做任何調(diào)整
public class NativeLib {
public static native String getNativeString();
static {
System.loadLibrary("NativeLib");
}
6.2. C++ 代碼實(shí)現(xiàn)
6.2.1 實(shí)現(xiàn)Native方法
//可以隨意定義方法名
jstring getString()
{
JNIEnv *env = NULL;
g_jvm->AttachCurrentThread(&env, NULL); //g_jvm為JavaVM指針
return env->NewStringUTF("This is Natvie String!");
}
6.2.2 定義JNINativeMethod數(shù)組碍彭, 聲明需要注冊的方法
static JNINativeMethod methods[] = {
{"getNativeString", "()Ljava/lang/String;", reinterpret_cast<void*>(getString)}
};
其中getNativeString
為Java類中定義的Native方法名晤硕。
()Ljava/lang/String;
為方法的簽名, ()
表示該方法無參數(shù)庇忌, Ljava/lang/String;
表示返回值為Java中的String類型舞箍。具體簽名規(guī)則請參考《JNI學(xué)習(xí)筆記》 中的內(nèi)容。
reinterpret_cast<void*>(getString)
為Native實(shí)現(xiàn)的方法名皆疹。這里強(qiáng)制轉(zhuǎn)換成了函數(shù)指針疏橄。
6.2.3 JNI_OnLoad()方法實(shí)現(xiàn)
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env;
if (JNI_OK != vm->GetEnv(reinterpret_cast<void**> (&env),JNI_VERSION_1_4)) {
LOGW("JNI_OnLoad could not get JNI env");
return JNI_ERR;
}
g_jvm = vm; //用于后面獲取JNIEnv
jclass clazz = env->FindClass("com/example/myndkproj/NativeLib"); //獲取Java NativeLib類
//注冊Native方法
if (env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof((methods)[0])) < 0) {
LOGW("RegisterNatives error");
return JNI_ERR;
}
return JNI_VERSION_1_4;
}