Android JNI使用总结
最近在做的一个项目需要在i.MX6上和MCU通过uart进行通讯。通讯算是一个JNI的典型应用了,在实现的过程中,遇到了不少问题,这里总结一下。
基本数据类型
对于java中的基本数据类型,在jni中都有对应的数据类型(见下表)。
Java | native | 字节数 |
---|---|---|
boolean | jboolean | 8u |
byte | jbyte | 8 |
char | jchar | 16u |
short | jshort | 16 |
int | jint | 32 |
long | jlong | 64 |
float | jfloat | 32 |
double | jdouble | 64 |
void | void | n/a |
在jni中,jchar、jint等基本数据类型可以和char、int一样用。例如,下面的两句是完全一样的。
jchar* p = (jchar*)malloc(sizeof(jchar) * 2);
char* p = (char*)malloc(sizeof(char) * 2)
需要注意的是,在java中char是16bit,byte是8bit,而在c/c++中char是8bit,没有byte。所以在涉及到char的时候,可能需要位操作。
String字符串
在jni中,与java字符串对应的是jstring。jstring不能像char*那样操作。使用之前需要使用jni中的特定函数转换为char*。举例如下:
char* c = env->GetStringChars(str, 0);
// do something
env->ReleaseStringChars(str, c);
常用的函数如下:
- GetStringChars/GetStringUTFChars
- ReleaseStringChars/ReleaseStringUTFChars
- NewString/newStringUTF
- GetStringLength/GetStringUTFLength
数组对象
在jni中数组的类型为jxxxArray(jintArray、jcharArray……)。和jstring对象一样,数组对象是不能直接访问的,需要借助于jni函数。
获取数组长度
int len = env->GetArrayLength(array);
获取指向数组元素的指针
jint* body = env->GetIntArrayElements(array, 0);
释放数组元素的引用
env->ReleaseIntArrayElements(array, body, 0)
新建数组
jintArray array = env->NewIntArray(len);
设置数组元素的值
env->SetIntArrayRegion(
array, // 目标数组
0, // 源数组的开始元素
len, // 源数组的结束元素
parray // 源数组
);
调用Java对象中的方法
要想调用对象的方法,首先要获取这个对象的类,然后获取类的方法ID,最后再通过实例和方法ID来调用。
- 获取java对象的类
jclass class = env->GetObjectClass(obj);
- 获取MethodID
jmethodId mid = env->GetMethodID(class, "callback", "(I)V");
- 调用方法
env->CallVoidMethod(obj, mid, param);
获取JNIEnv对象
JNIEnv不能为多个线程所公用,但是java虚拟机的JavaVm是整个JVM所公用的。因此可以通过JavaVm来得到当前线程的JNIEnv指针。
JavaVm* gJavaVm;
env->GetJavaVM(&gJavaVM);
JNIEnv* env;
gJavaVm->AttachCurrentThread(&env, NULL);
要注意的是这段代码中几个函数的参数都是双重指针,至于为什么用双重指针,我也不是很清楚。