一、预备知识及说明:
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变量中增加一行,把我们源文件加入到编译列表中:
~/Android$. ./build/envsetup.sh
//用mmm编译工具编译