在实际应用中,除了在JNI层对部分功能进行C++的实现,同时还会有在JNI中对Java函数的调用以实现某种逻辑的联通。
在JNI中回调Java函数,实际上是通过反射机制来实现的,通过反射机制取得目标函数所在的类,以及其名称,通过NDK提供的接口在JNI层进行调用。
package com.test.jni;
public class TestFunction {
public static void testFunc(){
Log.d("tag from Java", "worked!");
}
}
const char[] method_class_from_java = "com/teest/jni/TestFunction";
const char[] method_name_from_java = "testFunc";
jclass cls_str_id = jenv->FindClass(method_from_java);
jmethodID m_Java_TestFunc = jenv->GetStaticMethodID(cls_str_id, method_name_from_java, "()V");
jenv->CallStaticObjectMethod(cls_str_id, m_Java_TestFunc);
通过如上方式就可以实现在JNI中调用Java中函数,具体解释如下:
其中jenv为JNI函数的环境参数,注意在反射获取java函数时其参数及返回值数据类型的签名。
关于NDK中的签名规则如下:
字符签名 | jni中类型 | java中类型 |
---|---|---|
V | void | void |
Z | jboolean | boolean |
I | jint | int |
J | jlong | long |
D | jdouble | double |
F | jfloat | float |
B | jbyte | byte |
C | jchar | char |
S | jshort | short |
而对于数组而言,需要以"["开始,组合以上规则即可,具体对应关系表如下:
字符签名 | jni中类型 | java中类型 |
---|---|---|
[Z | jbooleanArray | boolean[] |
[I | jintArray | int[] |
[J | jlongArray | long[] |
[D | jdoubleArray | double[] |
[F | jfloatArray | float[] |
[B | jbyteArray | byte[] |
[C | jcharArray | char[] |
[S | jshortArray | short[] |
以上均为基本数据类型的签名,对于另外两种情况:
"Landroid/os/FileUtils;"
"Landroid/os/FileUtils$FileStatus;"
讲到这里,基本上十分清楚了,但是有一个特殊情况,细心的同学应该可以发现,以上列表中我们并没有标记String类型。那是因为确实存在一个例外情况一定要当心,那就是String类。在使用其签名时,要使用:
"Ljava/lang/String;"
如果直接使用jstring,那就会找不到。这个例外情况一定要当心。
当Java期待JNI中返回值为String[]时,比如:
//java native API
public native String[] getValues();
//JNI native code
JNIEXPORT jobjectArray JNICALL getValues(JNIEnv *jenv, jobject obj){
jclass stringClass = jenv->FindClass("java/lang/String");
char** user_ids = calling-other-apis //....
jobjectArray pidsArray = jenv->NewObjectArray((jsize) 10, stringClass, nullptr);
for (int i = 0; i < 10; i++) {
jstring userId = jenv->NewStringUTF(user_ids[i]);
jenv->SetObjectArrayElement(pidsArray, i, userId);
jenv->DeleteLocalRef(userId);
}
return pidsArray;
}
从以上的实例中看出,JNI中并没有’jstringArray’的类型与String[]匹配,可以替代使用的则是jobjectArray.
其中最值得关注的是以下两点:
1. 针对String的FindClass,其供反射的关键词为'java/lang/String'
(注意String的签名是'Ljava/lang/String;')
2. 该函数在JNI中注册时签名可以写为: '()[Ljava/lang/String;'
在每个JNI对Java层开发的native函数中,第一第二个参数均是如下形式:
static void JNICALL test (JNIEnv *jenv, jobject obj)