设为首页 收藏本站
查看: 1889|回复: 0

[Cloudstack] led HAL简单案例分析

[复制链接]
发表于 2015-10-14 08:03:40 | 显示全部楼层 |阅读模式





  • DSC0000.jpg

mr_raptor的专栏
专注Android系统,移动平台研究,ARM BSP开发,著有《深入浅出嵌入式底层软件开发》北航出版社

  • DSC0001.gif 目录视图
  • DSC0002.gif 摘要视图
  • DSC0003.gif 订阅
CSDN Android客户端 下载就送50C币     又见人月神话     最流行的语言想学就学     写博文,传代码,送C币     深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
分类: Android移植2012-10-1717:35 9872人阅读 评论(18) 收藏 举报androidjavajnimodule平台
目录(?)[+]



通过前两节HAL框架分析和JNI概述,我们对Android提供的Stub HAL有了比较详细的了解了,下面我们来看下led的实例,写驱动点亮led灯,就如同写程序,学语言打印HelloWorld一样,如果说打印HelloWorld是一门新语言使用的第一声吆喝,那么点亮led灯就是我们学习HAL的一座灯塔,指挥我们在后面的复杂的HAL代码里准确找到方向。


LedHAL实例架构
DSC0004.png



上图描述了我们Led实例的框架层次:

l  LedDemo.java:是我们写的Android应用程序

l  LedService.java:是根据Led HAL封装的Java框架层的API,主要用于向应用层提供框架层API,它属于Android的框架层

l  libled_runtime.so:由于Java代码不能访问HAL层,该库是LedService.java对应的本地代码部分

l  led.default.so:针对led硬件的HAL代码

LedDemo通过LedService提供的框架层API访问Led设备,LedService对于LedDemo应用程序而言是Led设备的服务提供者,LedService运行在Dalvik中没有办法直接访问Led硬件设备,它只能将具体的Led操作交给本地代码来实现,通过JNI来调用Led硬件操作的封装库libled_runtime.so,由HAL Stub框架可知,在libled_runtime.so中首先查找注册为led的硬件设备module,找到之后保存其操作接口指针在本地库中等待框架层LedService调用。led.default.so是HAL层代码,它是上层操作的具体实施者,它并不是一个动态库(也就是说它并没有被任何进程加载并链接),它只是在本地代码查找硬件设备module时通过ldopen”杀鸡取卵”找module,返回该硬件module对应的device操作结构体中封装的函数指针。

其调用时序如下:

DSC0005.jpg

Led HAL实例代码分析


我们来看下led实例的目录结构:

DSC0006.png


主要文件如下:

com.hello.LedService.cpp:它在frameworks/services/jni目录下,是的Led本地服务代码

led.c:HAL代码

led.h:HAL代码头文件

LedDemo.java:应用程序代码

LedService.java:Led框架层服务代码

在Android的源码目录下,框架层服务代码应该放在frameworks/services/java/包名/目录下,由Android的编译系统统一编译生成system/framework/services.jar文件,由于我们的测试代码属于厂商定制代码,尽量不要放到frameworks的源码树里,我将其和LedDemo应用程序放在一起了,虽然这种方式从Android框架层次上不标准。

另外,本地服务代码的文件名要和对应的框架层Java代码的名字匹配(包名+类文件名,包目录用“_“代替)。有源码目录里都有对应的一个Android.mk文件,它是Android编译系统的指导文件,用来编译目标module。

1)        Android.mk文件分析

先来看下led源码中①号Android.mk:

[plain] viewplaincopy DSC0007.png DSC0008.png

  • include $(call all-subdir-makefiles)  
代码很简单,表示包含当前目录下所有的Android.mk文件

先来看下led_app目录下的③号Android.mk:

[plain] viewplaincopy

  • # 调用宏my-dir,这个宏返回当前Android.mk文件所在的路径  
  • LOCAL_PATH:= $(call my-dir)                                       
  •   
  • # 包含CLEAR_VARS变量指向的mk文件build/core/clear_vars.mk,它主要用来清除编译时依赖的编译变量  
  • include $(CLEAR_VARS)                                      
  •   
  • # 指定当前目标的TAG标签,关于其作用见前面Android编译系统章节  
  • LOCAL_MODULE_TAGS := user  
  •   
  • # 当前mk文件的编译目标模块  
  • LOCAL_PACKAGE_NAME := LedDemo  
  •   
  • # 编译目标时依赖的源码,它调用了一个宏all-java-files-under,该宏在build/core/definitions.mk中定义  
  • # 表示在当前目录下查找所有的java文件,将查找到的java文件返回  
  • LOCAL_SRC_FILES := $(callall-java-files-under, src)  
  •   
  • # 在编译Android应用程序时都要指定API level,也就是当前程序的编译平台版本  
  • # 这里表示使用当前源码的版本  
  • LOCAL_SDK_VERSION := current  
  •   
  • # 最重要的就是这句代码,它包含了一个文件build/core/package.mk,根据前面设置的编译变量,编译生成Android包文件,即:apk文件  
  • include $(BUILD_PACKAGE)  
上述代码中都加了注释,基本上每一个编译目标都有类似上述的编译变量的声明:

LOCAL_MODULE_TAGS

LOCAL_PACKAGE_NAME

LOCAL_SRC_FILES

由于所有的Android.mk最终被编译系统包含,所以在编译每个目标模块时,都要通过LOCAL_PATH:= $(call my-dir)指定当前目标的目录,然后调用include $(CLEAR_VARS)先清除编译系统依赖的重要的编译变量,再生成新的编译变量。

让我们来看看LedDemo目标对应的源码吧。

2)        LedDemo代码分析

学习过Android应用的同学对其目录结构很熟悉,LedDemo的源码在src目录下。

@ led_app/src/com/farsight/LedDemo.java:

[java] viewplaincopy

  • package com.hello;  
  •   
  • import com.hello.LedService;  
  •   
  • import com.hello.R;  
  •   
  • importandroid.app.Activity;  
  •   
  • importandroid.os.Bundle;  
  •   
  • importandroid.util.Log;  
  •   
  • importandroid.view.View;  
  •   
  • import android.view.View.OnClickListener;  
  •   
  • importandroid.widget.Button;  
  •   
  •    
  •   
  • public classLedDemo extends Activity {  
  •      privateLedService led_svc;  
  •      private Buttonbtn;  
  •      private booleaniflag = false;  
  •      private Stringtitle;  
  •   
  •       /** Calledwhen the activity is first created. */  
  •      @Override  
  •      public void onCreate(Bundle savedInstanceState) {  
  •         super.onCreate(savedInstanceState);  
  •         setContentView(R.layout.main);  
  •   
  •         Log.i("Java App", "OnCreate");  
  •          led_svc =new LedService();  
  •          btn =(Button) this.findViewById(R.id.Button01);  
  •         this.btn.setOnClickListener(new OnClickListener() {  
  •             public void onClick(View v) {  
  •                 Log.i("Java App", "btnOnClicked");  
  •                 if (iflag) {  
  •                     title = led_svc.set_off();  
  •                     btn.setText("Turn On");  
  •                     setTitle(title);  
  •                     iflag = false;  
  •                 } else {  
  •                     title = led_svc.set_on();  
  •                     btn.setText("Turn Off");  
  •                     setTitle(title);  
  •                     iflag = true;  
  •                 }  
  •              }  
  •          });  
  •      }  
  • }  
代码很简单,Activity上有一个按钮,当Activity初始化时创建LedService对象,按钮按下时通过LedService对象调用其方法set_on()和set_off()。


3)        LedService代码分析

我们来看下LedService的代码:

@led_app/src/com/farsight/LedService.java:

