只完成驱动层和HAL层的开发,还不足以让上层APP访问到我们的硬件设备,APP客户端界面基本上都是java语言开发的,而我们前面开发的驱动层和HAL层都是用Native语言(C/C++语言)编写的,如何让上层Java语言能够调用Native语言,这就是JNI (Java Native Interface)技术完成的,当然,Native语言也可以利用JNI技术调用Java层的方法(C/C++语言中称为函数)。经过了JNI这道门,后面就进入Java世界了。
这里进行的JNI层开发以及下一篇将要讲述的Framework层API接口的开发是严格按照android推荐的框架格式来进行的。上层应用也可以单独写自己的JNI层代码来访问硬件设备。
一)编写调用HAL模块的Service文件com_android_server_BreathLedsService.cpp
进入frameworks/base/services/jni/目录,新建com_android_server_BreathLedsService.cpp:
#define LOG_TAG "BreathLedsService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <hardware/hw_breath_leds.h>
#include <hardware/hardware.h>
namespace android {
struct breath_leds_device_t* leds_dev = NULL; //hw_breath_leds.h中定义的HAL设备结构体
static void leds_ctl_open(const struct hw_module_t* module, struct breath_leds_device_t** dev)
{
//调用open函数进行一系列初始化工作
module->methods->open(module, BREATH_LEDS_HW_MODULE_ID, (struct hw_device_t**) dev);
}
static void init_leds(JNIEnv* env, jobject thiz)
{
breath_leds_module_t* leds_module = NULL;
//通过hw_get_module函数查找HAL模块
if (hw_get_module(BREATH_LEDS_HW_MODULE_ID, (const hw_module_t**) &leds_module) == 0)
{
leds_ctl_open(&(leds_module->breath_module), &leds_dev); //装载leds_dev
}
}
static void set_brightness_leds(JNIEnv* env, jobject thiz, jint level)
{
leds_dev->set_breath_value(leds_dev, level);
}
//定义jni函数映射
static const JNINativeMethod method_tab[] = {
{"init_native", "()V", (void*) init_leds},
{"set_brightness_native", "(I)V", (void*) set_brightness_leds},
};
int register_android_server_BreathLedsService(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"com/android/server/BreathLedsService", method_tab, NELEM(method_tab));
}
} /* 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类我们在下篇说。
JNI代码里面一般都会含有一个JNINativeMethod数组类型的JNI函数映射表,以及一个服务注册函数register_android_server_BreathLedsService。这里来看看JNI函数映射表:
static const JNINativeMethod method_tab[] = {
{"init_native", "()V", (void*) init_leds},
{"set_brightness_native", "(I)V", (void*) set_brightness_leds},
};
以{"set_brightness_native", "(I)V", (void*) set_brightness_leds}为例,第三个void指针变量很显然指向的是本文件中的set_brightness_leds函数,而第一个字符串"set_brightness_native"表示的是对应的java语言中的方法名为set_brightness_native,即当java语言中调用set_brightness_native()时,实际执行的是本地的set_brightness_leds函数,set_brightness_leds()是set_brightness_native()的具体实现。在下一篇我们可以看到,set_brightness_native()在java代码中仅仅只是一个声明为没有具体实现。最后来说下 "(I)V" ,这个是JNI规范定义的Java中对应函数的签名信息,由参数类型和返回值类型共同组成。他的格式是:
(参数1类型标示参数2类型标示...参数n类型标示)返回值类型标示
void对应的类型标示为V,int类型对应的类型标示为I,所以"(I)V"表示set_brightness_native()方法含有一个int型参数,没有返回值。更多类型标示可以去百度JNI详细。
而服务注册函数register_android_server_BreathLedsService则将com_android_server_BreathLedsService.cpp与BreathLedsService.java关联起来。这里用的是动态注册方法,还有一种静态注册方法,完全是按照文件名来匹配的,具体应用自查。
二)修改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);
三)修改Android.mk,添加编译路径
打开打开frameworks/base/services/jni/Android.mk,在LOCAL_SRC_FILES变量中添加:
com_android_server_BreathLedsService.cpp \
四)编译