为服务层实现jni方法的详解
这里说下我们的终极目的:给Android系统增加一个服务来控制开发板上的一个IO口。
花费三个小时的时间写完了mydrv的hal。编写的时候主要还是注意那两个结构体。还有各函数的参数。欢迎查看我的HAL层
https://blog.csdn.net/daoliting5268/article/details/87874521
下面开始写jni,这就写到了native libraries层。不再抄简易手册了,直接开始写mydrv的jni。
下面简述一下我写jni的过程:
1、首先加载头文件,主要有
include <jni.h>
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h> //杂项,大杂烩没找到文件说明,此处猜应该是工具中的乱七八糟的功能函数。
#include <utils/Log.h>
#include <hardware/hardware.h> //硬件抽象层native方法的头文件引入。
#include <hardware/mydrv.h>
写jni时这几个必须要引入的。
然后再namespace android {}中加上jni统一的注册接口函数:
/注册Java本地接口方法/
int register_android_server_mydrvService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, “com/android/server/MydrvService”, method_table, NELEM(method_table));
}
register_android_server_mydrvService //向Android server中注册mydrv的jni接口。
jniRegisterNativeMethods //native方法的注册函数。
此函数中的四个参数
env 当前的环境变量,这里我们不用配置调用者会自动配置,这里的调用者就是Android系统。
“com/android/server/MydrvService” 这里指明是为服务层的哪个类使用。注意只有这里指明的类才可以调用此jni的native方法。
method_table native方法,以一个table的方式注册。
NELEM(method_table) 这个就不用说了,是table的长度。
然后我们就看下这个method_table是怎么写的。
/Java本地接口方法注册/
static const JNINativeMethod method_table[] = {
{“init_native”, “()I”, (void*)mydrv_init},
{“setVal_native”, “(II)V”, (void*)mydrv_setVal},
};
很简单这个结构体数组是JNINativeMethod类型,这个类型是JNI统一形式,必须是此形式。
table中定义了jni中的方法和硬件驱动层的方法关联。
{“setVal_native”, “(II)V”, (void*)mydrv_setVal},
我们来详细解析一下其中的一个元素中的属性。
“setVal_native” 是硬件抽象层中的native方法
“(II)V” 中()内表示jni方法mydrv_setVal传入的参数类型,这里我们就看下mydrv_setVal函数的定义:
static void mydrv_setVal(JNIEnv* env, jobject clazz, jint ptr, jint value)
发现没有函数传入的参数是两个jint的类型,也就是 int类型对应II。同理"(II)V" 中的V就是函数的返回类型。
(void*)mydrv_setVal 这个就是jni中的方法了,java中会调用此方法。
好了目前为止就可以去具体是先jni方法了,想要控制驱动或者通过HAL控制驱动,那就必须要先打开驱动,这里我们看下是怎么打开的。
这里我们是通过HAL层控制的驱动设备,在init_native函数中打开的,
/加载硬件抽象层模块mydrv/
if (hw_get_module(MYDRV_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0)
首先我们要获取硬件抽象层模块,通过MYDRV_HARDWARE_MODULE_ID获取到。MYDRV_HARDWARE_MODULE_ID是在硬件抽象层中定义的,具体可以看我另外要一篇文章《手写HAL总结》。
获得驱动抽象层模块之后,那就要通过HAL来打开驱动了。
/打开mydrv硬件设备/
if (mydrv_device_open(&(module->common), &device) == 0)
这里通过了一个函数封装,来看下函数的具体实现:
static inline int mydrv_device_open(const hw_module_t *module, struct mydrv_device_t ** device)
{
return module->methods->open(module, MYDRV_HARDWARE_MODULE_ID, (struct hw_device_t **)device);
}
就是 module->methods->open(module, MYDRV_HARDWARE_MODULE_ID, (struct hw_device_t **)device);这一个方法。
这里会返回一个device的句柄。然后java拿着jni函数,jni拿着这个句柄就可来操作驱动了。
实现完具体操作后,就要来看下我们是怎么把我们这个jni告诉Android系统的。
熟悉Android启动流程肯定会知道,Android启动服务时会首先onload加载jni,所以我们就在onload.java中注册,打开此文件你会发现好多jni函数在这里调用。
我们类比之前的也在namespace android和JNI_OnLoad中同样的格式写进去。
int register_android_server_mydrvService(JNIEnv *env);
register_android_server_mydrvService(env);
就是把我们jni中注册Java本地接口方法写进去。
ok,到此我们的jni就写完了。下面贴上源码,在framework/base/services/jni/路径下添加了一个com_android_server_mydrvservice.cpp(统一的命名规则)
这里要注意,我们目的是为服务层增加,所以此jni时候在services下的。我还知道一种是在core目录下,我只知道core下的这个是系统内部的,会编译成libruntime.so,在Android启动runtime加载java虚拟机前加载的。
源码:
#define LOG_TAG "MydrvServiceJNI"
#include <jni.h>
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/misc.h> //杂项,大杂烩没找到文件说明,此处猜应该是工具中的乱七八糟的功能函数。
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/mydrv.h>
#include <stdio.h>
namespace android{
static void mydrv_setVal(JNIEnv* env, jobject clazz, jint ptr, jint value)
{
/*将ptr转化为mydrv_device_t结构体变量*/
mydrv_device_t* device = (mydrv_device_t*) ptr;
if (!device)
{
LOGI("device mydrv is not open.");
return;
}
LOGI("set value %d to device mydrv.", value);
device->set_low(device, value);
}
static inline int mydrv_device_open(const hw_module_t *module, struct mydrv_device_t ** device)
{
return module->methods->open(module, MYDRV_HARDWARE_MODULE_ID, (struct hw_device_t **)device);
}
static jint mydrv_init(JNIEnv* env, jclass clazz){
mydrv_module_t* module;
mydrv_device_t* device;
LOGI("Initializing HAL stub mydrv.....");
/*加载硬件抽象层模块mydrv*/
if (hw_get_module(MYDRV_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0)
{
LOGI("device mydrv found.");
/*打开mydrv硬件设备*/
if (mydrv_device_open(&(module->common), &device) == 0)
{
LOGI("device mydrv is open");
/*将mydrv_device_t转换为整形值返回*/
return (jint)device;
}
LOGI("failed to open device mydrv");
return 0;
}
LOGI("failed to get HAL stub mydrv.");
return 0;
}
/*Java本地接口方法注册*/
static const JNINativeMethod method_table[] = {
{"init_native", "()I", (void*)mydrv_init},
{"setVal_native", "(II)V", (void*)mydrv_setVal},
};
/*注册Java本地接口方法*/
int register_android_server_mydrvService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/MydrvService", method_table, NELEM(method_table));
}
};