Android HAL開發之Java應用程序直接調用JNI庫

這篇先介紹最簡單的一種實現方式 - Java應用程序直接調用JNI庫。

由於JNI技術的存在,在Android中,java程序能夠很好的調用C/C++庫。我們這裏設計一個簡單的HAL,一共只有三層: HAL stub <-> JNI 庫 <-> JAVA應用程序。

我們現看看HAL stub的代碼:

  1. int led_device_close(struct hw_device_t* device) 
  2.     struct led_control_device_t* ctx = (struct led_control_device_t*)device; 
  3.     if (ctx) { 
  4.         free(ctx); 
  5.     } 
  6.     return 0; 
  7.  
  8. int led_on(struct led_control_device_t *dev, int32_t led) 
  9.     LOGI("LED Stub: set %d on.", led); 
  10.     return 0; 
  11.  
  12. int led_off(struct led_control_device_t *dev, int32_t led) 
  13.     LOGI("LED Stub: set %d off.", led); 
  14.     return 0; 
  15.  
  16. static int led_device_open(const struct hw_module_t* module, const char* name, 
  17.         struct hw_device_t** device)  
  18.     struct led_control_device_t *dev; 
  19.  
  20.     dev = (struct led_control_device_t *)malloc(sizeof(*dev)); 
  21.     memset(dev, 0, sizeof(*dev)); 
  22.  
  23.     dev->common.tag =  HARDWARE_DEVICE_TAG; 
  24.     dev->common.version = 0; 
  25.     dev->common.module = module; 
  26.     dev->common.close = led_device_close; 
  27.  
  28.     dev->set_on = led_on; 
  29.     dev->set_off = led_off; 
  30.  
  31.     *device = &dev->common; 
  32.  
  33. success: 
  34.     return 0; 
  35.  
  36. static struct hw_module_methods_t led_module_methods = { 
  37.     open: led_device_open 
  38. }; 
  39.  
  40. const struct led_module_t HAL_MODULE_INFO_SYM = { 
  41.     common: { 
  42.         tag: HARDWARE_MODULE_TAG, 
  43.         version_major: 1, 
  44.         version_minor: 0, 
  45.         id: LED_HARDWARE_MODULE_ID, 
  46.         name: "Sample LED Stub"
  47.         author: "The Mokoid Open Source Project"
  48.         methods: &led_module_methods, 
  49.     } 
  50.     /* supporting APIs go here */ 
  51. }; 

我在前面關於HAL技術的文章中已經介紹瞭如何寫HAL stub,需要注意的只有hw_module_t和hw_device_t這兩個數據結構,這裏就不復述了。

下面看看JNI層代碼:

  1. struct led_control_device_t *sLedDevice = NULL; 
  2.  
  3. static jboolean mokoid_setOn(JNIEnv* env, jobject thiz, jint led)  
  4.     LOGI("LedService JNI: mokoid_setOn() is invoked."); 
  5.  
  6.     if (sLedDevice == NULL) { 
  7.         LOGI("LedService JNI: sLedDevice was not fetched correctly."); 
  8.         return -1; 
  9.     } else { 
  10.         return sLedDevice->set_on(sLedDevice, led); 
  11.     } 
  12.  
  13. static jboolean mokoid_setOff(JNIEnv* env, jobject thiz, jint led)  
  14.     LOGI("LedService JNI: mokoid_setOff() is invoked."); 
  15.  
  16.  
  17.     if (sLedDevice == NULL) { 
  18.         LOGI("LedService JNI: sLedDevice was not fetched correctly."); 
  19.         return -1; 
  20.     } else { 
  21.         return sLedDevice->set_off(sLedDevice, led); 
  22.     } 
  23.  
  24. /** helper APIs */ 
  25. static inline int led_control_open(const struct hw_module_t* module, 
  26.         struct led_control_device_t** device) { 
  27.     return module->methods->open(module, 
  28.             LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device); 
  29.  
  30. static jboolean mokoid_init(JNIEnv *env, jclass clazz) 
  31.     led_module_t* module; 
  32.  
  33.     if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) { 
  34.         LOGI("LedService JNI: LED Stub found."); 
  35.         if (led_control_open(&module->common, &sLedDevice) == 0) { 
  36.             LOGI("LedService JNI: Got Stub operations."); 
  37.             return 0; 
  38.         } 
  39.     } 
  40.  
  41.     LOGE("LedService JNI: Get Stub operations failed."); 
  42.     return -1; 
  43.  
  44. static const JNINativeMethod gMethods[] = { 
  45.     { "_init",      "()Z",  (void *)mokoid_init }, 
  46.     { "_set_on",        "(I)Z", (void *)mokoid_setOn }, 
  47.     { "_set_off",       "(I)Z", (void *)mokoid_setOff }, 
  48. }; 
  49.  
  50. int register_mokoid_server_LedService(JNIEnv* env) { 
  51.     static const charconst kClassName = 
  52.         "com/mokoid/LedClient/LedClient"
  53.     jclass clazz; 
  54.  
  55.     /* look up the class */ 
  56.     clazz = env->FindClass(kClassName); 
  57.     if (clazz == NULL) { 
  58.         LOGE("Can't find class %s\n", kClassName); 
  59.         return -1; 
  60.     } 
  61.  
  62.     /* register all the methods */ 
  63.     if (env->RegisterNatives(clazz, gMethods, 
  64.             sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) 
  65.     { 
  66.         LOGE("Failed registering methods for %s\n", kClassName); 
  67.         return -1; 
  68.     } 
  69.  
  70.     /* fill out the rest of the ID cache */ 
  71.     return 0; 
  72.  
  73. extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) 
  74.     JNIEnv* env = NULL; 
  75.     jint result = -1; 
  76.  
  77.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 
  78.         LOGE("GetEnv failed!"); 
  79.         return result; 
  80.     } 
  81.     LOG_ASSERT(env, "Could not retrieve the env!"); 
  82.  
  83.     register_mokoid_server_LedService(env); 
  84.  
  85.     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程序使用。

最後是應用程序代碼:

  1. public class LedClient extends Activity { 
  2.  
  3.     static { 
  4.         System.load("/system/lib/libmokoid_runtime.so"); 
  5.     } 
  6.  
  7.     @Override 
  8.     public void onCreate(Bundle savedInstanceState) { 
  9.         super.onCreate(savedInstanceState); 
  10.  
  11.         // Call an API on the library. 
  12.         _init(); 
  13.         _set_on(1); 
  14.         _set_off(2); 
  15.          
  16.         TextView tv = new TextView(this); 
  17.         tv.setText("LED 1 is on. LED 2 is off."); 
  18.         setContentView(tv); 
  19.     } 
  20.     private static native boolean _init(); 
  21.     private static native boolean _set_on(int led); 
  22.     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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章