[java] viewplaincopy

  • package com.hello;  
  • import android.util.Log;  
  •   
  • public class LedService {  
  •   
  •     /*
  •      * loadnative service.
  •      */  
  •     static {         // 静态初始化语言块,仅在类被加载时被执行一次,通常用来加载库  
  •         Log.i ("Java Service" , "Load Native Serivce LIB" );  
  •        System.loadLibrary ( "led_runtime" );  
  •     }  
  •   
  •     // 构造方法  
  •     public LedService() {        
  •         int icount ;  
  •   
  •         Log.i ("Java Service" , "do init Native Call" );  
  •         _init ();           
  •         icount =_get_count ();  
  •         Log.d ("Java Service" , "led count = " + icount );  
  •         Log.d ("Java Service" , "Init OK " );  
  •     }  
  •   
  •     /*
  •      * LED nativemethods.
  •      */  
  •     public Stringset_on() {  
  •         Log.i ("com.hello.LedService" , "LED On" );  
  •         _set_on();  
  •         return"led on" ;  
  •      }  
  •   
  •      public String set_off() {  
  •          Log.i ("com.hello.LedService" , "LED Off" );  
  •          _set_off();  
  •          return"led off" ;  
  •      }  
  •   
  •      /*
  •      * declare all the native interface.
  •      */  
  •      private static native boolean _init();  
  •      private static native int _set_on();  
  •      private static native int _set_off();  
  •      private static native int _get_count();  
  •   
  •   }  
通过分析上面代码可知LedService的工作:

l   加载本地服务的库代码

l   在构造方法里调用_init本地代码,对Led进行初始化,并调用get_count得到Led灯的个数

l   为LedDemo应用程序提供两个API:set_on和set_off,这两个API方法实际上也是交给了本地服务代码来操作的

由于Java代码无法直接操作底层硬件,通过JNI方法将具体的操作交给本地底层代码实现,自己只是一个API Provider,即:服务提供者。

让我们来到底层本地代码,先看下底层代码的Android.mk文件:

@ frameworks/Android.mk:

[plain] viewplaincopy

  • LOCAL_PATH:= $(call my-dir)  
  • include $(CLEAR_VARS)  
  •   
  • LOCAL_MODULE_TAGS := eng  
  • LOCAL_MODULE:= libled_runtime                    # 编译目标模块  
  • LOCAL_SRC_FILES:= \  
  •        services/jni/com_farsight_LedService.cpp  
  •   
  •    
  • LOCAL_SHARED_LIBRARIES := \                       # 编译时依赖的动态库  
  •        libandroid_runtime  \  
  •        libnativehelper    \  
  •         libcutils         \  
  •         libutils          \  
  •        libhardware  
  •   
  • LOCAL_C_INCLUDES += \                                  #编译时用到的头文件目录  
  •        $(JNI_H_INCLUDE)  
  •   
  • LOCAL_PRELINK_MODULE := false                            # 本目标为非预链接模块  
  • include $(BUILD_SHARED_LIBRARY)                # 编译生成共享动态库  
结合前面分析的Android.mk不难看懂这个mk文件。之前的mk文件是编译成Android apk文件,这儿编译成so共享库,所以LOCAL_MODULE和include $(BUILD_SHARED_LIBRARY)与前面mk文件不同,关于Android.mk文件里的变量作用,请查看Android编译系统章节。

总而言之,本地代码编译生成的目标是libled_runtime.so文件。


4)        Led本地服务代码分析

我们来看下本地服务的源码:

@ frameworks/services/jni/com_farsight_LedService.cpp:

[cpp] viewplaincopy

  • #define LOG_TAG "LedService"  
  • #include "utils/Log.h"  
  • #include <stdlib.h>  
  • #include <string.h>  
  • #include <unistd.h>  
  • #include <assert.h>  
  • #include <jni.h>  
  • #include &quot;../../../hardware/led.h&quot;  
  •   
  • static led_control_device_t *sLedDevice = 0;  
  • static led_module_t* sLedModule=0;   
  •   
  • static jint get_count(void)  
  • {  
  •     LOGI(&quot;%sE&quot;, __func__);  
  •    if(sLedDevice)  
  •         returnsLedDevice->get_led_count(sLedDevice);  
  •     else  
  •        LOGI(&quot;sLedDevice is null&quot;);  
  •     return 0;  
  • }  
  •   
  •   
  • static jint led_setOn(JNIEnv* env, jobject thiz) {  
  •     LOGI(&quot;%sE&quot;, __func__);  
  •     if(sLedDevice) {  
  •        sLedDevice->set_on(sLedDevice);  
  •     }else{  
  •        LOGI(&quot;sLedDevice is null&quot;);  
  •     }  
  •     return 0;  
  • }   
  •   
  • static jint led_setOff(JNIEnv* env, jobject thiz) {  
  •     LOGI(&quot;%s E&quot;, __func__);  
  •      if(sLedDevice) {  
  •         sLedDevice->set_off(sLedDevice);  
  •      }else{  
  •          LOGI(&quot;sLedDevice is null&quot;);  
  •      }  
  •      return 0;  
  • }  
  •   
  • static inline int led_control_open(const structhw_module_t* module,  
  •      structled_control_device_t** device) {  
  •     LOGI(&quot;%s E &quot;, __func__);  
  •      returnmodule->methods->open(module,  
  •         LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
  • }  
  •   
  • static jint led_init(JNIEnv *env, jclass clazz)  
  • {  
  •     led_module_tconst * module;  
  •     LOGI(&quot;%s E &quot;, __func__);  
  •      if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0){  
  •         LOGI(&quot;get Module OK&quot;);  
  •         sLedModule = (led_module_t *) module;  
  •         if(led_control_open(&module->common, &sLedDevice) != 0) {  
  •            LOGI(&quot;led_init error&quot;);  
  •            return-1;  
  •         }  
  •     }   
  •   
  •     LOGI(&quot;led_init success&quot;);  
  •     return 0;  
  • }  
  •   
  •    
  •   
  • /*
  •   *
  •   * Array ofmethods.
  •   * Each entryhas three fields: the name of the method, the method
  •   * signature,and a pointer to the native implementation.
  •   */  
  • static const JNINativeMethod gMethods[] = {  
  •     {&quot;_init&quot;,    &quot;()Z&quot;,(void*)led_init},  
  •     {&quot;_set_on&quot;,   &quot;()I&quot;,(void*)led_setOn },  
  •     {&quot;_set_off&quot;, &quot;()I&quot;,(void*)led_setOff },  
  •     {&quot;_get_count&quot;, &quot;()I&quot;,(void*)get_count },  
  • };  
  •   
  •   static int registerMethods(JNIEnv* env) {  
  •      static constchar* const kClassName = &quot;com/hello/LedService&quot;;  
  •      jclass clazz;  
  •      /* look upthe class */  
  •      clazz =env->FindClass(kClassName);  
  •      if (clazz ==NULL) {  
  •         LOGE(&quot;Can't find class %s\n&quot;, kClassName);  
  •          return-1;  
  •      }   
  •   
  •      /* registerall the methods */  
  •      if(env->RegisterNatives(clazz, gMethods,  
  •             sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)  
  •      {  
  •         LOGE(&quot;Failed registering methods for %s\n&quot;, kClassName);  
  •          return -1;  
  •      }  
  •      /* fill outthe rest of the ID cache */  
  •      return 0;  
  • }  
  •   
  •    
  •   
  • /*
  •   * This iscalled by the VM when the shared library is first loaded.
  •   */  
  • jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
  •      JNIEnv* env= NULL;  
  •      jint result= -1;  
  •     LOGI(&quot;JNI_OnLoad&quot;);  
  •      if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  •         LOGE(&quot;ERROR: GetEnv failed\n&quot;);  
  •          gotofail;  
  •      }  
  •   
  •      assert(env!= NULL);  
  •      if(registerMethods(env) != 0) {  
  •          LOGE(&quot;ERROR: PlatformLibrary nativeregistration failed\n&quot;);  
  •          gotofail;  
  •      }  
  •      /* success-- return valid version number */  
  •      result =JNI_VERSION_1_4;   
  •   
  • fail:  
  •      return result;  
  • }  
         这儿的代码不太容易读,因为里面是JNI的类型和JNI特性的代码,看代码先找入口。LedService.java框架代码一加载就调用静态初始化语句块里的System.loadLibrary ( &quot;led_runtime&quot; ),加载libled_runtime.so,该库刚好是前面Android.mk文件的目标文件,也就是说LedService加载的库就是由上面的本地代码生成的。当一个动态库被Dalvik加载时,首先在Dalvik会回调该库代码里的JNI_OnLoad函数。也就是说JNI_OnLoad就是本地服务代码的入口函数。

         JNI_OnLoad的代码一般来说是死的,使用的时候直接拷贝过来即可,vm->GetEnv会返回JNIEnv指针,而这个指针其实就是Java虚拟机的环境变量,我们可以通过该指针去调用JNI提供的方法,如FindClass等,调用registerMethods方法,在方法里通过JNIEnv的FindClass查找LedService类的引用,然后在该类中注册本地方法与Java方法的映射关系,上层Java代码可以通过这个映射关系调用到本地代码的实现。RegisterNatives方法接收三个参数:

l  第一个参数jclass:要注册哪个类里的本地方法映射关系

l  第二个参数JNINativeMethod*:这是一个本地方法与Java方法映射数组,JNINativeMethod是个结构体,每个元素是一个Java方法到本地方法的映射。

[cpp] viewplaincopy

  • typedef struct {  
  •          constchar* name;  
  •          constchar* signature;  
  •          void*fnPtr;  
  • } JNINativeMethod;  
         name:表示Java方法名

         signature:表示方法的签名

         fnPtr:Java方法对应的本地方法指针

l  第三个参数size:映射关系个数

由代码可知,Java方法与本地方法的映射关系如下:

Java方法

本地方法

void _init()

jint led_init(JNIEnv *env, jclass clazz)

int _set_on()

jint led_setOn(JNIEnv* env, jobject thiz)

int _set_off()

jint led_setOff(JNIEnv* env, jobject thiz)

int _get_count()

jint get_count(void)

通过上表可知,本地方法参数中默认会有两个参数:JNIEnv* env, jobject thiz,分别表示JNI环境和调用当前方法的对象引用,当然你也可以不设置这两个参数,在这种情况下你就不能访问Java环境中的成员。本地方法与Java方法的签名必须一致,返回&#20540;不一致不会造成错误。

现在我们再来回顾下我们的调用调用流程:

l  LedDemo创建了LedService对象

l  LedService类加载时加载了对应的本地服务库,在本地服务库里Dalvik自动调用JNI_OnLoad函数,注册Java方法和本地方法映射关系。

根据Java语言特点,当LedDemo对象创建时会调用其构造方法LedService()。

[cpp] viewplaincopy

  • // 构造方法  
  •     public LedService() {        
  •         int icount ;  
  •         Log.i (&quot;Java Service&quot; , &quot;do init Native Call&quot; );  
  •         _init ();           
  •         icount =_get_count ();  
  •         Log.d (&quot;Java Service&quot; , &quot;led count = &quot; &#43; icount );  
  •         Log.d (&quot;Java Service&quot; , &quot;Init OK &quot; );  
  •     }  
在LedService构造方法里直接调用了本地方法_init和_get_count(通过native保留字声明),也就是说调用了本地服务代码里的jint led_init(JNIEnv *env, jclass clazz)和jintget_count(void)。

在led_init方法里的内容就是我们前面分析HAL框架代码的使用规则了。

l  通过hw_get_module方法查到到注册为LED_HARDWARE_MODULE_ID,即:”led”的module模块。

l  通过与led_module关联的open函数指针打开led设备,返回其device_t结构体,保存在本地代码中,有的朋友可能会问,不是本地方法不能持续保存一个引用吗?由于device_t结构是在open设备时通过malloc分配的,只要当前进程不死,该指针一直可用,在这儿本地代码并没有保存Dalvik里的引用,保存的是mallco的分配空间地址,但是在关闭设备时记得要将该地址空间free了,否则就内存泄漏了。

l  拿到了led设备的device_t结构之后,当LedDemo上的按钮按下时调用LedService对象的set_on和set_off方法,这两个LedService方法直接调用了本地服务代码的对应映射方法,本地方法直接调用使用device_t指向的函数来间接调用驱动操作代码。

好吧,让我们再来看一个详细的时序图:
DSC0009.png


不用多解释了。

最后一个文件,HAL对应的Android.mk文件:

@ hardware/Android.mk:

[plain] viewplaincopy

  • LOCAL_PATH := $(call my-dir)  
  • include $(CLEAR_VARS)  
  •   
  • LOCAL_C_INCLUDES &#43;= \  
  •          include/  
  •   
  • LOCAL_PRELINK_MODULE := false  
  • LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw  
  • LOCAL_SHARED_LIBRARIES := liblog  
  • LOCAL_SRC_FILES := led.c  
  • LOCAL_MODULE := led.default  
  • include $(BUILD_SHARED_LIBRARY)  
注:LOCAL_PRELINK_MODULE:= false要加上,否则编译出错

指定目标名为:led.default

目标输入目录LOCAL_MODULE_PATH为:/system/lib/hw/,不指定会默认输出到/system/lib目录下。

根据前面HAL框架分析可知,HAL Stub库默认加载地址为:/vendor/lib/hw/或/system/lib/hw/,在这两个目录查找:硬件id名.default.so,所以我们这儿指定了HAL Stub的编译目标名为led.default,编译成动态库,输出目录为:$(TARGET_OUT_SHARED_LIBRARIES)/hw,TARGET_OUT_SHARED_LIBRARIES指/system/lib/目录。
5) 深入理解
我们从进程空间的概念来分析下我们上面写的代码。
DSC00010.jpg

我们前面的示例代码中,将LedDemo.java和LedService.java都放在了一个APK文件里,这也就意味着这个应用程序编译完之后,它会运行在一个Dalvik虚拟机实例中,即:一个进程里,在LedService.java中加载了libled_runtime.so库,通过JNI调用了本地代码,根据动态库的运行原理,我们知道,libled_runtime.so在第一次引用时会被加载到内存中并映射到引用库的进程空间中,我们可以简单理解为引用库的程序和被引用的库在一个进程中,而在libled_runtime.so库中,又通过dlopen打开了库文件led.default.so(该库并没有被库加载器加载,而是被当成一个文件打开的),同样我们可以理解为led.default.so和libled_runtime.so在同一个进程中。
由此可见,上面示例的Led HAL代码全部都在一个进程中实现,在该示例中的LedService功能比较多余,基本上不能算是一个服务。如果LedDemo运行在两个进程中,就意味着两个进程里的LedService不能复用,通常我们所谓的Service服务一般向客户端提供服务并且同时可以为多个客户端服务(如下图),所以我们的示例Led HAL代码不是完美的HAL模型,我们后面章节会再实现一个比较完美的HAL架构。
DSC00011.jpg










  • 上一篇深入浅出- Android系统移植与平台开发(九)- JNI介绍
  • 下一篇深入浅出- Android系统移植与平台开发(十一) - Sensor HAL框架分析之一
