MT6572平台加入呼吸灯功能——编写JNI

    前面我们完成了驱动和HAL层的开发,然而仅仅这些还不足以让上层APP访问到我们的硬件设备,APP客户端界面基本上都是java语言开发的,而我们前面开发的驱动层和HAL层都是用Native语言(C/C++语言)编写的,如何让上层Java语言能够调用Native语言,这就是JNI 技术完成的。

    JNI是Java Native Interface的缩写,中文译为“Java本地调用”。JNI层的代码使用Native语言写的,通过JNI技术可以实现:Java程序中的函数可以调用Native语言写的函数,Native一般指C/C++语言编写的程序;Native程序中的函数可以调用Java层的函数。

    由于Java世界要保持它的平台无关性,所以一些硬件相关的操作就需要在Native世界实现(这部分操作在JNI层实现),这里就出现了一个问题:Java世界调用的函数是怎么与Native世界的函数关联起来的呢?如本实验中,Java类BreathLedsService.class中有两个方法是在Native世界实现的,如下:


 
 
  1. private native void init_native();
  2. private native void set_brightness_native(int data);
使用native关键字修饰的方法表示该方法实体是用Native语言实现的,在Java语言中可直接调用,如:


 
 
  1. public void turnOnLeds() {
  2. set_brightness_native( 0x80);
  3. }
这两个方法在JNI层的函数实体如下:


 
 
  1. static void init_leds(JNIEnv* env, jobject thiz)
  2. {
  3. breath_leds_module_t* leds_module = NULL;
  4. //通过hw_get_module函数查找HAL模块
  5. if (hw_get_module(BREATH_LEDS_HW_MODULE_ID, ( const hw_module_t**) &leds_module) == 0)
  6. {
  7. leds_ctl_open(&(leds_module->breath_module), &leds_dev); //装载leds_dev
  8. }
  9. }

 
 
  1. static void set_brightness_leds(JNIEnv* env, jobject thiz, jint level)
  2. {
  3. leds_dev->set_breath_value(leds_dev, level);
  4. }
系统怎么知道init_leds函数是init_native的实现呢?

    这其实就是JNI函数的注册问题了,JNI函数注册有两种方式:静态注册和动态注册。静态注册完全依靠名字寻找的,这种注册方法比较简单但是效率不高而且JNI层函数名特别长,写错一个字母就会导致关联出错。这里讲下动态注册,本实验就是使用动态注册的。

    既然Java native函数和JNI函数一一对应,理所当然会有一个结构来保存这种对应关系。在JNI技术中,用来记录这种一一对应关系的,是一个叫JNINativeMethod的结构,原型如下:


 
 
  1. typedef struct {
  2. /** Java中native函数的名字,如上面的"init_native" */
  3. const char* name;
  4. /** Java函数的签名信息,后面会说到 */
  5. const char* signature;
  6. /** JNI层对应函数的函数指针,必须是void*类型 */
  7. void* fnPtr;
  8. } JNINativeMethod;
如何使用该结构体呢?如本例中使用如下:


 
 
  1. static const JNINativeMethod method_tab[] = {
  2. { "init_native", "()V", ( void*) init_leds},
  3. { "set_brightness_native", "(I)V", ( void*) set_brightness_leds},
  4. };
另外AndroidRunTime类提供了一个registerNativeMethods函数来完成注册工作,使用如下:


 
 
  1. int register_android_server_BreathLedsService(JNIEnv *env)
  2. {
  3. return AndroidRuntime::registerNativeMethods(env,
  4. "com/android/server/BreathLedsService", method_tab, NELEM(method_tab));
  5. }
其中第二个参数表明是这些函数在Java中的哪个类中使用。这里只是注册的实现,那么注册工作在哪调用呢?当Java层通过System.loadLibrary加载完JNI动态库后,紧接着会查找该库中一个叫JNI_Onload的函数,如果有,就调用它,而动态注册的工作就是在这里完成的。可以自己实现JNI_Onload函数,这里因为和整个系统一起编译,所以在公共加载的地方进行添加:将register_android_server_BreathLedsService函数添加进了frameworks/base/services/jni/onload.cpp文件中的JNI_OnLoad函数中。

    如上就完成的动态注册。下面来说下Java函数的签名信息,估计很多刚接触JNI的人看到上面的"()V"和"(I)V"会非常纳闷,完全不明所以。为什么要使用签名信息呢?因为Java支持函数重载,即可同名但不同参,所以仅仅根据函数名是没法找到具体函数的,Java签名信息的出现就是为了解决这个问题。JNI规范定义的函数签名信息看起来比较别扭,格式如下:

            (参数1类型标示参数2类型标示...参数n类型标示)返回值类型标示

    类型标示示意表如下:

