Android 4.4.2 动态添加JNI库方法记录 (一 JNI库层)
歡迎轉(zhuǎn)載,務(wù)必注明出處。http://blog.csdn.net/wang_shuai_ww/article/details/44456755
本篇是繼《s5p4418 Android 4.4.2 驅(qū)動層 HAL層 服務(wù)層 應(yīng)用層 開發(fā)流程記錄》之后的另外一種添加JNI和服務(wù)的方法。
前面的方法是直接把HAL和服務(wù)層添加到了,Android的api中,這樣的方式好處是操作系統(tǒng)已開發(fā)完成,剩下做APP的開發(fā),那么我們只需要一個classes.jar文件即可使用我們自己Android系統(tǒng)的被隱藏的PI了(在Android官方的sdk中沒有的API和用戶自己添加的API),在運行自己Android系統(tǒng)的平臺可直接方便的測試。當然也可以做出自己的android.jar文件放到sdk的platforms對應(yīng)的android版本中,替換之前的android.jar,那么以后選擇該目標Android版本時,加載的就是我們自己的Android系統(tǒng)的API了,可以像android.os.xxx這樣調(diào)用我們的接口。
本篇介紹的是動態(tài)加載JNI的方式,顧名思義,就是在apk運行的時候去加載我們寫好的.so JNI庫。本方法靈活,開發(fā)移植使用也很方便,也不用在源碼樹中進入framework等目錄進行各種繁瑣的操作,只需要寫一個滿足要求的.c文件,編譯生成對應(yīng)的.so文件就行了,而且對于存放的位置沒有什么特別要求,我一般放在對應(yīng)的板級目錄下(/device/nexell/realarm)。
看起來比直接添加到Android的api中要方便多了,最起碼簡潔多了,沒那么繁瑣,只是該方法在eclipse中需要多建立一個類文件,不過也沒什么大不了的,O(∩_∩)O。對于想快速學(xué)習(xí),添加自己的led操作的朋友來說,本篇的方法非常合適。
對于動態(tài)JNI,首先引用 三篇博文,地址是http://blog.sina.com.cn/s/blog_4c451e0e0101339i.html、http://www.cnblogs.com/simonshi/archive/2011/01/25/1944910.html、http://blog.csdn.net/happy08god/article/details/11405607。朋友們可以多讀讀,看看他們介紹的相同和不同之處。
本篇文章是在參考多篇博文之后自己編寫代碼在開發(fā)板上測試通過后記錄寫下的,大家可以參考代碼寫出自己的程序。然后對比與其他博文的不同之處。
我的源碼目錄是/device/nexell/realarm/led2,在該目錄下有兩個文件led2.c和Android.mk。它們的源碼如下:
led2.c:
#include <stdio.h> #include "jni.h" #include "JNIHelp.h" #include <assert.h> // 引入log頭文件 #include <android/log.h> // log標簽 #define TAG "Led_Load_JNI" // 定義info信息 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) // 定義debug信息 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) // 定義error信息 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)<span style="color:#ff0000;">#define DEVICE_NAME "/dev/real_led"#define NO_REGISTER</span><span style="color:#ff0000;">#ifndef NO_REGISTER</span> static jint Java_realarm_hardware_HardwareControl_LedSetState(JNIEnv *env, jobject thiz,jint ledNum,jint ledState) #else JNIEXPORT jint JNICALL Java_realarm_hardware_HardwareControl_LedSetState(JNIEnv *env, jobject thiz,jint ledNum,jint ledState) #endif {int fd = open(DEVICE_NAME, 0);if (fd == -1){LOGE("led open error");return 1; }if(ledState == 0)LOGD("Led close success");else if(ledState == 1)LOGD("Led open success");else {LOGD("Led ledState parameters ERROR.Only 0 or 1.");return 1;}ledState &= 0x01;ioctl(fd, ledState, 0);close(fd);return 0; } <span style="color:#ff0000;">#ifndef NO_REGISTER</span> static JNINativeMethod gMethods[] = { {"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, }; static int register_android_test_led(JNIEnv *env) { jclass clazz;static const char* const kClassName = "realarm/hardware/HardwareControl";/* look up the class */clazz = (*env)->FindClass(env, kClassName);//clazz = env->FindClass(env,kClassBoa);if (clazz == NULL) {LOGE("Can't find class %s\n", kClassName);return -1;}/* register all the methods */if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)//if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK){LOGE("Failed registering methods for %s\n", kClassName);return -1;}/* fill out the rest of the ID cache */return 0; } #endif jint JNI_OnLoad(JavaVM* vm, void* reserved) {<span style="color:#ff0000;">#ifndef NO_REGISTER</span>JNIEnv *env = NULL;if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { //if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { LOGI("Error GetEnv\n"); return -1; } assert(env != NULL); if (register_android_test_led(env) < 0) { printf("register_android_test_led error.\n"); return -1; } #endif/* success -- return valid version number */LOGI("/*****************realarm**********************/");return JNI_VERSION_1_4; }本代碼在了解其原理后做了一些處理,本代碼可以使用兩個方式來最終引用LedSetState的C/C++實現(xiàn),具體的控制,就是上面代碼的紅色部分的宏。
1.如果定義了上面的宏,那么JNI_OnLoad函數(shù)里面有用的就是最后一句話,return JNI_VERSION_1_4;返回JNI版本,并沒有對LedSetState的C/C++實現(xiàn)函數(shù)進行注冊,那么Android又怎么識別呢。如果是這種情況的話,Android會根據(jù)java層對JNI引用的類里面的native定義自動搜索對應(yīng)的JNI方法,那么這就對函數(shù)定義的名字有要求了,格式為java_包名_類名_函數(shù)名字,Android的app在調(diào)用HardwareControl類里面的LedSetState方法時,就會自動匹配到對應(yīng)的JNI方法。這種方法有個缺點,就是需要消耗CPU資源去匹配函數(shù),導(dǎo)致運行效率不高,當程序大時就麻煩了。
2.因此推薦使用不定義上面紅色的宏的方式,這樣的話,在調(diào)用這個庫時,就會把LedSetState函數(shù)通過(*env)->RegisterNatives這個注冊,把它與Java_realarm_hardware_HardwareControl_LedSetState綁定在一起,而apk在調(diào)用LedSetState方法時執(zhí)行效率就高多了,不用去自動匹配了,因為已經(jīng)明白告訴操作系統(tǒng)該調(diào)用哪個函數(shù)了。
Android.mk:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := led2.c LOCAL_SHARED_LIBRARIES := liblog LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) LOCAL_MODULE := libLedJniinclude $(BUILD_SHARED_LIBRARY)從上面代碼可知道,我編譯生成的庫名為libLedJni.so,注意LOCAL_MODULE_PATH要使用上圖所示的路徑,也就是/system/lib目錄,在動態(tài)加載時,也是去這個目錄下尋找。
好,到這里JNI庫就完成了,我們使用adb push命令把它發(fā)到開發(fā)板的/system/lib目錄下,在下一篇將介紹怎么使用它。
總結(jié)
以上是生活随笔為你收集整理的Android 4.4.2 动态添加JNI库方法记录 (一 JNI库层)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: s5p4418 Android 4.4.
- 下一篇: Android 4.4.2 动态添加JN