Android 中使用 JNI 的一些常用技巧。

1. JNI中的类型对应关系

C/C++类型 Java类型 具体描述 类型署名
unsigned char jboolean unsigned 8 bits Z
signed char jbyte signed 8 bits B
unsigned short jchar unsigned 16 bits C
short jshort signed 16 bits S
long jint signed 32 bits I
long long __int64 jlong signed 64 bits J
float jfloat 32 bits F
double jdouble 64 bits D
void V
int[] jint [] array [I

此外,Java 中的类的类型署名使用的是"L fully-qualified-class ;"形式,例如: Java 中的 java.lang.String ,使用的是"Ljava/lang/String;"名称。

2. C/C++ 中获得 JNIEnv 指针

  • 和 Java 虚拟机在同一线程时,可使用 GetEnv 直接获得 JNIEnv 指针:
JavaVM* vm; // ...
JNIEnv* env = NULL;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_OK) {
    // Do something
}
  • 和 Java 虚拟机不在同一线程时,要使用 AttachCurrentThread 来获得 JNIEnv 指针,然后在使用完后用 DetachCurrentThread 释放:
JavaVM* vm; // ...
if (vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
    // Do something
    vm->DetachCurrentThread();
}

3. C/C++ 中构造 Java 对象

在 C/C++ 中构造 Java 对象需要这么几个步骤:

通过 FindClass 找到要构造的 Class

通过 GetMethodID 在已经找到的 Class 中找到对应的构造函数

通过 NewObject 来创建对象

最后在不需要时利用 DeleteLocalRef 来销毁对象

具体的代码如下:

JNIEnv* env; // ...
jclass jclz= env->FindClass("com/example/MyClass");
jmethodID jmid = env->GetMethodID(jclz, "<init>", "()V");
if (jclz && jmid) {
    jobject obj = env->NewObject(jclz, jmid);
    if (obj != NULL) {
        // Do something
        env->DeleteLocalRef(obj);
    }
}

4. C/C++ 中构造 Java 数组

在 C/C++ 中构造 Java 数组需要这么几个步骤:

通过 NewObjectArray 创建 Java 数组对象

通过 SetObjectArrayElement 来填充数组对象

具体的代码如下:

JNIEnv* env; // ...
int count; // 数组元素数量
jclass jclz; // 数组中的对象
jmethodID jmid; // 数组中对象的构造函数
jobjectArray array = env->NewObjectArray(count, jclz, NULL);
if (array) {
    for (int i = 0; i < count; i++) {
        // 数组元素的对象
        jobject obj = env->NewObject(jclz, jmid);
        // Do something
        env->SetObjectArrayElement(array, i, obj);
        env->DeleteLocalRef(obj);
    }
    env->DeleteLocalRef(array);
}