先介绍最简单的一种实现方式 - 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. {
3. struct led_control_device_t* ctx = (struct led_control_device_t*)device;
4. if (ctx) {
5. free(ctx);
6. }
7. return 0;
8. }
9.
10. int led_on(struct led_control_device_t *dev, int32_t led)
11. {
12. LOGI("LED Stub: set %d on.", led);
13. return 0;
14. }
15.
16. int led_off(struct led_control_device_t *dev, int32_t led)
17. {
18. LOGI("LED Stub: set %d off.", led);
19. return 0;
20. }
21.
22. static int led_device_open(const struct hw_module_t* module, const char* name,
23. struct hw_device_t** device)
24. {
25. struct led_control_device_t *dev;
26.
27. dev = (struct led_control_device_t *)malloc(sizeof(*dev));
28. memset(dev, 0, sizeof(*dev));
29.
30. dev->common.tag = HARDWARE_DEVICE_TAG;
31. dev->common.version = 0;
32. dev->common.module = module;
33. dev->common.close = led_device_close;
34.
35. dev->set_on = led_on;
36. dev->set_off = led_off;
37.
38. *device = &dev->common;
39.
40. success:
41. return 0;
42. }
43.
44. static struct hw_module_methods_t led_module_methods = {
45. open: led_device_open
46. };
47.
48. const struct led_module_t HAL_MODULE_INFO_SYM = {
49. common: {
50. tag: HARDWARE_MODULE_TAG,
51. version_major: 1,
52. version_minor: 0,
53. id: LED_HARDWARE_MODULE_ID,
54. name: "Sample LED Stub",
55. author: "The Mokoid Open Source Project",
56. methods: &led_module_methods,
57. }
58. /* supporting APIs go here */
59. };
我在前面关于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. {
5. LOGI("LedService JNI: mokoid_setOn() is invoked.");
6.
7. if (sLedDevice == NULL) {
8. LOGI("LedService JNI: sLedDevice was not fetched correctly.");
9. return -1;
10. } else {
11. return sLedDevice->set_on(sLedDevice, led);
12. }
13. }
14.
15. static jboolean mokoid_setOff(JNIEnv* env, jobject thiz, jint led)
16. {
17. LOGI("LedService JNI: mokoid_setOff() is invoked.");
18.
19.
20. if (sLedDevice == NULL) {
21. LOGI("LedService JNI: sLedDevice was not fetched correctly.");
22. return -1;
23. } else {
24. return sLedDevice->set_off(sLedDevice, led);
25. }
26. }
27.
28. /** helper APIs */
29. static inline int led_control_open(const struct hw_module_t* module,
30. struct led_control_device_t** device) {
31. return module->methods->open(module,
32. LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
33. }
34.
35. static jboolean mokoid_init(JNIEnv *env, jclass clazz)
36. {
37. led_module_t* module;
38.
39. if ( hw_get_module (LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
40. LOGI("LedService JNI: LED Stub found.");
41. if (led_control_open(&module->common, &sLedDevice) == 0) {
42. LOGI("LedService JNI: Got Stub operations.");
43. return 0;
44. }
45. }
46.
47. LOGE("LedService JNI: Get Stub operations failed.");
48. return -1;
49. }
50.
51. static const JNINativeMethod gMethods[] = {
52. { "_init", "()Z", (void *)mokoid_init},
53. { "_set_on", "(I)Z", (void *)mokoid_setOn},
54. { "_set_off", "(I)Z", (void *)mokoid_setOff},
55. };
56.
57. int register_mokoid_server_LedService(JNIEnv* env) {
58. static const char* const kClassName =
59. "com/mokoid/LedClient/LedClient"; // kclassname 指定了需要调用该Jni库的Java APP类
60. jclass clazz;
61.
62. /* look up the class */
63. clazz = env->FindClass(kClassName);
64. if (clazz == NULL) {
65. LOGE("Can't find class %s\n", kClassName);
66. return -1;
67. }
68.
69. /* register all the methods */
70. if (env->RegisterNatives(clazz, gMethods,
71. sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
72. {
73. LOGE("Failed registering methods for %s\n", kClassName);
74. return -1;
75. }
76.
77. /* fill out the rest of the ID cache */
78. return 0;
79. }
80.
81. extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
82. {
83. JNIEnv* env = NULL;
84. jint result = -1;
85.
86. if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
87. LOGE("GetEnv failed!");
88. return result;
89. }
90. LOG_ASSERT(env, "Could not retrieve the env!");
91.
92. register_mokoid_server_LedService(env);
93.
94. return JNI_VERSION_1_4;
95. }
1. 上面的Jni代码首先通过hw_get_module得到HAL stub,open以后就可以直接使用HAL stub中定义的接口。
2. 这里还需要注意JNI_OnLoad这个函数,当Jni库被App load的时候,该函数将会自动被调用,所以我们在这里实现了注册Led Service的操作,也就是说把C/C++的接口映射到Java中去,这样在Java APP中就可以使用该接口了。
3. 在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"); // 装载完该Jni库后,就可以使用映射后的接口了
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);
23. }
上面使用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是如何实现的了。下一篇我们会介绍这种方法。