Chinaunix首页 | 论坛 | 博客
  • 博客访问: 404606
  • 博文数量: 120
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 741
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-27 18:15
文章分类

全部博文(120)

文章存档

2016年(13)

2015年(41)

2014年(66)

我的朋友

分类: Android平台

2014-08-09 14:18:35

/*************************************************************************************************/
本文转自:http://blog.csdn.net/mr_raptor/article/details/8082360

    
再次向原创者mr_raptor大神致敬!
    mr_raptor大神是在12年基于Android2.0来分析,现在我结合4.2的版本,然后加上我的理解,算是对原创文章的一种回馈吧,让mr_raptor看到他的杰作给别人带来的一种进步,我想这样能更加鼓励mr_raptor原创文章!
    由于我是Android初学者,有些理解可能是错的,希望大神门指教!
    谢谢!
/*************************************************************************************************/
    通过前两节HAL框架分析和JNI概述,我们对Android提供的Stub HAL有了比较详细的了解,下面我们来看下led的实例,写驱动点亮led灯,就如同写程序,学语言打印HelloWorld一样,如果说打印HelloWorld是一门新语言使用的第一声吆喝,那么点亮led灯就是我们学习HAL的一座灯塔,指挥我们在后面的复杂的HAL代码里准确找到方向。

一、LedHAL实架构

上图描述了我们Led实例的框架层次:
    * LedDemo.java:是我们写的Android应用程序
    * LedService.java:是根据Led JNI封装的Java框架层的API,主要用于向应用层(LedDemo.java)提供框架层API,它属于Android的框架层
    * libled_runtime.so:由于Java代码不能访问HAL层,该库是LedService.java对应的本地代码部分,需要在LedService中被动态的加载
    * led.default.so:针对led硬件的HAL代码,不需要被加载,里面存储着LED硬件对象结构和LED设备结构及设备具体方法,需要被查找并返回给本地代码
    
    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层代码,它是上层操作的具体实施者(直接使用Linux SYS api接口操作设备),它并不是一个动态库(也就是说它并没有被任何进程加载并链接),它只是在本地代码查找硬件对象module时通过dlopen函数”杀鸡取卵”找module,并且通过open函数返回该硬件module对应的device操作结构体中封装的函数指针。

其调用时序如下:

二、Led HAL实例代码分析
    先来看下led实例的目录结构:

主要文件如下:层次从下到上
    led.c:HAL代码
    led.h:HAL代码头文件
    com_hello_LedService.cpp:它在frameworks/base/services/jni目录下,是的Led本地代码
    LedService.java:Led框架层服务代码
    LedDemo.java:应用程序代码
    每层都有自己的Android.mk文件

    在Android的源码目录下,框架层服务代码应该放在frameworks/base/services/java/包名(com)/目录(hello)下,由Android的编译系统统一编译生成out/target/product/proudct_name/system/framework/services.jar文件,由于我们的测试代码属于厂商定制代码,尽量不要放到frameworks的源码树里,我将其和LedDemo应用程序放在一起了,虽然这种方式从Android框架层次上不标准。
    另外,本地服务代码的文件名要和对应的框架层Java代码的名字匹配,文件名格式为“包名_类文件名_xx"(包目录用“_“代替,Ex:com_hello_LedService.cpp)。有源码目录里都有对应的一个Android.mk文件,它是Android编译系统的指导文件,用来编译目标module。

2.1 Android.mk文件分析
    a) 上图中①号Android.mk,该mk文件为本地代码的编译文件。

点击(此处)折叠或打开

  1. include $(call all-subdir-makefiles)
    代码很简单,表示包含当前目录下所有的Android.mk文件
    b) 上图中③号Android.mk:框架层服务代码和应用层服务代码的编译文件

点击(此处)折叠或打开

  1. # 调用宏my-dir,这个宏返回当前Android.mk文件所在的路径
  2. LOCAL_PATH:= $(call my-dir)

  3. # 包含CLEAR_VARS变量指向的mk文件build/core/clear_vars.mk,它主要用来清除编译时依赖的编译变量
  4. include $(CLEAR_VARS)

  5. # 指定当前目标的TAG标签,关于其作用见前面Android编译系统章节eng\user\test
  6. LOCAL_MODULE_TAGS := user

  7. # 当前mk文件的编译目标模块,apk的名字
  8. LOCAL_PACKAGE_NAME := LedDemo

  9. # 编译目标时依赖的源码,它调用了一个宏all-java-files-under,该宏在build/core/definitions.mk中定义
  10. # 表示在当前目录下查找所有的java文件,将查找到的java文件返回
  11. LOCAL_SRC_FILES := $(call all-java-files-under, src)

  12. # 在编译Android应用程序时都要指定API level,也就是当前程序的编译平台版本
  13. # 这里表示使用当前源码的版本
  14. LOCAL_SDK_VERSION := current

  15. # 最重要的就是这句代码,它包含了一个文件build/core/package.mk,根据前面设置的编译变量,编译生成Android包文件,即:apk文件
  16. include $(BUILD_PACKAGE)
