android 是的jni是java与c之间的中介。java通过jni访问c/c++的函数功能。
jni的编写,比较简单,而且有一个模板可以套用。就像写八股文一样,并且不用像写八股文那样要把内容填入很漂亮,而是完成功能即可,没有华丽的外表。
在hal目录下创建framework,并在framework下创建service/jni目录,即
cd hal
mkdir -p framework/jni
在jni目录下创建com_ask_gpio.cpp文件。
头文件:
#include "utils/Log.h" //用于在logcat中可以看到log信息
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <jni.h> //jni相关的函数
#include <gpio.h> //gpio操作的函数及变量(id号),这个头文件其实就是module/gpio/目录下的gpio.h文件
实现gpio置1
static jboolean ask_gpio_set(JNIEnv* env, jobject thiz, jint gpio)
{
LOGI("GPIOService JNI: ask_gpioSet()");
// apc
if (g_gpioDev == NULL)
{
LOGI("GPIOService JNI: not gpio device.");
return -1;
}
else
{
return g_gpioDev->SetGPIO(g_gpioDev, gpio);
}
}
实现gpio清0
static jboolean ask_gpio_clr(JNIEnv* env, jobject thiz, jint gpio)
{
LOGI("GPIOService JNI: ask_gpioClr()");
if (g_gpioDev == NULL)
{
LOGI("GPIOService JNI: not gpio device.");
return -1;
}
else
{
return g_gpioDev->ClrGPIO(g_gpioDev, gpio);
}
}
jni层打开gpio设备,其实就是调用了module/gpio/gpio.c中的gpio_device_open函数
static inline int ask_gpio_open(const struct hw_module_t* module, struct gpio_device_t** device)
{
return module->methods->open(module, GPIO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
给java层调用的接口函数
static const JNINativeMethod gMethods[] = {
{ "_gpio_init", "()Z", (void *)ask_gpio_init },
{ "_gpio_set", "(I)Z", (void *)ask_gpio_set },
{ "_gpio_clr", "(I)Z", (void *)ask_gpio_clr },
};
注:关于这个结构体的填写,涉及到c类型到java类型的转换,android定义了桥接变量,如()z之类的,具体的映射如下
JNINativeMethod的结构体变量成员分别为
const char* name; //Java中函数的名字
const char* signature; //signature,用字符串是描述了函数的参数和返回值
void* fnPtr; //fnPtr是函数指针,指向C函数。
对于signature,内容必须与fnPtr是函数指针的格式一致,即
(1)如果函数fnPtr的指向的函数为void fun(void),那么signature的内容必须为“()”
(2)如果函数fnPtr的指向的函数为int func(int i, int j),那么signature的内容必须为“(II)I”
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V: void <--> void
Z: jboolean <--> boolean
I: jint <--> int
J: jlong long
D: jdouble <--> double
F: jfloat <--> float
B: jbyte <--> byte
C: jchar <--> char
S: jshort <--> short
数组则以"["开始,用两个字符表示
[I: jintArray <--> int[]
[F: jfloatArray <--> float[]
[B: jbyteArray <--> byte[]
[C: jcharArray <--> char[]
[S: jshortArray <--> short[]
[D: jdoubleArray <--> double[]
[J: jlongArray <--> long[]
[Z: jbooleanArray <--> boolean[]
以上为基本类型,如果Java函数的参数是class,则以"L"开头,以";"结尾中间用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject。 一个例外是String类,其对应的类为jstring
Ljava/lang/String; : String <--> jstring
Ljava/net/Socket; : Socket <--> jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
注册访问方法
int register_askhals_server_GpioService(JNIEnv* env) {
static const char* const kClassName = "com/ask/server/GpioService";
jclass clazz;
/* look up the class */
clazz = env->FindClass(kClassName);
if (clazz == NULL)
{
LOGE("Can't find class %s/n", kClassName);
return -1;
}
/* register all the methods */
if (env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
{
LOGE("Failed registering methods for %s/n", kClassName);
return -1;
}
/* fill out the rest of the ID cache */
return 0;
}
其中kClassName必须与编写java代码的service的时涉及到.xml中的name项一致,否则将找不到类名,这个将在下节说明。
加载c模块,调用register_askhals_server_GpioService函数
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("GetEnv failed!");
return result;
}
LOG_ASSERT(env, "Could not retrieve the env!");
register_ctophals_server_GpioService(env);
return JNI_VERSION_1_4;
}
java相关中System.load函数时,就是调用此函数了。
写完cpp文件
那么剩下就是Android.mk文件了。
如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
ASK_MODULE_H_DIR := /home/gium/android_src/vendor/ask/modules/gpio/
LOCAL_SRC_FILES:= /
com_ask_gpio.cpp
LOCAL_C_INCLUDES += /
$(JNI_H_INCLUDE) /
$(ASK_MODULE_H_DIR)
LOCAL_SHARED_LIBRARIES := /
libcutils /
libhardware /
libhardware_legacy /
libnativehelper /
libsystem_server /
libutils /
libui
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS),linux)
ifeq ($(TARGET_ARCH),x86)
LOCAL_LDLIBS += -lpthread -ldl -lrt
endif
endif
endif
ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif
LOCAL_MODULE:= libaskgpio
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
注:ASK_MODULE_H_DIR这个是自己定义的,就是gpio模块所在目录,要根据自己的实际情况而改,目前我的目录为/home/gium/android_src/vendor/ctop/modules/gpio/
这样在文中的包含头文件可以写成include <gpio.h>
下节说明java调用这个jni的内容。
android分层学习笔记(二)
最新推荐文章于 2023-05-26 19:57:02 发布