上一篇里面主要是JNI中一些函数的介绍,这篇博客就举个例子来说明这些函数的使用方法。
项目介绍
这个例子来源于我实际的项目。这段代码的作用是:
- 通过Uart发送消息;
- 接受Uart传送过来的消息;
因为这里的主要目的不是说明如何使用uart,所以我的这段代码中发送是通过调用下面的方法实现的:
int uart_send(
message *callmsg, // 待发送的消息
message *retmsg, // 发送消息之后的返回值
unsigned timeout // 发送超时
);
接受是通过注册回调函数实现的。当uart处理机接受到消息之后,将调用我的回调函数。回调函数将消息处理后返回给java层的代码。
- 声明几个宏。
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className);
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
这几个宏是我在framework/base/service/jni中找到的,挺好用的。
- 声明全局变量。
static struct
{
jmethodID recvMsgFromNative; // 这里将存储java类UartManagerService中的方法recvMsgFromNative的ID。
} gUartManagerServiceClassInfo;
static jobject gUartManagerServiceObj; // 这个对象将存储UartManagerService对象的实例。
static jclass gUartManagerServiceClass; // 这个东西将存储UartManagerService的class。
static JavaVM * gJavaVM; // 这个就是我在上一遍博文的最后提到的全局的JavaVM对象,在必要的时候,将通过这个对象获取JNIEnv指针。
- 声明native方法。
static JNINativeMethod gUartManagerServiceMethods[] =
{
/* name, signature, funcPt */
{ "nativeTest", "()V", (void*) nativeTest },
{ "nativeInit", "()V", (void*) nativeInit }
};
- 注册native方法。
int register_android_server_iflytek_uartmanagerservice(JNIEnv* env)
{
int res = jniRegisterNativeMethods(env,
"com/android/server/iflytek/UartManagerService",
gUartManagerServiceMethods, NELEM(gUartManagerServiceMethods) );
if(res < 0)
ALOGD("Unable to register native methods.");
env->GetJavaVM(&gJavaVM);
return 0;
}
- 本地初始化。
static void nativeInit(JNIEnv* env, jobject obj)
{
ALOGD("nativeInit() is called");
// 获取UartManagerService对象的实例
gUartManagerServiceObj = env->NewGlobalRef(obj);
// Callbacks
FIND_CLASS(gUartManagerServiceClass, "com/android/server/iflytek/UartManagerService");
GET_METHOD_ID(gUartManagerServiceClassInfo.recvMsgFromNative, gUartManagerServiceClass,
"recvMsgFromNative", "([I)V");
// 注册回调
set_callback(get_msg_cb);
}
这个函数时在UartManagerService里调用的,所以刚好可以用来获取UartManagerService的实例。
- 实现发送代码。
static void nativeTest(JNIEnv* env, jobject obj)
{
ALOGD("nativeTest() is called");
message callmsg;
message retmsg;
// 循环发送1000次
for (int i = 0; i < 1000; ++ i)
{
// 填充callmsg
// ……
//
uart_send(&callmsg, &retmsg, 200);
}
}
- 实现接收的回调。
回调函数的参数是message结构体,而java中的回调函数的参数是int数组,所以这里要新建数组并填内容。
void get_msg_cb(message *evtmsg)
{
ALOGD("get_msg_cb() is called");
int i;
JNIEnv* env;
gJavaVM->AttachCurrentThread(&env, NULL); // 这里就是上一篇博文的最后提到的“在需要的时候”。因为这里没有JNIEnv指针,所以只能从gJavaVM中重新获取。
if(env == NULL){
ALOGD("JNIEnv is null");
return;
}
jintArray returnArray = env->NewIntArray(len);
int * pArray = (int*)malloc(sizeof(int) * len);
// 给本地数组赋值
pArray[0] = ……;
pArray[1] = ……;
for(i = 0; i < len; i++)
{
pArray[2+i] = evtmsg->data[i];
}
// 将值赋给返回值
env->SetIntArrayRegion(returnArray, 0, len + 2, pArray);
// 调用java中的函数
if(gUartManagerServiceObj != NULL){
env->CallVoidMethod(gUartManagerServiceObj, gUartManagerServiceClassInfo.recvMsgFromNative, returnArray);
}
// 释放内存
free(pArray);
}