顶23踩2主题推荐移植设计androidandroid应用应用程序猜你在找TP调试高通Android平台硬件调试之Camera篇queue_delayed_work和queue_work区别v4l2 编程接口二 driver实现uboot 命令自动补全 及 修正设置环境变量时自启动内核iOS开发Swift语言学习教程Java基础核心技术:多线程(day16-day17)Android入门实战教程Java基础核心技术:IO(day15-day16)Linux企业常用文件管理命令详解准备好了么? 跳吧             !更多职位尽在 CSDNJOB【精准广告投放】C/C&#43;&#43;研发工程师/数据挖掘工程师百度在线网络技术(北京)有限公司|18-35K/月我要跳槽C/C&#43;&#43;/C#软件工程师利博国际科技发展有限公司|10-20K/月我要跳槽C/C&#43;&#43;软件开发工程师合肥赛猊腾龙信息技术有限公司|10-20K/月我要跳槽C/C&#43;&#43;开发上海云盾信息技术有限公司|20-25K/月我要跳槽查看评论10楼 心鑫 2014-05-14 17:01发表 [回复] DSC00012.jpg 请问:我把mokoid的源码放在device目录下,编译有问题,修改下编译没问题,在/build/target/product/generic_no_telephony.mk下添加了 LedTest \
LedClient \
在out/target/product/generic/system/app目录下也有对应的apk,可为什么在虚拟机里就是看不到apk呢?
你的串口的例子就没问题,实在找不出两都的差别了Re: mr_raptor 2014-05-17 19:14发表 [回复] DSC00013.jpg 回复proud2005:你看看你的Logcat,你的这个问题我遇到过,不是因为API level不对,就是APk安装出了问题。9楼 南枫co 2014-05-09 21:55发表 [回复] DSC00014.jpg 请问,你这个工程怎么编译的呢?有c,cpp,javaRe: mr_raptor 2014-05-12 10:57发表 [回复]回复far5811:通过编写Android.mk文件来实现编译,Android的编译系统会自动完成编译。详细请看Android编译系统一、二、三文章。Re: 南枫co 2014-05-24 21:10发表 [回复]回复mr_raptor:嗯,我还有个问题请教下:
假设系统开发和应用开不是同一个人,想将uart,led,i2c等的Hal,jni,server直接编译进system.img里面,这样就可以多个应用访问这个公共的接口了,而不需要每个应用都写一套hal,jni,server等,但是应用开发的时候不知道怎么引用这些接口,Eclipse里不识别。Re: 南枫co 2014-05-24 21:09发表 [回复]回复mr_raptor:嗯,我还有个问题请教下:
假设系统开发和应用开不是同一个人,想将uart,led,i2c等的Hal,jni,server直接编译进system.img里面,这样就可以多个应用访问这个公共的接口了,而不需要每个应用都写一套hal,jni,server等,但是应用开发的时候不知道怎么引用这些接口,Eclipse里不识别。8楼 bossqingge 2014-04-24 23:07发表 [回复] DSC00015.jpg 时不时来拜读你的文章啊7楼 wujijunzhu 2014-04-16 15:27发表 [回复] DSC00016.jpg 顶!6楼 mozun1 2014-03-26 15:37发表 [回复] DSC00017.jpg 源代码 有地方下载吗?, 你出的书在哪里? 谢谢5楼 pianov 2013-08-15 17:37发表 [回复] DSC00018.jpg 您好
frameworks/Android.mk:裡的 services/jni/com_farsight_LedService.cpp有寫錯嗎?
應該是frameworks/services/jni/com_hello_LedService.cpp嗎?Re: mr_raptor 2014-01-17 11:21发表 [回复]回复pianovv510:您看的很仔细,是有写错,已经修改了,多谢指正,祝您学习愉快。4楼 williamlin3 2013-03-11 10:46发表 [回复] DSC00019.jpg 文章写得很好,没有经你允许我转走了几篇。不介意吧??Re: mr_raptor 2013-03-11 17:11发表 [回复]回复williamlin3:欢迎转载,互相学习~3楼 fandh2011 2012-12-01 11:13发表 [回复] DSC00020.gif 问题解决了,硬件id名是LED_HARDWARE_MODULE_ID一类的!2楼 fandh2011 2012-12-01 10:27发表 [回复]“硬件id名.default.so”???
硬件id名可以理解为/dev/led或/dev/beep等设备下的设备号吗?1楼 JayZhang 2012-10-18 08:47发表 [回复] DSC00021.jpg 在平台移植的时候更多的是把,jni,service都编译到系统中去吧!Re: mr_raptor 2012-10-18 09:43发表 [回复]回复zhangjie201412:不管什么移植,最终的目标都要编译到系统中,只是出现的位置不一样,Android给的源码里frameworks目录是框架层主要代码,包含Java部分和对应的JNI部分,不过,Google希望第三方厂商的代码放在vendor或device目录下,而不是直接修改Android的原生源码,所以我们自己添加的代码放到了vendor或device目录下,当然,如果你要放到frameworks下,也没有问题,这对产品开发没有问题,但是如果从开源的角度来看,特定设备的源码放到了框架层,就意味着,其它人使用你的源码,也要使用相同的设备了,别人不利用你的框架!!祝你工作愉快Re: JayZhang 2012-10-18 13:57发表 [回复]回复mr_raptor:嗯,多谢赐教发表评论

  • 用 户 名:
  • u012497906


  • 评论内容:
  • DSC00022.png

      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStack VPN Spark ERP IE10Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide Maemo Compuware 大数据 aptech PerlTornado Ruby Hibernate ThinkPHP HBase Pure Solr Angular Cloud Foundry Redis Scala Django Bootstrap
    个人资料

    DSC00023.png
    mr_raptor DSC00024.jpg

    • 访问:563314次
    • 积分:7578
    • 等级: DSC00025.jpg
    • 排名:第1133名


    • 原创:95篇
    • 转载:28篇
    • 译文:1篇
    • 评论:716条


    个人简介


    DSC00026.jpg mr_raptor北京

    《深入浅出:嵌入式底层软件开发》作者,从事Android移动开发、嵌入式系统开发。

    邮箱:tangpan09@gmail.com

    博客专栏

    DSC00027.gif Android平台移植文章:32篇
    阅读:245881WindowsPhone开发技术专栏文章:13篇
    阅读:49149

    文章分类


  • Android移植(59)
  • ARM体系结构(22)
  • Linux内核(3)
  • 裸板驱动(10)
  • 其它杂项(14)
  • WindowsPhone(15)
  • C语言详解(10)

    阅读排行


  • Android编译系统详解(一)(21408)
  • 深入浅出 - Android系统移植与平台开发(二) - 准备Android开发环境(17362)
  • 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一(14385)
  • 深入浅出 - Android系统移植与平台开发(一)(13899)
  • ARM处理器模式切换(含MRS,MSR指令)(12371)
  • Android编译系统详解(二)(12253)
  • 我与《深入浅出嵌入式底层软件开发》(12174)
  • 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析(11819)
  • 深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速(11626)
  • Android编译系统详解(三)(11258)

    文章搜索



    文章存档


  • 2015年04月(4)
  • 2015年01月(2)
  • 2014年08月(2)
  • 2014年06月(8)
  • 2014年05月(1)展开

    评论排行


  • WindowsPhone下拉刷新控件 - PullRefreshListBox(一)(97)
  • WindowsPhone下拉刷新控件 - PullRefreshListBox(二)(57)
  • S3C2440 SDRAM内存驱动(40)
  • WindowsPhone之我见(35)
  • 深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一(28)
  • 我与《深入浅出嵌入式底层软件开发》(21)
  • 整理Windows Phone 7教程(很全面)(20)
  • 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析(18)
  • mini6410中断控制器-VIC中断控制器(16)
  • miniOS_V2.0更新(15)

    最新评论


  • 深入浅出 - Android系统移植与平台开发(三)- 配置Vmware网络qq_26476749: 多谢楼主无私奉献该顶
  • 蓝牙聊天室App设计与实现diagram98: 唐老师,在Eclipse中import此源码运行发现只能发送信息,不能接收。提示TAG:Blueto...
  • Android蓝牙调试助手luguoqingting1956: 老师,我是初学者,看到你的百度里面Android应用开发从基础到项目第一讲【华清远见】课程,来找源码...
  • 深入浅出 - Android系统移植与平台开发(十二)- Android JNI机制Emilio66: 哥们可以出本书 了
  • Android蓝牙调试助手xiaobailong24: 有源码吗?楼主。希望分享一下,谢谢哈
  • 深入浅出 - Android系统移植与平台开发(一)剑晨无痕: 楼主有出书么~~书名叫什么啊
  • 深入浅出 - Android系统移植与平台开发(十二)- Android JNI机制EmilyLv: 对JNI的知识点将的很全面,很强大,学习了
  • 深入浅出 - Android系统移植与平台开发(十四) - Sensor HAL框架分析之四Vincent_shawn: 老师,如果我要添加一个一个新的传感器到sensor框架,该怎么做,看得迷迷糊糊的,刚接触androi...
  • 深入浅出 - Android系统移植与平台开发(十四) - Sensor HAL框架分析之四Vincent_shawn: 老师,如果我要添加一个一个新的传感器到sensor框架,该怎么做,看得迷迷糊糊的,刚接触androi...
  • 深入浅出 - Android系统移植与平台开发(一)frankqs: 书名是什么?谢谢 !
公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈网站客服杂志客服微博客服webmaster@iyunv.com400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持京 ICP 证 070598 号|Copyright &copy; 1999-2014, CSDN.NET, All Rights Reserved  







