现在在Android上的HAL开发总的来说还是随意性比较大,Android也并没有规范好一个具体的框架,下面我将根据Jollen的Mokoid工程,自己做了一些改动,分别给大家介绍一下三种实现方式。

这篇先介绍最简单的一种实现方式 - 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是如何实现的了。下一篇我们会介绍这种方法。