由于项目中的APP都是与硬件交互,所以难免会与Kernel层交互,但上层软件又是有Java语言编写的,怎么和C/C++语言交互呢?这里就使用到了JNI(Java Native Interface)。
Java如何调用JNI:
1.需要在Java中定义本地函数,使用关键字Native。
private native boolean jniopenserial()。
2.需要在Java中声明System.loadLibrary("本地库名称"),去让程序加载本地库。
3.在JNI中声明与Java对应的函数,并实现函数的功能。
static jboolean android_avin_openSerial(JNIEnv *env, jobject thiz){
fdTtyS3 = open(SEND_DEV_NODE, O_RDWR|O_NOCTTY|O_NONBLOCK);
if(fdTtyS3 < 0){
ALOGE("open %s failed,fdTtyS3=%d(%d,%s)\n", SEND_DEV_NODE, fdTtyS3, errno, strerror(errno));
return JNI_FALSE;
}
fdAudio = open(AUDIO_CTRL_NODE, O_RDWR|O_NOCTTY|O_NONBLOCK);
if(fdAudio < 0) {
ALOGE("open %s failed,fdAudio=%d(%d,%s)\n", AUDIO_CTRL_NODE, fdAudio, errno, strerror(errno));
close(fdTtyS3);
fdTtyS3 = -1;
return JNI_FALSE;
}
return JNI_TRUE;
}
4.将JNI本地函数与Java Native声明的函数进行映射
static JNINativeMethod sMethods[] = {
{"jniopenserial","()Z", (void *)android_avin_openSerial},
{"jniAvInSwitch", "(Z)Z", (void *)android_avin_switch},
{"jniDVDSwitch", "(Z)Z", (void *)android_dvd_switch},
{"jniDVDOperation", "(I)Z", (void *)android_dvd_operation},
{"jnicloseserial", "()V", (void *)android_avin_closeSerial},
};
这个数组是什么时候被调用的呢?
首先我们需要知道Java中的System.loadLibray,它一般在类加载的时候或者构造函数之前被调用,经过层层关系,会调用到JNI的JNI_OnLoad函数:
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
ALOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
ALOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
ALOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_6;
bail:
return result;
}
这个函数又会调用:
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
sMethods, sizeof(sMethods) / sizeof(sMethods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
这里我们看到了registerNativeMethods这个函数,并且发现了sMethods数组的身影。
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* methods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, methods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
通过上述调用关系我们可以知道,当加载本地库的时候,JNI和Java声明的本地函数就对应了起来。
JNI调用Java成员变量流程:
1.获取Java类
FindClass
2.获取成员变量的FieldID
GetFieldID
3.获取成员变量
GetXXXField
JNI调用Java对象:
1.获取Java类
FindClass/GetObjectClass
2.获取构造方法ID
GetMethodID
3 生成对象
NewObject
JNI改变Java成员变量值:
和JNI调用Java成员变量流程一样,只不过第三部变为SetXXXField
JNI调用Java方法
1.获取Java类或者对象
FindClass
2.获取方法的ID
GetMethodID
3.调用方法
CallXXXMethod