mr_raptor的专栏
专注Android系统,移动平台研究,ARM BSP开发,著有《深入浅出嵌入式底层软件开发》北航出版社

  • 目录视图
  • 摘要视图
  • 订阅
CSDN Android客户端 下载就送50C币     又见人月神话     最流行的语言想学就学     写博文,传代码,送C币     深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
分类: Android移植2012-10-17 17:35 9872人阅读 评论(18) 收藏 举报androidjavajnimodule平台
目录(?)[+]



通过前两节HAL框架分析和JNI概述,我们对Android提供的Stub HAL有了比较详细的了解了,下面我们来看下led的实例,写驱动点亮led灯,就如同写程序,学语言打印HelloWorld一样,如果说打印HelloWorld是一门新语言使用的第一声吆喝,那么点亮led灯就是我们学习HAL的一座灯塔,指挥我们在后面的复杂的HAL代码里准确找到方向。


LedHAL实例架构




上图描述了我们Led实例的框架层次:

l  LedDemo.java:是我们写的Android应用程序

l  LedService.java:是根据Led HAL封装的Java框架层的API,主要用于向应用层提供框架层API,它属于Android的框架层

l  libled_runtime.so:由于Java代码不能访问HAL层,该库是LedService.java对应的本地代码部分

l  led.default.so:针对led硬件的HAL代码

LedDemo通过LedService提供的框架层API访问Led设备,LedService对于LedDemo应用程序而言是Led设备的服务提供者,LedService运行在Dalvik中没有办法直接访问Led硬件设备,它只能将具体的Led操作交给本地代码来实现,通过JNI来调用Led硬件操作的封装库libled_runtime.so,由HAL Stub框架可知,在libled_runtime.so中首先查找注册为led的硬件设备module,找到之后保存其操作接口指针在本地库中等待框架层LedService调用。led.default.so是HAL层代码,它是上层操作的具体实施者,它并不是一个动态库(也就是说它并没有被任何进程加载并链接),它只是在本地代码查找硬件设备module时通过ldopen”杀鸡取卵”找module,返回该硬件module对应的device操作结构体中封装的函数指针。

其调用时序如下:



Led HAL实例代码分析


我们来看下led实例的目录结构:




主要文件如下:

com.hello.LedService.cpp:它在frameworks/services/jni目录下,是的Led本地服务代码

led.c:HAL代码

led.h:HAL代码头文件

LedDemo.java:应用程序代码

LedService.java:Led框架层服务代码

在Android的源码目录下,框架层服务代码应该放在frameworks/services/java/包名/目录下,由Android的编译系统统一编译生成system/framework/services.jar文件,由于我们的测试代码属于厂商定制代码,尽量不要放到frameworks的源码树里,我将其和LedDemo应用程序放在一起了,虽然这种方式从Android框架层次上不标准。

另外,本地服务代码的文件名要和对应的框架层Java代码的名字匹配(包名+类文件名,包目录用“_“代替)。有源码目录里都有对应的一个Android.mk文件,它是Android编译系统的指导文件,用来编译目标module。

1)        Android.mk文件分析

先来看下led源码中①号Android.mk:

[plain] view plaincopy

  • include $(call all-subdir-makefiles)  
代码很简单,表示包含当前目录下所有的Android.mk文件

先来看下led_app目录下的③号Android.mk:

[plain] view plaincopy

  • # 调用宏my-dir,这个宏返回当前Android.mk文件所在的路径  
  • LOCAL_PATH:= $(call my-dir)                                       
  •   
  • # 包含CLEAR_VARS变量指向的mk文件build/core/clear_vars.mk,它主要用来清除编译时依赖的编译变量  
  • include $(CLEAR_VARS)                                      
  •   
  • # 指定当前目标的TAG标签,关于其作用见前面Android编译系统章节  
  • LOCAL_MODULE_TAGS := user  
  •   
  • # 当前mk文件的编译目标模块  
  • LOCAL_PACKAGE_NAME := LedDemo  
  •   
  • # 编译目标时依赖的源码,它调用了一个宏all-java-files-under,该宏在build/core/definitions.mk中定义  
  • # 表示在当前目录下查找所有的java文件,将查找到的java文件返回  
  • LOCAL_SRC_FILES := $(callall-java-files-under, src)  
  •   
  • # 在编译Android应用程序时都要指定API level,也就是当前程序的编译平台版本  
  • # 这里表示使用当前源码的版本  
  • LOCAL_SDK_VERSION := current  
  •   
  • # 最重要的就是这句代码,它包含了一个文件build/core/package.mk,根据前面设置的编译变量,编译生成Android包文件,即:apk文件  
  • include $(BUILD_PACKAGE)  
上述代码中都加了注释,基本上每一个编译目标都有类似上述的编译变量的声明:

LOCAL_MODULE_TAGS

LOCAL_PACKAGE_NAME

LOCAL_SRC_FILES

由于所有的Android.mk最终被编译系统包含,所以在编译每个目标模块时,都要通过LOCAL_PATH:= $(call my-dir)指定当前目标的目录,然后调用include $(CLEAR_VARS)先清除编译系统依赖的重要的编译变量,再生成新的编译变量。

让我们来看看LedDemo目标对应的源码吧。

2)        LedDemo代码分析

学习过Android应用的同学对其目录结构很熟悉,LedDemo的源码在src目录下。

@ led_app/src/com/farsight/LedDemo.java:

[java] view plaincopy

  • package com.hello;  
  •   
  • import com.hello.LedService;  
  •   
  • import com.hello.R;  
  •   
  • importandroid.app.Activity;  
  •   
  • importandroid.os.Bundle;  
  •   
  • importandroid.util.Log;  
  •   
  • importandroid.view.View;  
  •   
  • import android.view.View.OnClickListener;  
  •   
  • importandroid.widget.Button;  
  •   
  •    
  •   
  • public classLedDemo extends Activity {  
  •      privateLedService led_svc;  
  •      private Buttonbtn;  
  •      private booleaniflag = false;  
  •      private Stringtitle;  
  •   
  •       /** Calledwhen the activity is first created. */  
  •      @Override  
  •      public void onCreate(Bundle savedInstanceState) {  
  •         super.onCreate(savedInstanceState);  
  •         setContentView(R.layout.main);  
  •   
  •         Log.i(&quot;Java App&quot;, &quot;OnCreate&quot;);  
  •          led_svc =new LedService();  
  •          btn =(Button) this.findViewById(R.id.Button01);  
  •         this.btn.setOnClickListener(new OnClickListener() {  
  •             public void onClick(View v) {  
  •                 Log.i(&quot;Java App&quot;, &quot;btnOnClicked&quot;);  
  •                 if (iflag) {  
  •                     title = led_svc.set_off();  
  •                     btn.setText(&quot;Turn On&quot;);  
  •                     setTitle(title);  
  •                     iflag = false;  
  •                 } else {  
  •                     title = led_svc.set_on();  
  •                     btn.setText(&quot;Turn Off&quot;);  
  •                     setTitle(title);  
  •                     iflag = true;  
  •                 }  
  •              }  
  •          });  
  •      }  
  • }  
代码很简单,Activity上有一个按钮,当Activity初始化时创建LedService对象,按钮按下时通过LedService对象调用其方法set_on()和set_off()。


3)        LedService代码分析

我们来看下LedService的代码:

@led_app/src/com/farsight/LedService.java:

[java] view plaincopy

  • package com.hello;  
  • import android.util.Log;  
  •   
  • public class LedService {  
  •   
  •     /*
  •      * loadnative service.
  •      */  
  •     static {         // 静态初始化语言块,仅在类被加载时被执行一次,通常用来加载库  
  •         Log.i (&quot;Java Service&quot; , &quot;Load Native Serivce LIB&quot; );  
  •        System.loadLibrary ( &quot;led_runtime&quot; );  
  •     }  
  •   
  •     // 构造方法  
  •     public LedService() {        
  •         int icount ;  
  •   
  •         Log.i (&quot;Java Service&quot; , &quot;do init Native Call&quot; );  
  •         _init ();           
  •         icount =_get_count ();  
  •         Log.d (&quot;Java Service&quot; , &quot;led count = &quot; + icount );  
  •         Log.d (&quot;Java Service&quot; , &quot;Init OK &quot; );  
  •     }  
  •   
  •     /*
  •      * LED nativemethods.
  •      */  
  •     public Stringset_on() {  
  •         Log.i (&quot;com.hello.LedService&quot; , &quot;LED On&quot; );  
  •         _set_on();  
  •         return&quot;led on&quot; ;  
  •      }  
  •   
  •      public String set_off() {  
  •          Log.i (&quot;com.hello.LedService&quot; , &quot;LED Off&quot; );  
  •          _set_off();  
  •          return&quot;led off&quot; ;  
  •      }  
  •   
  •      /*
  •      * declare all the native interface.
  •      */  
  •      private static native boolean _init();  
  •      private static native int _set_on();  
  •      private static native int _set_off();  
  •      private static native int _get_count();  
  •   
  •   }  
