前言:本文提供在Android系统如何使用jni进行JAVA到C++的调用,再从HAL层回调C++→JAVA
1.2 你需要加载你编写的jni lib库,并声明native方法 Jni_testcallback.java
2.1 在C++你需要实现一个刚刚提到的libtest_jni库文件,首先是一个Android.mk
3.1 如上一步,当注册回调函数被传递给Hal层时,我们创建一个线程,在线程里面去不断获取数据
1. JAVA层
1.1 首先是构造一个编译APP的Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
# Select source code
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
modules_to_install_sky += Test
LOCAL_PACKAGE_NAME := Test
LOCAL_PRIVILEGED_MODULE := true
# Each update should be signed by OEM
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
# 你需要添加依赖你编写的jni库
LOCAL_JNI_SHARED_LIBRARIES := libtest_jni
# Disable dexpreopt
LOCAL_DEX_PREOPT := false
# Disable proguard
LOCAL_PROGUARD_ENABLED := disabled
# Resource directory
# Make sure the generated resource will be added to the apk.
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
# AndroidManifest file
LOCAL_FULL_MANIFEST_FILE := $(LOCAL_PATH)/AndroidManifest.xml
include $(BUILD_PACKAGE)
##################################################
# Use the following include to make our test so.
include $(call all-makefiles-under, $(LOCAL_PATH))
1.2 你需要加载你编写的jni lib库,并声明native方法 Jni_testcallback.java
(可以直接在app添加代码,这是一个简单的demo)
//你应该先调用Jni_testcallback .getObjectClass();
public class Jni_testcallback extends Application {
private static final String TAG = "[APP Jni_testcallback]";
static {
//注意test_jni为你编写的lib库名称,库名称应该为libtest_jni,加载不用补充lib
System.loadLibrary("test_jni");
}
//声明一个native 方法,用于传递jni环境参数
public native void getObjectClass();
//编写一个JAVA层的回调函数,接受底层传递的参数
public void Jni_testcallback(String score, String data) {
//这里可以做你想做的
Log.d(TAG, "APP jni score : " + score);
Log.d(TAG, "APP jni data : " + data);
}
}
}
2. C++层
2.1 在C++你需要实现一个刚刚提到的libtest_jni库文件,首先是一个Android.mk
//每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。
//宏my-dir 则由Build System提供。返回包含Android.mk的目录路径。
LOCAL_PATH:= $(call my-dir)
//在描述每个模块之前,必须声明(重新声明)此变量。
include $(CLEAR_VARS)
//这是jni必须依赖的头文件定义
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
//你的代码
LOCAL_SRC_FILES := \
libtest_jni.cpp
//可能需要依赖的共享库
LOCAL_SHARED_LIBRARIES := \
libhardware \
libbinder \
liblog \
libbinder \
libutils \
libcutils \
libnativehelper \
libandroid_runtime
//可能需要忽略的警告/报错
LOCAL_CFLAGS += -Wno-unused-parameter -Wunused-variable
//lib库名称
LOCAL_MODULE := libtest_jni
//该模块在所有版本下都编译
LOCAL_MODULE_TAGS := optional
//编译
include $(BUILD_SHARED_LIBRARY)
2.2 然后是你的libtest_jni.cpp
如下,
1. JAVA层在加载libtest_jni.so时会调用jni标准函数JNI_OnLoad获取jni的环境变量,注册jni方法getObjectClass
2. 当JAVA层调用getObjectClass时会将携带Testcallback_jni的函数指针注册到Hal层
#include <hardware/test_hal.h> //该头文件为你的hal库头文件
#include <hardware/hardware.h>
#include <utils/Log.h>
#include <iostream>
#include <sstream>
#include <string>
#include <jni.h>
#define LOG_TAG "[test_jni]"
//你在java文件声明的类
#define JAVA_CLASS "com/android/Test/Jni_testcallback"
int sFirstInitFlag = 0;
JavaVM *g_VM = nullptr;
jobject g_obj = nullptr;
test_jni_t* jni_callBack;
test_module_t* test_module = nullptr;
//由HAL层线程回调的jni函数,在这里实现对java方法的获取和调用
void Testcallback_jni(char *score, char *value) {
JNIEnv *env = nullptr;
if (g_VM == NULL) {
ALOGE(LOG_TAG"g_VM == 0");
return;
}
int getEnvStat = (*g_VM).GetEnv((void **) &env,
JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
ALOGW(LOG_TAG"getEnvStat == 0");
if ((*g_VM).AttachCurrentThread(&env, NULL) != 0) {
ALOGE(LOG_TAG"jni thread fail !!");
return;
} else {
ALOGD(LOG_TAG"jni ok");
}
}
//通过全局变量g_obj 获取到要回调的类
jclass javaClass = (*env).GetObjectClass(g_obj);
if (javaClass == 0) {
ALOGE(LOG_TAG"javaclass == 0");
ALOGD(LOG_TAG"DEATACH :%d", g_VM->DetachCurrentThread());
return;
}
//获取要回调的方法ID
jmethodID javaCallbackId = (*env).GetMethodID(
javaClass,//(*env)->GetMethodID(env,jCarplayManager ,
"Jni_testcallback", "(Ljava/lang/String;Ljava/lang/String;)V");
//这里的方法签名需要留意一下
if (javaCallbackId == NULL) {
ALOGE(LOG_TAG"javaCallbackId == 0");
ALOGD(LOG_TAG"DEATACH :%d", g_VM->DetachCurrentThread());
return;
} else {
ALOGD(LOG_TAG"Jni ready ");
}
//执行回调
(*env).CallVoidMethod(g_obj, javaCallbackId,env->NewStringUTF(score), env->NewStringUTF(value));
sFirstInitFlag = 0;
}
//JAVA层声明的native 方法
void getObjectClass(JNIEnv *env, jobject thiz) {
jni_callBack = new test_jni_t();
jni_callBack->testjni_callback_cb = Testcallback_jni;
if (sFirstInitFlag == 0)
{
const hw_module_t* hw_module = nullptr;
env->GetJavaVM(&g_VM);
g_obj = env->NewGlobalRef(thiz);
if (env != NULL && g_obj != NULL) {
ALOGD(LOG_TAG"AUDIO INI OK !!");
}
int err = hw_get_module(TEST_HARDWARE_MODULE_ID, &hw_module);
if (err == 0) {
ALOGE(LOG_TAG" Set test_hal");
if (!hw_module->methods || !hw_module->methods->open) {
test_module = reinterpret_cast<test_module_t*>(
const_cast<hw_module_t*>(hw_module));
} else {
err = hw_module->methods->open(
hw_module, TEST_HARDWARE_MODULE_ID,
reinterpret_cast<hw_device_t**>(test_module));
if (err) {
ALOGE(LOG_TAG" %s: HwBinder failed to load legacy Hal\n",
__func__);
}
}
test_module->TestInit(test_module, "1111");
test_module->Testregistercallbacks(jni_callBack);
} else {
ALOGE(LOG_TAG" %s: hw_get_module (%s) failed: %d\n", __func__, TEST_HARDWARE_MODULE_ID, err);
}
ALOGD(LOG_TAG"--xxxxOpen succeed--");
sFirstInitFlag = 1;
} else {
ALOGD(LOG_TAG"--xxxxOpen error--");
}
ALOGD(LOG_TAG" getjavaclass exit");
}
//映射JAVA方法
static JNINativeMethod sMethods[] = {
{"getObjectClass", "()V", (void *) getObjectClass}
};
static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods,
int numMethods) {
//注册上面实现的的native 方法(getObjectClass)
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGD(LOG_TAG" FIND CLASS FAIL");
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGD(LOG_TAG"REGISTER FAIL");
}
return JNI_TRUE;
}
int register_com_android_test_meacallback(JNIEnv* env) {
int re = registerNativeMethods(env, JAVA_CLASS, sMethods,
sizeof(sMethods) / sizeof(sMethods[0]));
return re;
}
//标准的jni函数,在JAVA层加载该库时会自动执行
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) {
int status;
JNIEnv *env = nullptr;
ALOGD(LOG_TAG" %s: entry!\n", __func__);
if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
status = register_com_android_test_meacallback(env);
if (status < 0) {
ALOGE(LOG_TAG"jni registration failure, status: %d", status);
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
3. HAL (C)
3.1 如上一步,当注册回调函数被传递给Hal层时,我们创建一个线程,在线程里面去不断获取数据
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#define LOG_TAG "[text_hal]"
#include <utils/Log.h>
#include <hardware/test_hal.h>
#include <hardware/hardware.h>
#define UNUSED_ARGUMENT __attribute((unused))
// 全局变量
pthread_t th;
int callback_exit_flag;
static test_jni_t* mCallback;
void get_drv()
{
//做你想做的事情,比如获取某些底层数据
//如果他需要一定时间准备的话
if(1) {//得到数据后调用回调函数
mCallback.testjni_callback_cb(xxx, xxxx);
callback_exit_flag = 1;
}
}
void* callback_thread(void *args)
{
ALOGD(LOG_TAG" %s: Enter!\n", __func__);
callback_exit_flag = 0;
mCallback = (test_jni_t*)args;
while(!callback_exit_flag){
usleep(300000);
get_drv();
}
ALOGD(LOG_TAG" %s: thread exit !\n", __func__);
pthread_exit(NULL);
}
//注册回调函数
void test_register_callbacks(test_jni_t* jni_callback)
{
int ret;
ALOGD(LOG_TAG" %s: Enter!\n", __func__);
if(jni_callback == NULL){
ALOGE(LOG_TAG" %s: jni_callback is NULL!\n", __func__);
return;
}
//将jni回调函数指针传递入线程函数
ret = pthread_create(&th, NULL, callback_thread, jni_callback);
}
//这是必要的hal模块结构体
test_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = TEST_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = TEST_HARDWARE_MODULE_ID,
.name = MODULE_NAME,
.author = MODULE_AUTHOR,
.methods = &test_module_methods,
},
.Testregistercallbacks = test_register_callbacks
};
3.2 Hal层的头文件:test_hal.h
#include <stdbool.h>
#include <sys/cdefs.h>
#include <hardware/hardware.h>
__BEGIN_DECLS
//模块ID
#define TEST_HARDWARE_MODULE_ID "test_hal"
//版本号
#define TEST_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0)
typedef void (*java_Testcallback)(char *score, char *value);
typedef struct {
java_Testcallback testjni_callback_cb;
} test_jni_t;
typedef struct test_module {
void (*testregistercallbacks)(test_jni_t* jni_callback);
} test_module_t;
4. 可能踩到的坑
1. 关于JAVA层对lib库的加载
a) lib库的命名;lib库的名字格式前必须要有lib字段,即在该示例中lib库文件全名应该是libtest_jni.so。
b) lib库的路径;Android系统默认会在system/lib64等路径去查找并加载so。
c) lib库的权限;默认最少需要root:root 、0644的用户分组权限,否则可能存在打开失败的问题。
2. 关于jni使用
a) 在so被加载后,JNI_OnLoad函数会自动执行,当然你也可以不重写它。
b) 本文的示例中,需要映射完JAVA→C++的调用后,APP先调用native方法,至C++层获取jni必须的变量后,在进行C++→JAVA的获取调用。
c) JAVA方法签名,不同的方法类型及参数都有不同的方法签名,编译完成后,可以在out目录查找生成的对应class文件,使用命令查看对应的方法签名 javap -s Jni_testcallback.class
d) 字符串的转化,从遇到的编译报错来看,在执行jni回调函数时发现(*env).CallVoidMethod无法接受String的参数,他需要一个jstring类型,在网上查到了许多方法,
通过jstring jtemp = env->NewStringUTF(temp.c_str());会发现执行到NewStringUTF后会崩溃,
网上的解释大多是NewStringUTF使用完没有释放或者编码格式的异常,本文直接将NewStringUTF构建的jstring传给JAVA层,可以避免这个问题