在实际应用中,除了在JNI层对部分功能进行C++的实现,同时还会有在JNI中对Java函数的调用以实现某种逻辑的联通。
在JNI中回调Java函数,实际上是通过反射机制来实现的,通过反射机制取得目标函数所在的类,以及其名称,通过NDK提供的接口在JNI层进行调用。
JNI中调用Java函数的栗子
- TestFunction.java
package com.test.jni;
public class TestFunction {
public static void testFunc(){
Log.d("tag from Java", "worked!");
}
}
- TestFunctionJNI.cpp
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中函数,具体解释如下:
- 通过反射获取函数所在类的jclass;
- 通过反射获取目标函数的id;
- 通过NDK提供的接口实现调用;
其中jenv为JNI函数的环境参数,注意在反射获取java函数时其参数及返回值数据类型的签名。
NDK中数据类型签名规则
关于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[] |
而对于数组而言,需要以"["开始,组合以上规则即可,具体对应关系表如下:
字符签名 | 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[] |
以上均为基本数据类型的签名,对于另外两种情况:
- Java类(包括自定义类)
对于参数或者返回值类型是Java类的情况,需要以"L"开头,并且以";"结束,并且以"/"隔开包名路径,并且此时在JNI中其对应接收的类型为jobject, 而对于基本数据类型,则使用其对应的NDK中的数据类型表示即可。比如:
"Landroid/os/FileUtils;"
- Java内部类
对于参数或者返回值类型是Java类中的内部类的情况,则需要在以上基础结合"$"索引,比如:
"Landroid/os/FileUtils$FileStatus;"
讲到这里,基本上十分清楚了,但是有一个特殊情况,细心的同学应该可以发现,以上列表中我们并没有标记String类型。那是因为确实存在一个例外情况一定要当心,那就是String类。在使用其签名时,要使用:
"Ljava/lang/String;"
如果直接使用jstring,那就会找不到。这个例外情况一定要当心。
JNI中的 JNIEnv 和 jobject
在每个JNI对Java层开发的native函数中,第一第二个参数均是如下形式:
static void JNICALL test (JNIEnv *jenv, jobject obj)
- JNIEnv
该参数代表Java环境,通过这个环境可以调用Java中的函数,这些函数可以在jni.h中查到,通过这些函数可以实现Java与JNI层的交互,通过JNIEnv调用JNI函数可以访问java虚拟机,操作java对象;
JNIEnv在在当前的线程有效,JNIEnv不能跨线程传递,相同的Java线程调用本地方法所使用的JNIEnv是相同的,一个native函数不能被不同的Java线程调用;该JNIEnv只想一个线程相关的结构,想成相关结构只想一个指针数组,指针数组中的内阁元素最终就会指向某一个JNI函数; - jobject
该参数代表调用jni函数的Java类或者对象,如果native方法是非静态的,那么这个参数就是对Java对象的引用,如果native函数是静态的,那么这个参数就是对Java类的class对象的引用;
CSDN同步发布地址
网友评论