通过分析上面代码可知LedService的工作:

l   加载本地服务的库代码

l   在构造方法里调用_init本地代码,对Led进行初始化,并调用get_count得到Led灯的个数

l   为LedDemo应用程序提供两个API:set_on和set_off,这两个API方法实际上也是交给了本地服务代码来操作的

由于Java代码无法直接操作底层硬件,通过JNI方法将具体的操作交给本地底层代码实现,自己只是一个API Provider,即:服务提供者。

让我们来到底层本地代码,先看下底层代码的Android.mk文件:

@ frameworks/Android.mk:

[plain] view plaincopy

  • LOCAL_PATH:= $(call my-dir)  
  • include $(CLEAR_VARS)  
  •   
  • LOCAL_MODULE_TAGS := eng  
  • LOCAL_MODULE:= libled_runtime                    # 编译目标模块  
  • LOCAL_SRC_FILES:= \  
  •        services/jni/com_farsight_LedService.cpp  
  •   
  •    
  • LOCAL_SHARED_LIBRARIES := \                       # 编译时依赖的动态库  
  •        libandroid_runtime  \  
  •        libnativehelper    \  
  •         libcutils         \  
  •         libutils          \  
  •        libhardware  
  •   
  • LOCAL_C_INCLUDES += \                                  #编译时用到的头文件目录  
  •        $(JNI_H_INCLUDE)  
  •   
  • LOCAL_PRELINK_MODULE := false                            # 本目标为非预链接模块  
  • include $(BUILD_SHARED_LIBRARY)                # 编译生成共享动态库  
结合前面分析的Android.mk不难看懂这个mk文件。之前的mk文件是编译成Android apk文件,这儿编译成so共享库,所以LOCAL_MODULE和include $(BUILD_SHARED_LIBRARY)与前面mk文件不同,关于Android.mk文件里的变量作用,请查看Android编译系统章节。

总而言之,本地代码编译生成的目标是libled_runtime.so文件。


4)        Led本地服务代码分析

我们来看下本地服务的源码:

@ frameworks/services/jni/com_farsight_LedService.cpp:

[cpp] view plaincopy

  • #define LOG_TAG &quot;LedService&quot;  
  • #include &quot;utils/Log.h&quot;  
  • #include <stdlib.h>  
  • #include <string.h>  
  • #include <unistd.h>  
  • #include <assert.h>  
  • #include <jni.h>  
  • #include &quot;../../../hardware/led.h&quot;  
  •   
  • static led_control_device_t *sLedDevice = 0;  
  • static led_module_t* sLedModule=0;   
  •   
  • static jint get_count(void)  
  • {  
  •     LOGI(&quot;%sE&quot;, __func__);  
  •    if(sLedDevice)  
  •         returnsLedDevice->get_led_count(sLedDevice);  
  •     else  
  •        LOGI(&quot;sLedDevice is null&quot;);  
  •     return 0;  
  • }  
  •   
  •   
  • static jint led_setOn(JNIEnv* env, jobject thiz) {  
  •     LOGI(&quot;%sE&quot;, __func__);  
  •     if(sLedDevice) {  
  •        sLedDevice->set_on(sLedDevice);  
  •     }else{  
  •        LOGI(&quot;sLedDevice is null&quot;);  
  •     }  
  •     return 0;  
  • }   
  •   
  • static jint led_setOff(JNIEnv* env, jobject thiz) {  
  •     LOGI(&quot;%s E&quot;, __func__);  
  •      if(sLedDevice) {  
  •         sLedDevice->set_off(sLedDevice);  
  •      }else{  
  •          LOGI(&quot;sLedDevice is null&quot;);  
  •      }  
  •      return 0;  
  • }  
  •   
  • static inline int led_control_open(const structhw_module_t* module,  
  •      structled_control_device_t** device) {  
  •     LOGI(&quot;%s E &quot;, __func__);  
  •      returnmodule->methods->open(module,  
  •         LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
  • }  
  •   
  • static jint led_init(JNIEnv *env, jclass clazz)  
  • {  
  •     led_module_tconst * module;  
  •     LOGI(&quot;%s E &quot;, __func__);  
  •      if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0){  
  •         LOGI(&quot;get Module OK&quot;);  
  •         sLedModule = (led_module_t *) module;  
  •         if(led_control_open(&module->common, &sLedDevice) != 0) {  
  •            LOGI(&quot;led_init error&quot;);  
  •            return-1;  
  •         }  
  •     }   
  •   
  •     LOGI(&quot;led_init success&quot;);  
  •     return 0;  
  • }  
  •   
  •    
  •   
  • /*
  •   *
  •   * Array ofmethods.
  •   * Each entryhas three fields: the name of the method, the method
  •   * signature,and a pointer to the native implementation.
  •   */  
  • static const JNINativeMethod gMethods[] = {  
  •     {&quot;_init&quot;,    &quot;()Z&quot;,(void*)led_init},  
  •     {&quot;_set_on&quot;,   &quot;()I&quot;,(void*)led_setOn },  
  •     {&quot;_set_off&quot;, &quot;()I&quot;,(void*)led_setOff },  
  •     {&quot;_get_count&quot;, &quot;()I&quot;,(void*)get_count },  
  • };  
  •   
  •   static int registerMethods(JNIEnv* env) {  
  •      static constchar* const kClassName = &quot;com/hello/LedService&quot;;  
  •      jclass clazz;  
  •      /* look upthe class */  
  •      clazz =env->FindClass(kClassName);  
  •      if (clazz ==NULL) {  
  •         LOGE(&quot;Can't find class %s\n&quot;, kClassName);  
  •          return-1;  
  •      }   
  •   
  •      /* registerall the methods */  
  •      if(env->RegisterNatives(clazz, gMethods,  
  •             sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)  
  •      {  
  •         LOGE(&quot;Failed registering methods for %s\n&quot;, kClassName);  
  •          return -1;  
  •      }  
  •      /* fill outthe rest of the ID cache */  
  •      return 0;  
  • }  
  •   
  •    
  •   
  • /*
  •   * This iscalled by the VM when the shared library is first loaded.
  •   */  
  • jint JNI_OnLoad(JavaVM* vm, void* reserved) {  
  •      JNIEnv* env= NULL;  
  •      jint result= -1;  
  •     LOGI(&quot;JNI_OnLoad&quot;);  
  •      if(vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {  
  •         LOGE(&quot;ERROR: GetEnv failed\n&quot;);  
  •          gotofail;  
  •      }  
  •   
  •      assert(env!= NULL);  
  •      if(registerMethods(env) != 0) {  
  •          LOGE(&quot;ERROR: PlatformLibrary nativeregistration failed\n&quot;);  
  •          gotofail;  
  •      }  
  •      /* success-- return valid version number */  
  •      result =JNI_VERSION_1_4;   
  •   
  • fail:  
  •      return result;  
  • }  
         这儿的代码不太容易读,因为里面是JNI的类型和JNI特性的代码,看代码先找入口。LedService.java框架代码一加载就调用静态初始化语句块里的System.loadLibrary ( &quot;led_runtime&quot; ),加载libled_runtime.so,该库刚好是前面Android.mk文件的目标文件,也就是说LedService加载的库就是由上面的本地代码生成的。当一个动态库被Dalvik加载时,首先在Dalvik会回调该库代码里的JNI_OnLoad函数。也就是说JNI_OnLoad就是本地服务代码的入口函数。

         JNI_OnLoad的代码一般来说是死的,使用的时候直接拷贝过来即可,vm->GetEnv会返回JNIEnv指针,而这个指针其实就是Java虚拟机的环境变量,我们可以通过该指针去调用JNI提供的方法,如FindClass等,调用registerMethods方法,在方法里通过JNIEnv的FindClass查找LedService类的引用,然后在该类中注册本地方法与Java方法的映射关系,上层Java代码可以通过这个映射关系调用到本地代码的实现。RegisterNatives方法接收三个参数:

l  第一个参数jclass:要注册哪个类里的本地方法映射关系

l  第二个参数JNINativeMethod*:这是一个本地方法与Java方法映射数组,JNINativeMethod是个结构体,每个元素是一个Java方法到本地方法的映射。

[cpp] view plaincopy

  • typedef struct {  
  •          constchar* name;  
  •          constchar* signature;  
  •          void*fnPtr;  
  • } JNINativeMethod;  
         name:表示Java方法名

         signature:表示方法的签名

         fnPtr:Java方法对应的本地方法指针

l  第三个参数size:映射关系个数

由代码可知,Java方法与本地方法的映射关系如下:

Java方法

本地方法

void _init()

jint led_init(JNIEnv *env, jclass clazz)

int _set_on()

jint led_setOn(JNIEnv* env, jobject thiz)

int _set_off()

jint led_setOff(JNIEnv* env, jobject thiz)

int _get_count()

jint get_count(void)

通过上表可知,本地方法参数中默认会有两个参数:JNIEnv* env, jobject thiz,分别表示JNI环境和调用当前方法的对象引用,当然你也可以不设置这两个参数,在这种情况下你就不能访问Java环境中的成员。本地方法与Java方法的签名必须一致,返回值不一致不会造成错误。

现在我们再来回顾下我们的调用调用流程:

l  LedDemo创建了LedService对象

l  LedService类加载时加载了对应的本地服务库,在本地服务库里Dalvik自动调用JNI_OnLoad函数,注册Java方法和本地方法映射关系。

根据Java语言特点,当LedDemo对象创建时会调用其构造方法LedService()。

[cpp] view plaincopy

  • // 构造方法  
  •     public LedService() {        
  •         int icount ;  
  •         Log.i (&quot;Java Service&quot; , &quot;do init Native Call&quot; );  
  •         _init ();           
  •         icount =_get_count ();  
  •         Log.d (&quot;Java Service&quot; , &quot;led count = &quot; + icount );  
  •         Log.d (&quot;Java Service&quot; , &quot;Init OK &quot; );  
  •     }  
在LedService构造方法里直接调用了本地方法_init和_get_count(通过native保留字声明),也就是说调用了本地服务代码里的jint led_init(JNIEnv *env, jclass clazz)和jintget_count(void)。

在led_init方法里的内容就是我们前面分析HAL框架代码的使用规则了。

l  通过hw_get_module方法查到到注册为LED_HARDWARE_MODULE_ID,即:”led”的module模块。

l  通过与led_module关联的open函数指针打开led设备,返回其device_t结构体,保存在本地代码中,有的朋友可能会问,不是本地方法不能持续保存一个引用吗?由于device_t结构是在open设备时通过malloc分配的,只要当前进程不死,该指针一直可用,在这儿本地代码并没有保存Dalvik里的引用,保存的是mallco的分配空间地址,但是在关闭设备时记得要将该地址空间free了,否则就内存泄漏了。

l  拿到了led设备的device_t结构之后,当LedDemo上的按钮按下时调用LedService对象的set_on和set_off方法,这两个LedService方法直接调用了本地服务代码的对应映射方法,本地方法直接调用使用device_t指向的函数来间接调用驱动操作代码。

好吧,让我们再来看一个详细的时序图:



不用多解释了。

最后一个文件,HAL对应的Android.mk文件:

@ hardware/Android.mk:

[plain] view plaincopy

  • LOCAL_PATH := $(call my-dir)  
  • include $(CLEAR_VARS)  
  •   
  • LOCAL_C_INCLUDES += \  
  •          include/  
  •   
  • LOCAL_PRELINK_MODULE := false  
  • LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw  
  • LOCAL_SHARED_LIBRARIES := liblog  
  • LOCAL_SRC_FILES := led.c  
  • LOCAL_MODULE := led.default  
  • include $(BUILD_SHARED_LIBRARY)  
注:LOCAL_PRELINK_MODULE:= false要加上,否则编译出错

指定目标名为:led.default

目标输入目录LOCAL_MODULE_PATH为:/system/lib/hw/,不指定会默认输出到/system/lib目录下。

根据前面HAL框架分析可知,HAL Stub库默认加载地址为:/vendor/lib/hw/或/system/lib/hw/,在这两个目录查找:硬件id名.default.so,所以我们这儿指定了HAL Stub的编译目标名为led.default,编译成动态库,输出目录为:$(TARGET_OUT_SHARED_LIBRARIES)/hw,TARGET_OUT_SHARED_LIBRARIES指/system/lib/目录。
5) 深入理解
我们从进程空间的概念来分析下我们上面写的代码。


我们前面的示例代码中,将LedDemo.java和LedService.java都放在了一个APK文件里,这也就意味着这个应用程序编译完之后,它会运行在一个Dalvik虚拟机实例中,即:一个进程里,在LedService.java中加载了libled_runtime.so库,通过JNI调用了本地代码,根据动态库的运行原理,我们知道,libled_runtime.so在第一次引用时会被加载到内存中并映射到引用库的进程空间中,我们可以简单理解为引用库的程序和被引用的库在一个进程中,而在libled_runtime.so库中,又通过dlopen打开了库文件led.default.so(该库并没有被库加载器加载,而是被当成一个文件打开的),同样我们可以理解为led.default.so和libled_runtime.so在同一个进程中。
由此可见,上面示例的Led HAL代码全部都在一个进程中实现,在该示例中的LedService功能比较多余,基本上不能算是一个服务。如果LedDemo运行在两个进程中,就意味着两个进程里的LedService不能复用,通常我们所谓的Service服务一般向客户端提供服务并且同时可以为多个客户端服务(如下图),所以我们的示例Led HAL代码不是完美的HAL模型,我们后面章节会再实现一个比较完美的HAL架构。











  • 上一篇深入浅出 - Android系统移植与平台开发(九)- JNI介绍
  • 下一篇深入浅出 - Android系统移植与平台开发(十一) - Sensor HAL框架分析之一
顶23踩2主题推荐移植设计androidandroid应用应用程序猜你在找TP调试高通Android平台硬件调试之Camera篇queue_delayed_work和queue_work区别v4l2 编程接口二 driver实现uboot 命令自动补全 及 修正设置环境变量时自启动内核iOS开发Swift语言学习教程Java基础核心技术:多线程(day16-day17)Android入门实战教程Java基础核心技术:IO(day15-day16)Linux企业常用文件管理命令详解准备好了么? 跳吧             !更多职位尽在 CSDN JOB【精准广告投放】C/C++研发工程师/数据挖掘工程师百度在线网络技术(北京)有限公司|18-35K/月我要跳槽C/C++/C#软件工程师利博国际科技发展有限公司|10-20K/月我要跳槽C/C++软件开发工程师合肥赛猊腾龙信息技术有限公司|10-20K/月我要跳槽C/C++开发上海云盾信息技术有限公司|20-25K/月我要跳槽查看评论10楼 心鑫 2014-05-14 17:01发表 [回复]请问:我把mokoid的源码放在device目录下,编译有问题,修改下编译没问题,在/build/target/product/generic_no_telephony.mk下添加了 LedTest \
LedClient \
在out/target/product/generic/system/app目录下也有对应的apk,可为什么在虚拟机里就是看不到apk呢?
你的串口的例子就没问题,实在找不出两都的差别了Re: mr_raptor 2014-05-17 19:14发表 [回复]回复proud2005:你看看你的Logcat,你的这个问题我遇到过,不是因为API level不对,就是APk安装出了问题。9楼 南枫co 2014-05-09 21:55发表 [回复]请问,你这个工程怎么编译的呢?有c,cpp,javaRe: mr_raptor 2014-05-12 10:57发表 [回复]回复far5811:通过编写Android.mk文件来实现编译,Android的编译系统会自动完成编译。详细请看Android编译系统一、二、三文章。Re: 南枫co 2014-05-24 21:10发表 [回复]回复mr_raptor:嗯,我还有个问题请教下:
假设系统开发和应用开不是同一个人,想将uart,led,i2c等的Hal,jni,server直接编译进system.img里面,这样就可以多个应用访问这个公共的接口了,而不需要每个应用都写一套hal,jni,server等,但是应用开发的时候不知道怎么引用这些接口,Eclipse里不识别。Re: 南枫co 2014-05-24 21:09发表 [回复]回复mr_raptor:嗯,我还有个问题请教下:
假设系统开发和应用开不是同一个人,想将uart,led,i2c等的Hal,jni,server直接编译进system.img里面,这样就可以多个应用访问这个公共的接口了,而不需要每个应用都写一套hal,jni,server等,但是应用开发的时候不知道怎么引用这些接口,Eclipse里不识别。8楼 bossqingge 2014-04-24 23:07发表 [回复]时不时来拜读你的文章啊7楼 wujijunzhu 2014-04-16 15:27发表 [回复]顶!6楼 mozun1 2014-03-26 15:37发表 [回复]源代码 有地方下载吗?, 你出的书在哪里? 谢谢5楼 pianov 2013-08-15 17:37发表 [回复]您好
frameworks/Android.mk:裡的 services/jni/com_farsight_LedService.cpp有寫錯嗎?
應該是frameworks/services/jni/com_hello_LedService.cpp嗎?Re: mr_raptor 2014-01-17 11:21发表 [回复]回复pianovv510:您看的很仔细,是有写错,已经修改了,多谢指正,祝您学习愉快。4楼 williamlin3 2013-03-11 10:46发表 [回复]文章写得很好,没有经你允许我转走了几篇。不介意吧??Re: mr_raptor 2013-03-11 17:11发表 [回复]回复williamlin3:欢迎转载,互相学习~3楼 fandh2011 2012-12-01 11:13发表 [回复]问题解决了,硬件id名是LED_HARDWARE_MODULE_ID一类的!2楼 fandh2011 2012-12-01 10:27发表 [回复]“硬件id名.default.so”???
硬件id名可以理解为/dev/led或/dev/beep等设备下的设备号吗?1楼 JayZhang 2012-10-18 08:47发表 [回复]在平台移植的时候更多的是把,jni,service都编译到系统中去吧!Re: mr_raptor 2012-10-18 09:43发表 [回复]回复zhangjie201412:不管什么移植,最终的目标都要编译到系统中,只是出现的位置不一样,Android给的源码里frameworks目录是框架层主要代码,包含Java部分和对应的JNI部分,不过,Google希望第三方厂商的代码放在vendor或device目录下,而不是直接修改Android的原生源码,所以我们自己添加的代码放到了vendor或device目录下,当然,如果你要放到frameworks下,也没有问题,这对产品开发没有问题,但是如果从开源的角度来看,特定设备的源码放到了框架层,就意味着,其它人使用你的源码,也要使用相同的设备了,别人不利用你的框架!!祝你工作愉快Re: JayZhang 2012-10-18 13:57发表 [回复]回复mr_raptor:嗯,多谢赐教发表评论

  • 用 户 名:
  • u012497906


  • 评论内容:


      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStack VPN Spark ERP IE10Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BI HTML5 Spring Apache .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components WindowsMobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace WebApp SpringSide Maemo Compuware 大数据 aptech PerlTornado Ruby Hibernate ThinkPHP HBase Pure Solr Angular CloudFoundry Redis Scala Django Bootstrap
    个人资料


    mr_raptor

    • 访问:563314次
    • 积分:7578
    • 等级:
    • 排名:第1133名


    • 原创:95篇
    • 转载:28篇
    • 译文:1篇
    • 评论:716条


    个人简介


    mr_raptor北京

    《深入浅出:嵌入式底层软件开发》作者,从事Android移动开发、嵌入式系统开发。

    邮箱:tangpan09@gmail.com

    博客专栏

    Android平台移植文章:32篇
    阅读:245881WindowsPhone开发技术专栏文章:13篇
    阅读:49149

    文章分类


  • Android移植(59)
  • ARM体系结构(22)
  • Linux内核(3)
  • 裸板驱动(10)
  • 其它杂项(14)
  • WindowsPhone(15)
  • C语言详解(10)

    阅读排行


  • Android编译系统详解(一)(21408)
  • 深入浅出- Android系统移植与平台开发(二) - 准备Android开发环境(17362)
  • 深入浅出- Android系统移植与平台开发(十一) - Sensor HAL框架分析之一(14385)
  • 深入浅出 -Android系统移植与平台开发(一)(13899)
  • ARM处理器模式切换(含MRS,MSR指令)(12371)
  • Android编译系统详解(二)(12253)
  • 我与《深入浅出嵌入式底层软件开发》(12174)
  • 深入浅出- Android系统移植与平台开发(八)- HAL Stub框架分析(11819)
  • 深入浅出- Android系统移植与平台开发(六)- 为Android启动加速(11626)
  • Android编译系统详解(三)(11258)

    文章搜索



    文章存档


  • 2015年04月(4)
  • 2015年01月(2)
  • 2014年08月(2)
  • 2014年06月(8)
  • 2014年05月(1)展开

    评论排行


  • WindowsPhone下拉刷新控件- PullRefreshListBox(一)(97)
  • WindowsPhone下拉刷新控件- PullRefreshListBox(二)(57)
  • S3C2440 SDRAM内存驱动(40)
  • WindowsPhone之我见(35)
  • 深入浅出- Android系统移植与平台开发(十一) - Sensor HAL框架分析之一(28)
  • 我与《深入浅出嵌入式底层软件开发》(21)
  • 整理WindowsPhone 7教程(很全面)(20)
  • 深入浅出- Android系统移植与平台开发(十) - led HAL简单设计案例分析(18)
  • mini6410中断控制器-VIC中断控制器(16)
  • miniOS_V2.0更新(15)

    最新评论


  • 深入浅出 - Android系统移植与平台开发(三)-配置Vmware网络qq_26476749: 多谢楼主无私奉献该顶
  • 蓝牙聊天室App设计与实现diagram98: 唐老师,在Eclipse中import此源码运行发现只能发送信息,不能接收。提示TAG:Blueto...
  • Android蓝牙调试助手luguoqingting1956: 老师,我是初学者,看到你的百度里面Android应用开发从基础到项目第一讲【华清远见】课程,来找源码...
  • 深入浅出 - Android系统移植与平台开发(十二)-Android JNI机制Emilio66: 哥们可以出本书 了
  • Android蓝牙调试助手xiaobailong24: 有源码吗?楼主。希望分享一下,谢谢哈
  • 深入浅出 - Android系统移植与平台开发(一)剑晨无痕: 楼主有出书么~~书名叫什么啊
  • 深入浅出 - Android系统移植与平台开发(十二)-Android JNI机制EmilyLv: 对JNI的知识点将的很全面,很强大,学习了
  • 深入浅出 - Android系统移植与平台开发(十四)- Sensor HAL框架分析之四Vincent_shawn: 老师,如果我要添加一个一个新的传感器到sensor框架,该怎么做,看得迷迷糊糊的,刚接触androi...
  • 深入浅出 - Android系统移植与平台开发(十四)- Sensor HAL框架分析之四Vincent_shawn: 老师,如果我要添加一个一个新的传感器到sensor框架,该怎么做,看得迷迷糊糊的,刚接触androi...
  • 深入浅出 - Android系统移植与平台开发(一)frankqs: 书名是什么?谢谢 !
公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈网站客服杂志客服微博客服webmaster@iyunv.com400-600-2320|北京创新乐知信息技术有限公司版权所有|江苏乐知网络技术有限公司提供商务支持京 ICP 证 070598 号|Copyright© 1999-2014, CSDN.NET, All Rights Reserved

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-126428-1-1.html 上篇帖子: Apache Software Foundation Index: Project Listing 下篇帖子: Secondary Storage VM 介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表