android内核字符驱动设备实战之----------运行时库层jni动态库编程(应该是应用框架层)

一、预备知识及说明:

  1、硬件抽象层的设备驱动动态库模块是标准C语言编写的,所以应用层的java程序不能直接调用,所以需要添加符合java规范的jni C++程序动态库。

  2、android系统的java程序要调用jni中的接口方法,必须有一个类要同其对应

  3、jni动态库模块在应用框架层添加,对jni服务提供统一的jni动态库服务

  4、应用框架层jni服务源代码放在/android/frameworks/base/services/jni目录中

  5、输出在/out/target/product/generic/system/lib目录中

  6、根据输出可以看出,该模块源代码虽然在应用框架层,但生成的库是在system/lib目录下,所以,应该属于系统运行库层

二、在jni目录中,创建自己的设备驱动jni接口C++源文件com_android_server_TestService.cpp

根据命名规则,该jni文件提供的接口函数,将由com.android.server这个包里的HelloService这个类调用。

系统内jni调用规范要求:   java调用类要同jni C++函数名要对应,如com.android.server.TestService-->com_android_server_TestService.cpp

       为什么有这个要求呢?

        一种可能是:在eclipse里开发的jni则没有这个要求,可能是因为系统把系统内的所有JNI C++文件打包成一个动态库,也就是很多java类加载一个动态库调用对应的jni C++接口,那么java类怎么知道它要调用那个jni C++文件中的接口函数?文件名同类名一致,则通过文件名就可以知道。 而在eclipse里开发的jni c++动态库,只有一个C++接口源文件(其他非接口的源文件可以有多个),所以,可以名称不对应。

       另一种可能是:eclipse里jni c++文件名没要求对应,但接口函数名则要求对应,每个函数名都要求加调用类的前缀。如:com_android_server_TestService_getval()

com_android_server_TestService.cpp内容如下:   

//日志标签
#define LOG_TAG "TestService"  

//jni相关头文件
#include "jni.h"  
#include "JNIHelp.h"  
   
#include "android_runtime/AndroidRuntime.h"  
#include <utils/misc.h>  
#include <utils/Log.h>  
#include <hardware/hardware.h>    
#include <stdio.h>  

//自己编写的硬件抽象层test库的头文件
#include <hardware/test.h>  
    
namespace android  
{  
   //在硬件抽象层中定义的硬件设备结构体
   struct test_device_t* test_device = NULL;  

   //内部调用函数,用来打开硬件抽象层的设备,相当给硬件设备结构体test_device赋值
   static inline int test_device_open(const hw_module_t* module, struct test_device_t** device) 
   {  
     return module->methods->open(module, TEST_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
   }  
   
   //向上层java类提供的接口函数,初始化打开硬件抽象层模块
   //相当于根据硬件抽象层硬件模块ID(TEST_HARDWARE_MOUDLE_ID)打开设备文件
   static jboolean test_init(JNIEnv* env, jclass clazz) 
   {  
       test_module_t* module;  
        
       //根据硬件抽象层硬件模块ID获取test硬件抽象层硬件模块
       if(hw_get_module(TEST_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) 
       {  
          //打开这个模块对应的硬件设备test_device
          if(test_device_open(&(module->common), &test_device) == 0)
          {  
              return 0;  
          }  
       }  
       return -1;        
   }  
        
   //向上层java类提供的接口函数,写入设备接口函数
   static void test_setVal(JNIEnv* env, jobject clazz, jint value) 
   {  
      int val = value;  
      if(!test_device) 
      {  
           LOGI("Test JNI: device is not open.");  
           return;  
      } 
      //通过调用硬件抽象层的test_device 的写入接口函数写入数据           
      test_device->set_val(test_device, val);  
    }  
   
   //向上层java类提供的接口函数,读取设备接口函数       
   static jint test_getVal(JNIEnv* env, jobject clazz)
   {  
      int val = 0;  
      if(!test_device) {  
           LOGI("Test JNI: device is not open.");  
           return val;  
       }  
      //通过调用硬件抽象层的test_device 的读取接口函数读取设备数据      
      test_device->get_val(test_device, &val);  
      return val;  
    }  
            
    //JNI方法表
    static const JNINativeMethod method_table[] = 
    {  
       {"init_native", "()Z", (void*)test_init},  
       {"setVal_native", "(I)V", (void*)test_setVal},  
       {"getVal_native", "()I", (void*)test_getVal},  
    };  
    
    //定义注册JNI方法 ,该方法有onload.cpp这个文件的JNI_onLoad函数调用,从而实现方法表的注册加载
    int register_android_server_TestService(JNIEnv *env) 
    {  
       return jniRegisterNativeMethods(env, "com/android/server/TestService", 
                                       method_table, NELEM(method_table));  
    }  
};  

程序流程说明:

1、获取HAL模块:根据HAL层的模块ID(TEST_HARDWARE_MODULE_ID),通过系统的获取模块函数(hw_get_module)获取我们自己实现的HAL模块test_module_t

2、通过获得的这个HAL模块test_module_t,打开这个模块对应的 设备test_device_t。

3、定义符合规范的Jni的接口函数,通过这个设备test_device_t在函数内实现访问。

4、定义符合规范的Jni方法表,并定义注册方法。

方法表内容说明:以{"init_native""()Z", (void*)hello_init}这个为例

init_native:是在上层java类里声明的jni本地调用的函数名,即:private static native boolean init_native();

hello_init:是本地的被调用的函数

()Z:这个是两个函数的参数和返回值,参数含义见:

Android JNI 使用的数据结构JNINativeMethod详解

三、在同目录下的onload.cpp文件中,添加调用方法注册函数,实现方法表的加载

//在这里添加函数声明
 namespace android
 {
      ....//系统本身已有的方法注册函数声明
      int register_android_server_TestService(JNIEnv *env);
 };
 //在JNI_onLoad增加register_android_server_TestService函数调用:
 extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
 {
    ......//系统本身已有的方法调用
    register_android_server_TestService(env);
 }
 四. 修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行,把我们源文件加入到编译列表中:

      LOCAL_SRC_FILES:= \
      ....
       com_android_server_TestService.cpp \
      onload.cpp
五. 编译:
     //执行shell命令,获取mmm编译工具

    ~/Android$.  ./build/envsetup.sh

    //用mmm编译工具编译
      ~/Android$  mmm frameworks/base/services/jni
      编译成功后,就可以在/out/target/product/generic/system/lib目录下看到。
     
六. 重新打包Android系统镜像system.img:
      /Android$ make snod
      重新打包后,system.img就包含更新过的libandroid_servers.so模块。
    
  


你可能感兴趣的:(android内核字符驱动设备实战之----------运行时库层jni动态库编程(应该是应用框架层))