上述代码中都加了注释,基本上每一个编译目标都有类似上述的编译变量的声明:
    LOCAL_MODULE_TAGS
    LOCAL_PACKAGE_NAME
    LOCAL_SRC_FILES
    由于所有的Android.mk最终被编译系统包含,所以在编译每个目标模块时,都要通过LOCAL_PATH:= $(call my-dir)指定当前目标的目录,然后调用include $(CLEAR_VARS)先清除编译系统依赖的重要的编译变量,再生成新的编译变量。

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

2.2 LedDemo代码分析
    学习过Android应用的同学对其目录结构很熟悉,LedDemo的源码在src目录下。
    @led_app/src/com/hello/LedDemo.java:

点击(此处)折叠或打开

  1.  package com.hello;
  2.  import com.hello.LedService;
  3.  import com.hello.R;
  4.  importandroid.app.Activity;
  5.  importandroid.os.Bundle;
  6.  importandroid.util.Log;
  7.  importandroid.view.View;
  8.  import android.view.View.OnClickListener;
  9.  importandroid.widget.Button;

  10.  public classLedDemo extends Activity {
  11.      privateLedService led_svc;
  12.      private Buttonbtn;
  13.      private booleaniflag = false;
  14.      private Stringtitle;

  15.       /** Calledwhen the activity is first created. */
  16.      @Override
  17.      public void onCreate(Bundle savedInstanceState) {
  18.         super.onCreate(savedInstanceState);
  19.         setContentView(R.layout.main);

  20.         Log.i("Java App", "OnCreate");
  21.         led_svc =new LedService();    /*新建一个框架服务对象*/
  22.         btn =(Button) this.findViewById(R.id.Button01);
  23.         this.btn.setOnClickListener(new OnClickListener() {
  24.             public void onClick(View v) {
  25.                 Log.i("Java App", "btnOnClicked");
  26.                 if (iflag) {
  27.                     title = led_svc.set_off();
  28.                     btn.setText("Turn On");
  29.                     setTitle(title);
  30.                     iflag = false;
  31.                 } else {
  32.                     title = led_svc.set_on();
  33.                     btn.setText("Turn Off");
  34.                     setTitle(title);
  35.                     iflag = true;
  36.                 }
  37.              }
  38.          });
  39.      }
  40.  }

    代码很简单,Activity上有一个按钮,当Activity初始化时创建LedService对象,按钮按下时通过LedService对象调用其方法set_on()和set_off()。

2.3 LedService代码分析
我们来看下LedService的代码:
@led_app/src/com/hello/LedService.java:

点击(此处)折叠或打开

  1. package com.hello;
  2. import android.util.Log;

  3. public class LedService {

  4.     /*
  5.      * loadnative service.
  6.      */
  7.     static { // 静态初始化语言块,仅在类被加载时被执行一次,通常用来加载库
  8.         Log.i ("Java Service" , "Load Native Serivce LIB" );
  9.        System.loadLibrary ( "led_runtime" );
  10.     }

  11.     // 构造方法
  12.     public LedService() {
  13.         int icount ;

  14.         Log.i ("Java Service" , "do init Native Call" );
  15.         _init (); /*通过JNI调用本地方法获取LED设备,这样就将LED的HAL stub层的操作接口保存在本地代码中了*/
  16.         icount =_get_count ();
  17.         Log.d ("Java Service" , "led count = " + icount );
  18.         Log.d ("Java Service" , "Init OK " );
  19.     }

  20.     /*
  21.      * LED nativemethods.
  22.      */
  23.     public Stringset_on() {
  24.         Log.i ("com.hello.LedService" , "LED On" );
  25.         _set_on();/*通过JNI调用本地方法操作LED设备*/
  26.         return"led on" ;
  27.      }

  28.      public String set_off() {
  29.          Log.i ("com.hello.LedService" , "LED Off" );
  30.          _set_off();/*通过JNI调用本地方法操作LED设备*/
  31.          return"led off" ;
  32.      }

  33.      /*
  34.      * declare all the native interface.
  35.      */
  36.      private static native boolean _init();
  37.      private static native int _set_on();
  38.      private static native int _set_off();
  39.      private static native int _get_count();

  40.   }
