前言
前面我们已经介绍了native函数访问Java的属性,如果对此不是很了解的话,可以看博客Android Studio NDK开发(三):属性访问
本篇博客将介绍native函数访问Java的方法
访问Java非静态方法
首先在MainActivity(可自定义,可以是Java类)中定义一个方法,也就是native函数访问的Java非静态方法
//产生指定范围的随机数
public int getRandomInt(int value) {
System.out.println("getRandomInt 执行了");
return new Random().nextInt(value);
}
然后在MainActivity中再定义一个native方法,此方法的native函数实现就是去调用上面的getRandomInt,来实现native函数访问Java非静态方法
public native void accessMethod();
在onCreate中调用
accessStaticMethod();
至于JNI的配置信息,这里就不做赘述,不明白的,可以看我之前的博客,或者可以下载项目地址,查看代码。接下来就是native函数实现了:
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessMethod(JNIEnv *env, jobject jobj) {
//得到jclass
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到jmethodID
jmethodID jmid = (*env)->GetMethodID(env, jcla, "getRandomInt", "(I)I");
//调用java方法获取返回值,第四个参数100表示传入到java方法中的值
jint jRandom = (*env)->CallIntMethod(env, jobj, jmid, 100);
//可以在Android Studio中Logcat显示,需要定义头文件#include <android/log.h>
__android_log_print(ANDROID_LOG_DEBUG, "system.out", "Random:%ld", jRandom);
}
运行之后,打印的结果为:
getRandomInt 执行了
Random:56
访问Java中静态方法
定义一个静态方法
//产生UUID字符串
public static String getUUID() {
return UUID.randomUUID().toString();
}
再定义一个native方法
public native void accessStaticMethod();
在onCreate中调用
accessStaticMethod();
native函数的实现
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessStaticMethod(JNIEnv *env, jobject jobj) {
jclass jcla = (*env)->GetObjectClass(env, jobj);
jmethodID jmid = (*env)->GetStaticMethodID(env, jcla, "getUUID", "()Ljava/lang/String;");
jstring uuid = (*env)->CallStaticObjectMethod(env, jcla, jmid);
char* uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
__android_log_print(ANDROID_LOG_DEBUG, "system.out", "uuid_str:%ld", uuid_str);
}
运行打印:
uuid_str:368008644224
访问Java中构造方法
我们来实现访问Java中的Date构造方法,实例化Date对象,再利用这个对象调用getTime产生一个时间戳,并打印其值。
定义一个native方法
public native void accessConstructor();
在MainActivity的onCreate中调用
accessConstructor();
native函数的实现
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessConstructor(JNIEnv *env, jobject jobj) {
//得到类Date对应的jclass
jclass jcla = (*env)->FindClass(env, "java/util/Date");
//构造方法对应的都是<init>
//在任意位置打开命令行,输入javap -s -p java.util.Date可以看到空参构造的签名是()V
jmethodID jmid = (*env)->GetMethodID(env, jcla, "<init>", "()V");
//实例化Date对象
jobject jDate = (*env)->NewObject(env, jcla, jmid);
//下面需要调用的是getTime,对应这里第三个参数传入getTime,第四个参数为其签名
jmethodID jTimeMid = (*env)->GetMethodID(env, jcla, "getTime", "()J");
//调用getTime方法得到返回值jlong
jlong jtime = (*env)->CallLongMethod(env, jDate, jTimeMid);
__android_log_print(ANDROID_LOG_DEBUG, "system.out", "jtime:%ld", jtime);
}
运行打印结果:
jtime:1510550749157
访问Java中父类的方法
首先我们需要有一个父类和一个子类,子类重写了父类的方法,创建一个Animal类,并创建其子类Cat类
public class Animal {
public void eat() {
System.out.println("动物吃肉...");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
}
在MainActivity中创建native方法
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessNonvirtualMethod(JNIEnv *env, jobject jobj) {
//得到MainActivity对应的jclass
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到animal属性对应的jfieldID
jfieldID jfid = (*env)->GetFieldID(env, jcla, "animal", "Lcom/zhangpan/myjnicmake/Animal;"); //这里必须是父类对象的签名,否则会报NoSuchFieldError,因为Java中是父类引用指向子类对象
//得到animal属性对应的jobject
jobject animalObj = (*env)->GetObjectField(env, jobj, jfid);
// jclass animalCla =(*env)->GetObjectClass(env, animalObj); //这种方式,下面调用CallNonvirtualVoidMethod会执行子类的方法
//找到Animal对应的jclass
jclass animalCla = (*env)->FindClass(env, "com/zhangpan/myjnicmake/Animal"); //如果这里写成子类的全类名,下面调用CallNonvirtualVoidMethod会执行子类的方法
//得到eat对应的jmethodID
jmethodID eatID = (*env)->GetMethodID(env, animalCla, "eat", "()V");
// (*env)->CallVoidMethod(env, animalCla, eatID); //这样调用会报错
//调用父类的方法
(*env)->CallNonvirtualVoidMethod(env, animalObj, animalCla, eatID); //输出父类的方法
}
运行打印结果:
动物吃肉...
项目地址
https://github.com/fsrmeng/MyJniCmake-Master
展望
上面我们总结了native函数访问Java的各类方法,下篇博客我将带大家介绍NDK开发中的同步的问题,敬请期待!
网友评论