在上一篇文章《Android JNI開發(fā)入門之一》中,我介紹了Android應(yīng)用程序(APK)怎樣通過(guò)JNI調(diào)用Native C實(shí)現(xiàn)的共享庫(kù)。本文將進(jìn)一步介紹Android應(yīng)用程序通過(guò)JNI調(diào)用Native C++實(shí)現(xiàn)的共享庫(kù),并實(shí)現(xiàn)一個(gè)和上文《Android JNI開發(fā)入門之一》相同功能的Helloworld應(yīng)用程序。
兩套不同的API前文已經(jīng)提到,Android系統(tǒng)的Java虛擬機(jī)為C和C++實(shí)現(xiàn)兩套不同的API,所以我們調(diào)用的時(shí)候需要注意這一點(diǎn)兒。另外Google并沒(méi)有提供JNI的文檔,我們調(diào)用的時(shí)候可以參考Android的jni.h文件,里面有C和C++的JNI函數(shù)原型。也可以把本例的相同功能HelloWorld庫(kù)和上文《Android JNI開發(fā)入門之一》進(jìn)行比較。 C++實(shí)現(xiàn)HelloWorld共享庫(kù)在本例中Android應(yīng)用程序不需要有任何變化,我們需要重新用C++實(shí)現(xiàn)HelloWorld共享庫(kù)。創(chuàng)建com_simon_Helloworld.cpp文件,并在文件中輸入如下內(nèi)容: #include <jni.h> #define LOG_TAG "HelloWorld" #include <utils/Log.h> /* * Class: com_simon_Helloworld * Method: print * Signature: ()V */ /*JNIEXPORT void JNICALL Java_com_simon_Helloworld_print(JNIEnv *, jobject)*/ JNIEXPORT jstring JNICALL Java_com_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj) { LOGI("Hello World From libhelloworld.so!"); return env->NewStringUTF("Hello World!"); } static const char *classPathName = "com/simon/HelloWorld"; static JNINativeMethod methods[] = { {"printJNI", "()Ljava/lang/String;", (void*)Java_com_simon_HelloWorld_printJNI }, }; /* * Register several native methods for one class. */ static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { LOGE("Native registration unable to find class '%s'", className); return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { LOGE("RegisterNatives failed for '%s'", className); return JNI_FALSE; } return JNI_TRUE; } /* * Register native methods for all classes we know about. * * returns JNI_TRUE on success. */ static int registerNatives(JNIEnv* env) { if (!registerNativeMethods(env, classPathName, methods, sizeof(methods) / sizeof(methods[0]))) { return JNI_FALSE; } return JNI_TRUE; } typedef union { JNIEnv* env; void* venv; } UnionJNIEnvToVoid; /* This function will be call when the library first be loaded */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { UnionJNIEnvToVoid uenv; JNIEnv* env = NULL; LOGI("JNI_OnLoad!"); if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed"); return -1; } env = uenv.env;; if (registerNatives(env) != JNI_TRUE) { LOGE("ERROR: registerNatives failed"); return -1; } return JNI_VERSION_1_4; } 本例與上文《Android JNI開發(fā)入門之一》對(duì)比有如下幾點(diǎn)不同需要注意:
通過(guò)對(duì)比你會(huì)發(fā)現(xiàn)C++的實(shí)現(xiàn)同樣功能的共享庫(kù)比C加入更多的代碼,另外你可能會(huì)有疑問(wèn)既然Java虛擬機(jī)能用通過(guò)函數(shù)名訪問(wèn)到相應(yīng)的Native code函數(shù),為什么還要提供注冊(cè)映射函數(shù)表呢?沒(méi)錯(cuò)!作為一個(gè)HelloWorld程序,確實(shí)簡(jiǎn)單為第一要?jiǎng)?wù)!如果Java虛擬機(jī)能用函數(shù)名能訪問(wèn)到相應(yīng)的函數(shù)的話,我是不會(huì)多此一舉來(lái)注冊(cè)映射函數(shù)表。在實(shí)踐中我發(fā)現(xiàn): 標(biāo)準(zhǔn)JNI不能通過(guò)標(biāo)準(zhǔn)函數(shù)名找到C++實(shí)現(xiàn)的Helloworld共享庫(kù)中的函數(shù),但是C實(shí)現(xiàn)的helloworld共享沒(méi)有這個(gè)問(wèn)題。我不知道為什么會(huì)這樣,請(qǐng)達(dá)人指教。沒(méi)有辦法才提供注冊(cè)映射函數(shù)表。 下面提供一個(gè)helloworld共享庫(kù)的Makefile——Android.mk: LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:=com_simon_Helloworld.cpp LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) LOCAL_MODULE := libhelloworld LOCAL_SHARED_LIBRARIES := libutils LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY) 和前文《Android JNI開發(fā)入門之一》的Android.mk文件相比也就是修改了一下源文件,沒(méi)有什么可說(shuō)。編譯生成libhelloworld.so文件,允許前文HelloWorld Android應(yīng)用程序,你將會(huì)得到和前文相同的結(jié)果。 JNI的進(jìn)一步學(xué)習(xí)通過(guò)上面的例子我們已經(jīng)初步掌握了Android編寫JNI程序的方法。這也只能算是Android JNI入門而已,有很多JNI相關(guān)的內(nèi)容我們?cè)诶又胁](méi)有涉及到,比如:
如果想用JNI進(jìn)行Android應(yīng)用開發(fā)我們需要更深入的學(xué)習(xí)。為了大家共同進(jìn)步,我這里也可以提供一些相關(guān)的資料和方法。 如果我們想深入學(xué)習(xí)JNI首先要先熟悉標(biāo)準(zhǔn)的JNI。推薦大家學(xué)習(xí)《Java Native Interface: Programmer’s Guide and Specification》,權(quán)威的標(biāo)準(zhǔn)JNI學(xué)習(xí)文檔。 前文提到Google沒(méi)有Android JNI編程提供文檔,但是在網(wǎng)上Simon也找到了一篇非常好的文檔供大家參考——JNI Examples for Android。這篇文章中舉了一個(gè)例子包含了Android JNI開發(fā)的方方面面,想深入學(xué)習(xí)Android JNI開發(fā)的朋友請(qǐng)仔細(xì)研讀。 總結(jié)俗話說(shuō)得好“問(wèn)道有先后,如是而已”,Simon也是剛剛才開始學(xué)習(xí)Android JNI編程,對(duì)JNI的理解上面難免有一些地方有錯(cuò)誤,歡迎朋友們指正。 參考資料: Android JNI 使用的數(shù)據(jù)結(jié)構(gòu)JNINativeMethod詳解 How to add a new module to Android Android JNI(實(shí)現(xiàn)自己的JNI_OnLoad函數(shù)) |
|