通过分析上面代码可知LedService的工作:
    * 加载本地服务的库代码
    * 在构造方法里调用_init本地代码,对Led进行初始化,并调用get_count得到Led灯的个数
    * 为LedDemo应用程序提供两个API:set_on和set_off,这两个API方法实际上也是交给了本地服务代码来操作的
由于Java代码无法直接操作底层硬件,通过JNI方法将具体的操作交给本地底层代码实现,自己只是一个API Provider,即:服务提供者。
让我们来到底层本地代码,先看下底层代码的Android.mk文件:
@ frameworks/Android.mk:

点击(此处)折叠或打开

  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)

  3. LOCAL_MODULE_TAGS := eng
  4. LOCAL_MODULE:= libled_runtime # 编译目标模块
  5. LOCAL_SRC_FILES:= \
  6.        services/jni/com_farsight_LedService.cpp

  7. LOCAL_SHARED_LIBRARIES := \ # 编译时依赖的动态库
  8.        libandroid_runtime \
  9.        libnativehelper \
  10.        libcutils \
  11.        libutils \
  12.        libhardware

  13.  LOCAL_C_INCLUDES += \ #编译时用到的头文件目录
  14.        $(JNI_H_INCLUDE)

  15. LOCAL_PRELINK_MODULE := false # 本目标为非预链接模块
  16. include $(BUILD_SHARED_LIBRARY) # 编译生成共享动态库

    结合前面分析的Android.mk不难看懂这个mk文件。之前的mk文件是编译成Android apk文件,这儿编译成so共享库,所以LOCAL_MODULE和include $(BUILD_SHARED_LIBRARY)与前面mk文件不同,关于Android.mk文件里的变量作用,请查看Android编译系统章节。
    我认为,此处必须为BUILD_SHARED_LIBRARY,不然不能load。总而言之,本地代码编译生成的目标是libled_runtime.so文件。

2.4 本地服务代码
我们来看下本地服务的源码:
@frameworks/services/jni/com_helllo_LedService.cpp:

点击(此处)折叠或打开

  1. #define LOG_TAG "LedService"
  2. #include "utils/Log.h"
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <assert.h>
  7. #include <jni.h>
  8. #include "../../../hardware/led.h"

  9. static led_control_device_t *sLedDevice = 0;
  10. static led_module_t *sLedModule=0;    /*LED硬件对象,该对象继承hw_module_t*/

  11. static jint get_count(void)
  12. {
  13.     LOGI("%sE", __func__);
  14.     if (sLedDevice)
  15.         return sLedDevice->get_led_count(sLedDevice);
  16.     else
  17.         LOGI("sLedDevice is null");
  18.         
  19.     return 0;
  20. }

  21. static jint led_setOn(JNIEnv* env, jobject thiz) {
  22.     LOGI("%sE", __func__);
  23.     if(sLedDevice) {
  24.        sLedDevice->set_on(sLedDevice);
  25.     }else{
  26.        LOGI("sLedDevice is null");
  27.     }
  28.     return 0;
  29. }

  30. static jint led_setOff(JNIEnv* env, jobject thiz) {
  31.     LOGI("%s E", __func__);
  32.      if(sLedDevice) {
  33.         sLedDevice->set_off(sLedDevice);
  34.      }else{
  35.          LOGI("sLedDevice is null");
  36.      }
  37.      return 0;
  38. }

  39. static inline int led_control_open(const struct hw_module_t* module,
  40.     struct led_control_device_t **device)
  41. {
  42.     LOGI("%s E ", __func__);
  43.     return module->methods->open(module, LED_HARDWARE_MODULE_ID, (struct hw_device_t **)device);
  44. }

  45. static jint led_init(JNIEnv *env, jclass clazz)
  46. {
  47.     led_module_tconst * module;
  48.     LOGI("%s E ", __func__);
  49.     
  50.     /*获取LED硬件对象*/
  51.     if(hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0){
  52.         LOGI("get Module OK");
  53.         sLedModule = (led_module_t *) module;
  54.         if(led_control_open(&module->common, &sLedDevice) != 0) {
  55.            LOGI("led_init error");
  56.            return-1;
  57.         }
  58.     }

  59.     LOGI("led_init success");
  60.     return 0;
  61. }

  62. /*
  63. *
  64. * Array ofmethods.
  65. * Each entryhas three fields: the name of the method, the method
  66. * signature,and a pointer to the native implementation.
  67. */
  68. static const JNINativeMethod gMethods[] = {
  69.     {"_init",            "()Z",(void*)led_init},
  70.     {"_set_on",        "()I",(void*)led_setOn },
  71.     {"_set_off",    "()I",(void*)led_setOff },
  72.     {"_get_count",    "()I",(void*)get_count },
  73. };

  74. static int registerMethods(JNIEnv* env) {
  75.    static constchar* const kClassName = "com/hello/LedService";
  76.    jclass clazz;
  77.    
  78.    /* look up the class */
  79.    clazz =env->FindClass(kClassName);
  80.    if (clazz ==NULL) {
  81.       LOGE("Can't find class %s\n", kClassName);
  82.        return-1;
  83.    }

  84.    /* registerall the native methods */
  85.    if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
  86.    {
  87.             LOGE("Failed registering methods for %s\n", kClassName);
  88.             return -1;
  89.    }
  90.    
  91.    /* fill outthe rest of the ID cache */
  92.    return 0;
  93. }

  94. /*
  95. * This iscalled by the VM when the shared library is first loaded.
  96. */
  97. jint JNI_OnLoad(JavaVM *vm, void *reserved) {
  98.     JNIEnv* env= NULL;
  99.     jint result= -1;
  100.     
  101.     LOGI("JNI_OnLoad");
  102.     
  103.     if(vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
  104.         LOGE("ERROR: GetEnv failed\n");
  105.         gotofail;
  106.     }
  107.     
  108.     assert( NULL);
  109.     if(registerMethods(env) != 0) {
  110.      LOGE("ERROR: PlatformLibrary nativeregistration failed\n");
  111.      gotofail;
  112.     }
  113.     
  114.     /* success-- return valid version number */
  115.     result =JNI_VERSION_1_4;
  116.     
  117.     fail:
  118.     return result;
  119. }

    这儿的代码不太容易读,因为里面是JNI的类型和JNI特性的代码,看代码先找入口。
    LedService.java框架代码一加载就调用静态初始化语句块里的System.loadLibrary("led_runtime"),加载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方法接收三个参数:
    * 第一个参数jclass:要注册哪个类里的本地方法映射关系
    * 第二个参数JNINativeMethod *:这是一个本地方法与Java方法映射数组,JNINativeMethod是个结构体,每个元素是一个Java方法到本地方法的映射。

点击(此处)折叠或打开

  1. typedef struct {
  2.          constchar* name;    /*表示Java方法名*/
  3.          constchar* signature;    /*表示方法的签名*/
  4.          void* fnPtr;        /*Java方法对应的本地方法指针*/
  5. } JNINativeMethod;
    * 第三个参数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方法的签名必须一致,返回值不一致不会造成错误。

2.5 现在我们再来回顾下我们的调用调用流程:
    * LedDemo创建了LedService对象
    * LedService类加载时加载了对应的本地服务库,在本地服务库里Dalvik自动调用JNI_OnLoad函数,注册Java方法和本地方法映射关系。根据Java语言特点,当LedDemo对象创建时会调用其构造方法LedService()。

点击(此处)折叠或打开

  1. // 构造方法
  2.     public LedService() {
  3.         int icount ;
  4.         Log.i ("Java Service" , "do init Native Call" );
  5.         _init ();
  6.         icount =_get_count ();
  7.         Log.d ("Java Service" , "led count = " + icount );
  8.         Log.d ("Java Service" , "Init OK " );
  9.     }

    在LedService构造方法里直接调用了本地方法_init和_get_count(通过native保留字声明),也就是说调用了本地服务代码里的jint led_init(JNIEnv *env, jclass clazz)和jint get_count(void)。
    在led_init方法里的内容就是我们前面分析HAL stub框架代码的使用规则了。
    * 通过hw_get_module方法查到到注册为LED_HARDWARE_MODULE_ID,即:”led”的module模块。
    * 通过与led_module关联的open函数指针打开led设备,返回其device_t结构体,保存在本地代码中,有的朋友可能会问,不是本地方法不能持续保存一个引用吗?由于device_t结构是在open设备时通过malloc分配的,只要当前进程不死,该指针一直可用,在这儿本地代码并没有保存Dalvik里的引用,保存的是mallco的分配空间地址,但是在关闭设备时记得要将该地址空间free了,否则就内存泄漏了。
    * 拿到了led设备的device_t结构之后,当LedDemo上的按钮按下时调用LedService对象的set_on和set_off方法,这两个LedService方法直接调用了本地服务代码的对应映射方法,本地方法直接调用使用device_t指向的函数来间接调用驱动操作代码。

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

