在我们写Android代码时,调用一个类中没有公开的方法,可以进行反射调用,而JNI开发中C++调用java的方法也是反射调用。下面我们就了解一下通过jni中的c++调用java中的方法。
1.类似于我们java中进行反射调用一下,我们得拿到我们的字节码对象
2.通过字节码对象找到方法对象
3.通过对象调用方法,可以调用空参数方法,也可以调用有参数方法,并且将参数通过调用的方法传入,(这里需要将参数变成java对象后才能传入,比如说如果我们要传递一个char *,那么我们需要把它转化为jstring对象)
基本数据类型对应表
Java类型 | 本地类型 | 描述 |
---|---|---|
boolean | jboolean | C/C++8位整型 |
byte | jbyte | C/C++带符号的8位整型 |
char | jchar | C/C++无符号的16位整型 |
short | jshort | C/C++带符号的16位整型 |
int | jint | C/C++带符号的32位整型 |
long | jlong | C/C++带符号的64位整型 |
float | jfloat | C/C++32位浮点型 |
double | jdouble | C/C++64位浮点型 |
Object | jobject | 任何Java对象,或者没有对应java类型的对象 |
Class | jclass | Class对象 |
String | jstring | 字符串对象 |
Object[] | jobjectArray | 任何对象的数组 |
boolean[] | jbooleanArray | 布尔型数组 |
byte[] | jbyteArray | 比特型数组 |
char[] | jcharArray | 字符型数组 |
short[] | jshortArray | 短整型数组 |
int[] | jintArray | 整型数组 |
long[] | jlongArray | 长整型数组 |
float[] | jfloatArray | 浮点型数组 |
double[] | jdoubleArray | 双浮点型数组 |
下面我们就开始写代码,首先拿class对象
//可以通过传入全类名进行查找,注意这里需要把.替换成/
jclass _jclass = env->FindClass("com/test/ndktestapplication/TestBean");
//也可以在已有的对象中获取class对象
jclass _jclass = env -> GetObjectClass(test_bean);
然后我们通过class对象获取到方法ID,这里我们通过GetMethodID这个方法去获取,这里有三个参数 :第1个参数:Java类对象;第2个参数:方法名;第3个参数:该方法的签名。关于方法签名,下面提供一份表格。
Java类型 | 签名 |
---|---|
boolean | Z |
short | S |
float | F |
byte | B |
int | I |
double | D |
char | C |
long | J |
void | V |
引用类型 | L + 全限定名 + ; |
数组 | [+类型签名 |
//获取构造方法的方法ID,注意构造方法的方法名是"<init>"
jmethodID method_id = env->GetMethodID(_jclass, "<init>","()V");
//获取普通方法的方法ID,注意"()"中是传入参数的参数类型,外面是返回值的类型
jmethodID set_age_id = env->GetMethodID(_jclass,"setAge", "(I)V");
然后调用方法
jclass _jclass = env->FindClass("com/test/ndktestapplication/TestBean");
jmethodID method_id = env->GetMethodID(_jclass, "<init>","()V");
#调用构造方法创建对象
jobject o = env->NewObject(_jclass, method_id);
jmethodID set_name_id = env->GetMethodID(_jclass,"setName", "(Ljava/lang/String;)V");
env->CallVoidMethod(o,set_name_id,name_);
jmethodID set_age_id = env->GetMethodID(_jclass,"setAge", "(I)V");
env->CallVoidMethod(o,set_age_id,age);
env -> ReleaseStringUTFChars(name_,name);
env -> DeleteLocalRef(_jclass);
上面使用的java类
public class TestBean {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
如果java对象是通过java传递过来的,那么我们可以这么使用
jclass _jclass = env -> GetObjectClass(test_bean);
jmethodID get_name_id = env->GetMethodID(_jclass, "getName","()Ljava/lang/String;");
jstring s = static_cast<jstring>(env->CallObjectMethod(test_bean, get_name_id));
const char* name = env->GetStringUTFChars(s,0);
这里需要我们注意的是,所有我们在这里创建的对象,如果不返回java,必须用release掉,否则内存泄露。包括NewStringUTF,NewObject。如果返回java不必release,java会自己回收。
网友评论