Android(安卓)HAL实现的三种方式(1) - 基于JNI的简单HAL设计
现在在Android上的HAL开发总的来说还是随意性比较大,Android也并没有规范好一个具体的框架,下面我将根据Jollen的Mokoid工程,自己做了一些改动,分别给大家介绍一下三种实现方式。
这篇先介绍最简单的一种实现方式 - Java应用程序直接调用JNI库。
由于JNI技术的存在,在Android中,java程序能够很好的调用C/C++库。我们这里设计一个简单的HAL,一共只有三层: HAL stub <-> JNI 库 <-> JAVA应用程序。
我们现看看HAL stub的代码:
- intled_device_close(structhw_device_t*device)
- {
- structled_control_device_t*ctx=(structled_control_device_t*)device;
- if(ctx){
- free(ctx);
- }
- return0;
- }
- intled_on(structled_control_device_t*dev,int32_tled)
- {
- LOGI("LEDStub:set%don.",led);
- return0;
- }
- intled_off(structled_control_device_t*dev,int32_tled)
- {
- LOGI("LEDStub:set%doff.",led);
- return0;
- }
- staticintled_device_open(conststructhw_module_t*module,constchar*name,
- structhw_device_t**device)
- {
- structled_control_device_t*dev;
- dev=(structled_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:
- return0;
- }
- staticstructhw_module_methods_tled_module_methods={
- open:led_device_open
- };
- conststructled_module_tHAL_MODULE_INFO_SYM={
- common:{
- tag:HARDWARE_MODULE_TAG,
- version_major:1,
- version_minor:0,
- id:LED_HARDWARE_MODULE_ID,
- name:"SampleLEDStub",
- author:"TheMokoidOpenSourceProject",
- methods:&led_module_methods,
- }
- /*supportingAPIsgohere*/
- };
我在前面关于HAL技术的文章中已经介绍了如何写HAL stub,需要注意的只有hw_module_t和hw_device_t这两个数据结构,这里就不复述了。
下面看看JNI层代码:
- structled_control_device_t*sLedDevice=NULL;
- staticjbooleanmokoid_setOn(JNIEnv*env,jobjectthiz,jintled)
- {
- LOGI("LedServiceJNI:mokoid_setOn()isinvoked.");
- if(sLedDevice==NULL){
- LOGI("LedServiceJNI:sLedDevicewasnotfetchedcorrectly.");
- return-1;
- }else{
- returnsLedDevice->set_on(sLedDevice,led);
- }
- }
- staticjbooleanmokoid_setOff(JNIEnv*env,jobjectthiz,jintled)
- {
- LOGI("LedServiceJNI:mokoid_setOff()isinvoked.");
- if(sLedDevice==NULL){
- LOGI("LedServiceJNI:sLedDevicewasnotfetchedcorrectly.");
- return-1;
- }else{
- returnsLedDevice->set_off(sLedDevice,led);
- }
- }
- /**helperAPIs*/
- staticinlineintled_control_open(conststructhw_module_t*module,
- structled_control_device_t**device){
- returnmodule->methods->open(module,
- LED_HARDWARE_MODULE_ID,(structhw_device_t**)device);
- }
- staticjbooleanmokoid_init(JNIEnv*env,jclassclazz)
- {
- led_module_t*module;
- if(hw_get_module(LED_HARDWARE_MODULE_ID,(consthw_module_t**)&module)==0){
- LOGI("LedServiceJNI:LEDStubfound.");
- if(led_control_open(&module->common,&sLedDevice)==0){
- LOGI("LedServiceJNI:GotStuboperations.");
- return0;
- }
- }
- LOGE("LedServiceJNI:GetStuboperationsfailed.");
- return-1;
- }
- staticconstJNINativeMethodgMethods[]={
- {"_init","()Z",(void*)mokoid_init},
- {"_set_on","(I)Z",(void*)mokoid_setOn},
- {"_set_off","(I)Z",(void*)mokoid_setOff},
- };
- intregister_mokoid_server_LedService(JNIEnv*env){
- staticconstchar*constkClassName=
- "com/mokoid/LedClient/LedClient";
- jclassclazz;
- /*lookuptheclass*/
- clazz=env->FindClass(kClassName);
- if(clazz==NULL){
- LOGE("Can'tfindclass%s\n",kClassName);
- return-1;
- }
- /*registerallthemethods*/
- if(env->RegisterNatives(clazz,gMethods,
- sizeof(gMethods)/sizeof(gMethods[0]))!=JNI_OK)
- {
- LOGE("Failedregisteringmethodsfor%s\n",kClassName);
- return-1;
- }
- /*fillouttherestoftheIDcache*/
- return0;
- }
- extern"C"jintJNI_OnLoad(JavaVM*vm,void*reserved)
- {
- JNIEnv*env=NULL;
- jintresult=-1;
- if(vm->GetEnv((void**)&env,JNI_VERSION_1_4)!=JNI_OK){
- LOGE("GetEnvfailed!");
- returnresult;
- }
- LOG_ASSERT(env,"Couldnotretrievetheenv!");
- register_mokoid_server_LedService(env);
- returnJNI_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程序使用。
最后是应用程序代码:
- publicclassLedClientextendsActivity{
- static{
- System.load("/system/lib/libmokoid_runtime.so");
- }
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- //CallanAPIonthelibrary.
- _init();
- _set_on(1);
- _set_off(2);
- TextViewtv=newTextView(this);
- tv.setText("LED1ison.LED2isoff.");
- setContentView(tv);
- }
- privatestaticnativeboolean_init();
- privatestaticnativeboolean_set_on(intled);
- privatestaticnativeboolean_set_off(intled);
- }
上面使用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是如何实现的了。下一篇我们会介绍这种方法。
更多相关文章
- android之如何使用Android的搜索框架
- Android(安卓)事件全局监听(二)需要root权限 ,使用getevent监听Andr
- Android(安卓)APK 签名比对,防止软件被破解使用
- Android(安卓)Studio怎样提示函数使用方法
- 史上最全的Android开发学习教程集锦【初学者】
- Android中微信主界面菜单栏的布局实现代码
- 箭头函数的基础使用
- NPM 和webpack 的基础使用
- Python list sort方法的具体使用