不用多解释!!

2.6 最后一个Android.mk文件,2号,HAL stub层
@hardware/Android.mk:

点击(此处)折叠或打开

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)

  3. LOCAL_C_INCLUDES += \
  4.          include/

  5. LOCAL_PRELINK_MODULE := false
  6. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
  7. LOCAL_SHARED_LIBRARIES := liblog
  8. LOCAL_SRC_FILES := led.c
  9. LOCAL_MODULE := led.default
  10. include $(BUILD_SHARED_LIBRARY)
Note:
    * 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/目录。

@hardware/led.c:/*大概内容*/

点击(此处)折叠或打开

  1. /*声明LED硬件对象结构体,继承公用的硬件对象*/
  2. struct led_module_t {
  3.     struct hw_module_t common;
  4. };

  5. /*声明LED设备结构体,继承公用的硬件设备,同时新增了自己的成员方法*/
  6. struct led_device_t {
  7.     struct hw_device_t common; // led_devict_t的父结构,它里面只封装了close方法
  8.     // 下面三个函数指针是子结构led_device_t对父结构hw_device_t的扩展,可以理解为子类扩展了父类增加了三个方法
  9.     int (*getcount_led)(struct led_device_t *dev);
  10.     int (*set_on)(struct led_device_t *dev);
  11.     int (*set_off)(struct led_device_t *dev);
  12. };

  13. /*定义了硬件对象里面的open方法*/
  14. static struct hw_module_methods_t led_module_methods = {
  15.     open: led_device_open
  16. };

  17. /*定义LED硬件对象*/
  18. const struct led_module_t HAL_MODULE_INFO_SYM = {
  19.     common: {    // 初始化父结构hw_module_t成员
  20.         tag: HARDWARE_MODULE_TAG,
  21.      version_major: 1,
  22.      version_minor: 0,
  23.      id: LED_HARDWARE_MODULE_ID,
  24.      name: "led HAL Stub",
  25.      author: "farsight",
  26.      methods: &led_module_methods,
  27.     },
  28.     // 扩展属性放在这儿
  29. };

  30. /*定义并实现LED设备扩展的方法*/
  31. static int led_getcount(struct led_control_device_t*dev)
  32. {
  33.          LOGI("led_getcount");
  34.          return 4;
  35. }
  36.  
  37. static int led_set_on(struct led_control_device_t *dev)
  38. {
  39.          LOGI("led_set_on");
  40.          ioctl(fd,GPG3DAT2_ON,NULL);
  41.          return 0;
  42. }
  43.  
  44. static int led_set_off(struct led_control_device_t*dev)
  45. {
  46.          LOGI("led_set_off");
  47.          ioctl(fd,GPG3DAT2_OFF,NULL);
  48.          return 0;
  49. }

  50. /*实现硬件对象里面的open方法,该方法主要是返回硬件设备,里面包含着具体硬件设备的操作方法和属性,
  51. 返回之后,由本地代码中封装使用*/
  52. static int led_device_open(const struct hw_module_t* module, const char* name,
  53.     struct hw_device_t** device)
  54. {
  55.     struct led_device_t *led_device;
  56.     LOGI("%s E ", __func__);
  57.     led_device = (struct led_device_t *)malloc(sizeof(*led_device));
  58.     memset(led_device, 0, sizeof(*led_device));

  59.     // init hw_device_t
  60.     led_device->common.tag= HARDWARE_DEVICE_TAG;
  61.     led_device->common.version = 0;
  62.     led_device->common.module= module;
  63.     led_device->common.close = led_device_close;

  64.     // init operation interface
  65.     led_device->set_on= led_set_on;
  66.     led_device->set_off= led_set_off;
  67.     led_device->get_led_count = led_getcount;
  68.     *device= (struct hw_device_t *)led_device;

  69.     if((fd=open("/dev/leds",O_RDWR))==-1)
  70.     {
  71.         LOGI("open error");
  72.         return -1;
  73.     }else
  74.     LOGI("open ok\n");

  75.     return 0;
  76. }

2.7 深入理解
    我们从进程空间的概念来分析下我们上面写的代码。

    我们前面的示例代码中,将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服务一般向客户端提供服务并且同时可以为多个客户端服务,如下图,服务端可以同时向客户端1和3提供服务,所以我们的示例Led HAL代码不是完美的HAL模型,我们后面章节会再实现一个比较完美的HAL架构。


阅读(979) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~