Java Native Interface(JNI)是Java语言的本地编程接口,通过JNI能直接调用c++实现代码,Android系统也是通过JNI到达Java调用c++的目的。其中JNI的语法并且与c++数据结构之间的转换对应关系这里不解释,大家可以参考其他书籍。
上一章节我们已经在HAL层开发了硬件模块,在JNI层可以获取HAL层的硬件模块来完成对底层驱动的读写操作。
5.1 自定义与实现Jni接口
进入到目录:frameworks/base/services/core/jni 这里可以看到很多JNI实现。
我们可以随便找一个文件,按照上面的流程进行开发。这里可以看到com_android_server_IbankGpioService.cpp实现文件。
#define LOG_TAG "IbankGpioJNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include
#include
#include
#include
GpioDev_device_t* m_pDev = NULL;
namespace android
{
static void WriteDevNode(JNIEnv* env, jobject clazz, jint ptr, jint value) {
// GpioDev_device_t* device = (GpioDev_device_t*)ptr;
GpioDev_device_t* device = m_pDev;
ALOGE("grgDevice WriteDevNode");
if(!device) {
ALOGE("grgDevice gpio is not open.");
return;
}
int val = value;
ALOGE("Set value %d to device gpio.", val);
char path[10]={0};
device->writeGpioNode(device, val,path);
}
static jint ReadDevNode(JNIEnv* env, jobject clazz, jint ptr) {
GpioDev_device_t* device = m_pDev;
ALOGE("grgDevice ReadDevNode");
if(!device) {
ALOGE("Device gpio is not open.");
return 0;
}
int val = -1;
device->readGpioNode(device, &val);
ALOGE("Get value %d from device gpio.", val);
return val;
}
static inline int gpio_device_open(const hw_module_t* module, struct GpioDev_device_t** device) {
return module->methods->open(module, GRGDEV_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
}
static jint gpio_dev_init(JNIEnv* env, jclass clazz) {
GpioDev_module_t* module;
GpioDev_device_t* device;
ALOGE("Initializing HAL stub gpio......");
if(hw_get_module(GRGDEV_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
ALOGI("grg Device found.");
if(gpio_device_open(&(module->common), &device) == 0) {
ALOGI("grg Device gpio is open.");
m_pDev = device;
// return (jint)device;
return (jint)1;
}
ALOGE("Failed to open device gpio.");
return 0;
}
ALOGE("Failed to get HAL stub.");
return 0;
}
//将JNI接口与本地接口进行绑定,要注意数据类型的转换
static const JNINativeMethod method_table[] = {
{"init_native", "()I", (void*)gpio_dev_init},
{"writeDevNode_native", "(II)V", (void*)WriteDevNode},
{"readDevNode_native", "(I)I", (void*)ReadDevNode},
};
//注册JNI代码,并与接口进行绑定com/android/server/GrgIBanking/GrgIBankService
int register_android_server_GpioDevService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/GrgIBanking/GrgIBankService", method_table, NELEM(method_table));
}
};
上面出现的GpioDev_device_t、GpioDev_module_t等数据结构是在HAL层定义的,这里可以通过GRGDEV_HARDWARE_MODULE_ID找到我们实现的HAL模块,在HAL层模块中又可以通过GRGDEV_HARDWARE_DEVICE_ID找到对应的驱动设备。
在上面gpio_dev_init函数中,通过系统函数hw_get_module()找到ID为GRGDEV_HARDWARE_MODULE_ID HAL模块驱动模块,然后调用模块的open函数(这里在HAL层已经对open赋值)。打开成功之后会返回一个GpioDev_device_t驱动设备,通过这个GpioDev_device_t驱动设备我们就可以调用HAL层的读写函数了。
method_table 定义JNI函数接口与本地c++函数实现进行绑定,这里就会涉及到JNI编程标准数据结构转换,大家可以参考网上资料进行转换。定义完成之后通过jniRegisterNativeMethods进行注册。
完成com_android_server_IbankGpioService.cpp文件后,需要在当前目录下找到onload.cpp文件:
frameworks/base/services/core/jni/onload.cpp,并增加以下代码:
定义register_android_server_GpioDevService接口:
在JNI_OnLoad()函数里进行调用。
这个register_android_server_GpioDevService接口实现是在com_android_server_IbankGpioService.cpp里面,也就是我们定义的JNI接口与本地C++实现函数进行绑定的地方,这里应该是说告诉Java JVM定义了哪些JNI接口,不然在java中无法调用这些JNI接口。
5.2 编译Jni接口
在当前目录下frameworks/base/services/core/jni/Android.mk 文件中找到LOCAL_SRC_FILES,按规则添加com_android_server_IbankGpioService.cpp ,这里是告诉编译器需要编译增加的JNI实现文件。
至此,JNI的实现已经开发完成,从这里可以看出我们定义了三个JNI接口gpio_dev_init、WriteDevNode、ReadDevNode并且已经在com_android_server_IbankGpioService.cpp对其实现,也将其编译进入系统,在freamwork层中就可以使用JAVA代码进行调用。