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

更多相关文章

  1. Android(安卓)SDK 1.5中文版 (Application基础—1)
  2. Android入门篇二:使用意图在Activity之间传递数据
  3. Android100问之什么是Android
  4. Android异步加载全解析之Bitmap
  5. Android使用JNI实现Java与C之间传递数据
  6. Android:规范命名,让合作更加愉快
  7. 箭头函数的基础使用
  8. NPM 和webpack 的基础使用
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)DataPickerDialog组建只显
  2. Android手机上监听短信的两种方式
  3. 2014.01.21(2)——— android开发实例之vie
  4. mac系统连接Android手机
  5. 自定义Android,toast,以及多线程toast
  6. 解决Gradle minifyEnabled无法找到错误
  7. 在Android系统中调用系统前置摄像头
  8. Android的图片叠加
  9. Android(安卓)抓包的一些命令 及 adb使用
  10. Android下执行Runtime.getRuntime().exec