這篇先介紹最簡單的一種實現方式 - Java應用程序直接調用JNI庫。
由於JNI技術的存在,在Android中,java程序能夠很好的調用C/C++庫。我們這裏設計一個簡單的HAL,一共只有三層: HAL stub <-> JNI 庫 <-> JAVA應用程序。
我們現看看HAL stub的代碼:
- int led_device_close(struct hw_device_t* device)
- {
- struct led_control_device_t* ctx = (struct led_control_device_t*)device;
- if (ctx) {
- free(ctx);
- }
- return 0;
- }
- int led_on(struct led_control_device_t *dev, int32_t led)
- {
- LOGI("LED Stub: set %d on.", led);
- return 0;
- }
- int led_off(struct led_control_device_t *dev, int32_t led)
- {
- LOGI("LED Stub: set %d off.", led);
- return 0;
- }
- static int led_device_open(const struct hw_module_t* module, const char* name,
- struct hw_device_t** device)
- {
- struct led_control_device_t *dev;
- dev = (struct led_control_device_t *)malloc(sizeof(*dev));
- memset(dev, 0, sizeof(*dev));
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = module;
- dev->common.close = led_device_close;
- dev->set_on = led_on;
- dev->set_off = led_off;
- *device = &dev->common;
- success:
- return 0;
- }
- static struct hw_module_methods_t led_module_methods = {
- open: led_device_open
- };
- const struct led_module_t HAL_MODULE_INFO_SYM = {
- common: {
- tag: HARDWARE_MODULE_TAG,
- version_major: 1,
- version_minor: 0,
- id: LED_HARDWARE_MODULE_ID,
- name: "Sample LED Stub",
- author: "The Mokoid Open Source Project",
- methods: &led_module_methods,
- }
- /* supporting APIs go here */
- };
我在前面關於HAL技術的文章中已經介紹瞭如何寫HAL stub,需要注意的只有hw_module_t和hw_device_t這兩個數據結構,這裏就不復述了。
下面看看JNI層代碼:
- struct led_control_device_t *sLedDevice = NULL;
- static jboolean mokoid_setOn(JNIEnv* env, jobject thiz, jint led)
- {
- LOGI("LedService JNI: mokoid_setOn() is invoked.");
- if (sLedDevice == NULL) {
- LOGI("LedService JNI: sLedDevice was not fetched correctly.");
- return -1;
- } else {
- return sLedDevice->set_on(sLedDevice, led);
- }
- }
- static jboolean mokoid_setOff(JNIEnv* env, jobject thiz, jint led)
- {
- LOGI("LedService JNI: mokoid_setOff() is invoked.");
- if (sLedDevice == NULL) {
- LOGI("LedService JNI: sLedDevice was not fetched correctly.");
- return -1;
- } else {
- return sLedDevice->set_off(sLedDevice, led);
- }
- }
- /** helper APIs */
- static inline int led_control_open(const struct hw_module_t* module,
- struct led_control_device_t** device) {
- return module->methods->open(module,
- LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
- }
- static jboolean mokoid_init(JNIEnv *env, jclass clazz)
- {
- led_module_t* module;
- if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
- LOGI("LedService JNI: LED Stub found.");
- if (led_control_open(&module->common, &sLedDevice) == 0) {
- LOGI("LedService JNI: Got Stub operations.");
- return 0;
- }
- }
- LOGE("LedService JNI: Get Stub operations failed.");
- return -1;
- }
- static const JNINativeMethod gMethods[] = {
- { "_init", "()Z", (void *)mokoid_init },
- { "_set_on", "(I)Z", (void *)mokoid_setOn },
- { "_set_off", "(I)Z", (void *)mokoid_setOff },
- };
- int register_mokoid_server_LedService(JNIEnv* env) {
- static const char* const kClassName =
- "com/mokoid/LedClient/LedClient";
- jclass clazz;
- /* look up the class */
- clazz = env->FindClass(kClassName);
- if (clazz == NULL) {
- LOGE("Can't find class %s\n", kClassName);
- return -1;
- }
- /* register all the methods */
- if (env->RegisterNatives(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;
- }
- extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
- {
- JNIEnv* env = NULL;
- jint result = -1;
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- LOGE("GetEnv failed!");
- return result;
- }
- LOG_ASSERT(env, "Could not retrieve the env!");
- register_mokoid_server_LedService(env);
- return JNI_VERSION_1_4;
- }
上面的Jni代碼首先通過hw_get_module得到HAL stub,open以後就可以直接使用HAL stub中定義的接口。這裏還需要注意JNI_OnLoad這個函數,當Jni庫被App load的時候,該函數將會自動被調用,所以我們在這裏實現了註冊Led Service的操作,也就是說把C/C++的接口映射到Java中去,這樣在Java APP中就可以使用該接口了。在register_mokoid_server_LedService中,我們需要注意kclassname指定了需要調用該Jni庫的Java APP類 - com.mokoid.LedClient.LedClient,也就是說該Jni庫只能提供給該Java程序使用。
最後是應用程序代碼:
- public class LedClient extends Activity {
- static {
- System.load("/system/lib/libmokoid_runtime.so");
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Call an API on the library.
- _init();
- _set_on(1);
- _set_off(2);
- TextView tv = new TextView(this);
- tv.setText("LED 1 is on. LED 2 is off.");
- setContentView(tv);
- }
- private static native boolean _init();
- private static native boolean _set_on(int led);
- private static native boolean _set_off(int led);
- }
上面使用System.load來裝載Jni庫,當然我們也可以使用System.loadLibrary來裝載,他們的唯一區別就是前者需要指定完整的路徑,後者會在系統路徑上(比如/system/lib/) 查找庫。裝載完該Jni庫後,就可以使用映射後的接口了(_init, _set_on, _set_off)。
上面這種HAL的實現方式比較簡單,但是也存在一個很大的問題,就是Jni庫只能提供給某一個特定的Java使用,如何克服這個問題?我們可以在APP和Jni之間加一層Java service,該Jni提供給Java service使用,而所有的APP利用該service來使用Jni提供的接口。這樣的話,在應用程序層,就不需要關心Jni是如何實現的了。下一篇我們會介紹這種方法。
本文出自 “Mobile and Linux Deve..” 博客,請務必保留此出處http://buaadallas.blog.51cto.com/399160/384622