类型标示Java类型类型标示Java类型
VvoidJlong
ZbooleanFfloat
BbyteDdouble
CcharL/java/lang/String;String
Sshort[Iint[]
Iint[L/java/lang/object;Object[]
注意,当参数类型是引用类型时,其格式是"L包名;",其中包名中的"."换成“/”,引用类型标示最后都有一个";";如果是数组类型,则标示中会有一个"["。

    另外,关于JNI知识还有不少内容,如Java数据类型与Native类型的转换(Java中的int类型在JNI层对应转换成jint等),JNIEnv结构体中提供的一系列JNI系统函数的使用等,这里就不细说了,网上有很多资料,当然如果想追求深入,研究源代码是不二选择。下面记录下本实验JNI层开发过程。

    1)编写调用HAL模块的Service文件com_android_server_BreathLedsService.cpp

        进入frameworks/base/services/jni/目录,新建com_android_server_BreathLedsService.cpp:


 
 
  1. #define LOG_TAG "BreathLedsService"
  2. #include "jni.h"
  3. #include "JNIHelp.h"
  4. #include "android_runtime/AndroidRuntime.h"
  5. #include <hardware/hw_breath_leds.h>
  6. #include <hardware/hardware.h>
  7. namespace android {
  8. struct breath_leds_device_t* leds_dev = NULL; //hw_breath_leds.h中定义的HAL设备结构体
  9. static void leds_ctl_open(const struct hw_module_t* module, struct breath_leds_device_t** dev)
  10. {
  11. //调用open函数进行一系列初始化工作
  12. module->methods->open( module, BREATH_LEDS_HW_MODULE_ID, (struct hw_device_t**) dev);
  13. }
  14. static void init_leds(JNIEnv* env, jobject thiz)
  15. {
  16. breath_leds_module_t* leds_module = NULL;
  17. //通过hw_get_module函数查找HAL模块
  18. if (hw_get_module(BREATH_LEDS_HW_MODULE_ID, ( const hw_module_t**) &leds_module) == 0)
  19. {
  20. leds_ctl_open(&(leds_module->breath_module), &leds_dev); //装载leds_dev
  21. }
  22. }
  23. static void set_brightness_leds(JNIEnv* env, jobject thiz, jint level)
  24. {
  25. leds_dev->set_breath_value(leds_dev, level);
  26. }
  27. //定义jni函数映射
  28. static const JNINativeMethod method_tab[] = {
  29. { "init_native", "()V", ( void*) init_leds},
  30. { "set_brightness_native", "(I)V", ( void*) set_brightness_leds},
  31. };
  32. int register_android_server_BreathLedsService(JNIEnv *env)
  33. {
  34. return AndroidRuntime::registerNativeMethods(env,
  35. "com/android/server/BreathLedsService", method_tab, NELEM(method_tab));
  36. }
  37. } /* namespace android */
调用HAL模块涉及到一个非常重要的hw_get_module函数,基本上,JNI层就是靠该函数与HAL模块产生联系,该函数可通过HAL模块.h中定义的模块ID宏找到HAL模块,并得到hw_module_t结构体,然后调用hw_module_t.hw_module_methods_t.open函数来初始化驱动。除此之外,该文件还实现了开放给Java层的接口实现set_brightness_leds。

    另外注意文件的命名方法,com_android_server前缀表示包名,表示服务类BreathLedsService放在frameworks/base/services/java目录下的com/android/server/目录下,即存在一个com.android.server.BreathLedsService类,实际上我们可以将com_android_server_BreathLedsService.cpp和下篇将要讲到的BreathLedsService.java看成一个文件,com_android_server_BreathLedsService.cpp中主要实现了BreathLedsService.java中没有实现的内容(需要使用Native语言实现)。关于BreathLedsService类我们在下篇说。

    2)修改onload.cpp,使系统启动时能自动加载上述服务

        打开frameworks/base/services/jni/onload.cpp:

        1,在namespace android { }中加入函数声明:

int register_android_server_BreathLedsService(JNIEnv* env);
 
 
        2,在extern "C" jint JNI_OnLoad(){ }中添加函数调用:

register_android_server_BreathLedsService(env);
 
 
    3)修改Android.mk,添加编译路径

        打开打开frameworks/base/services/jni/Android.mk,在LOCAL_SRC_FILES变量中添加:

com_android_server_BreathLedsService.cpp \
然后编译即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值