欢迎转载,务必注明出处。http://blog.csdn.net/wang_shuai_ww/article/details/44456755
本篇是继《s5p4418 Android 4.4.2 驱动层 HAL层 服务层 应用层 开发流程记录》之后的另外一种添加JNI和服务的方法。
前面的方法是直接把HAL和服务层添加到了,Android的api中,这样的方式好处是操作系统已开发完成,剩下做APP的开发,那么我们只需要一个classes.jar文件即可使用我们自己Android系统的被隐藏的PI了(在Android官方的sdk中没有的API和用户自己添加的API),在运行自己Android系统的平台可直接方便的测试。当然也可以做出自己的android.jar文件放到sdk的platforms对应的android版本中,替换之前的android.jar,那么以后选择该目标Android版本时,加载的就是我们自己的Android系统的API了,可以像android.os.xxx这样调用我们的接口。
本篇介绍的是动态加载JNI的方式,顾名思义,就是在apk运行的时候去加载我们写好的.so JNI库。本方法灵活,开发移植使用也很方便,也不用在源码树中进入framework等目录进行各种繁琐的操作,只需要写一个满足要求的.c文件,编译生成对应的.so文件就行了,而且对于存放的位置没有什么特别要求,我一般放在对应的板级目录下(/device/nexell/realarm)。
看起来比直接添加到Android的api中要方便多了,最起码简洁多了,没那么繁琐,只是该方法在eclipse中需要多建立一个类文件,不过也没什么大不了的,O(∩_∩)O。对于想快速学习,添加自己的led操作的朋友来说,本篇的方法非常合适。
对于动态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。朋友们可以多读读,看看他们介绍的相同和不同之处。
本篇文章是在参考多篇博文之后自己编写代码在开发板上测试通过后记录写下的,大家可以参考代码写出自己的程序。然后对比与其他博文的不同之处。
我的源码目录是/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++实现,具体的控制,就是上面代码的红色部分的宏。
1.如果定义了上面的宏,那么JNI_OnLoad函数里面有用的就是最后一句话,return JNI_VERSION_1_4;返回JNI版本,并没有对LedSetState的C/C++实现函数进行注册,那么Android又怎么识别呢。如果是这种情况的话,Android会根据java层对JNI引用的类里面的native定义自动搜索对应的JNI方法,那么这就对函数定义的名字有要求了,格式为java_包名_类名_函数名字,Android的app在调用HardwareControl类里面的LedSetState方法时,就会自动匹配到对应的JNI方法。这种方法有个缺点,就是需要消耗CPU资源去匹配函数,导致运行效率不高,当程序大时就麻烦了。
2.因此推荐使用不定义上面红色的宏的方式,这样的话,在调用这个库时,就会把LedSetState函数通过(*env)->RegisterNatives这个注册,把它与Java_realarm_hardware_HardwareControl_LedSetState绑定在一起,而apk在调用LedSetState方法时执行效率就高多了,不用去自动匹配了,因为已经明白告诉操作系统该调用哪个函数了。
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 := libLedJni include $(BUILD_SHARED_LIBRARY)
好,到这里JNI库就完成了,我们使用adb push命令把它发到开发板的/system/lib目录下,在下一篇将介绍怎么使用它。