这次在做NDK开发的时候,有需求需要在JNI直接调用和获取Java中的方法和变量,比如回调,系统信息参数等…
在JNI中调用Java方法,就需要使用FindClass
和GetMethodID
这两个类
使用FindClass和GetMethodID调用Java方法
先上一个简单调用Java方法的例子:
我们有一个数学工具类包名是com.andy.obj
,里面有一个加法的静态方法和一个减法的普通方法,
import com.andy.obj;
public class MathUtil {
public static int add(int a, int b) {
return a+b;
}
public int reduce(int a,int b) {
return a-b;
}
}
首先在JNI中可以通过FindClass
可以找到Java类,得到jclass
,例如:
jclass javaclass = env->FindClass("com/andy/obj/MathUtil");
FindClass
的参数需要传入类的完整包名。
使用GetMethodID
可以获取类的方法,得到jmethodID,例如:
jmethodID jstaticmethodIds = env->GetMethodID(javaclass, "add", "(II)I");
jmethodID jmethodIds = env->GetMethodID(javaclass, "reduce", "(II)I");
其中第四个参数是比较奇怪的字符串,这就是方法签名,现在只需要知道是设置了Java原生方法的两个int类型的参数和一个int类型的返回值,具体用法和作用后面细讲
调用Java方法
//调用静态方法
env->CallStaticObjectMethod(javaclass, jstaticmethodIds, 1,1);
jobject object = env->AllocObject(jclazz);
//调用普通方法
env->.CallIntMethod(javaclass, jmethodIds, 1,1);
上总流程
extern "C"
JNIEXPORT void JNICALL
Java_com_andy_obj_test_add(JNIEnv *env, jobject jobj) {
//得到字节码
jclass javaclass = env->FindClass("com/andy/obj/MathUtil");
//得到普通方法
jmethodID jmethodIds = env->GetMethodID(jclazz,"reduce","(II)I");
//得到静态普通方法
jmethodID jstaticmethodIds = env->GetStaticMethodID(jclazz,"add","(II)I");
//调用静态方法
env->CallStaticObjectMethod(env, javaclass, jstaticmethodIds,1,1);
//调用普通方法需要实例化
jobject object = env->AllocObject(jclazz);
//调用普通方法
env->CallIntMethod(object,jmethodIds,100,1);
}
方法签名
在GetMethodID中
第四个参数()V就是方法签名,Java是支持重载的,所以需要标明方法的传参和返回值,这就是方法的签名。它是用来保证方法的唯一性。其中()代表不传参数,V代表返回值为void。
方法签名对于Java的类型都有一一对应的值。方法签名中用大写的字母对应了java的基本数据类型:
- Z -> boolean
- B -> byte
- C -> char
- S -> short
- I -> int
- J -> long
- F -> float
- D -> double
其实就是有两个比较特殊的:boolean对应的是Z,long对应的J,其他的都是首个字母的大写即可。
数组的表示方法,以[
为标志,一个[
标识一维数组,[[
表示二维数组,例如:
- byte[] -> [B
- int[][] -> [[I
引用类型的表示方法,需要以L开头,以;结束,中间对应类型的包名加类名,例如:
- String -> Ljava/lang/String;
- Object -> Ljava/lang/Object;
自定义类的表示方法类似,比如包名为com.andy.obj,类名为MethodBean的表示方法是:
- com.andy.obj.MethodBean ->Lcom.andy.obj.MethodBean;