上一篇中,咱们主要学习了Java调用本地方法,并列举了两大特殊实例来例证咱们的论据,还没学习的伙伴必须先去阅读下,本次的学习是直接在上一篇的基础上进行了。点击:Android NDK开发之从Java与C互调中详解JNI使用(一)html
本篇咱们主要学习如何从C源码中调用Java代码,以及使用gradle-experimental来调试原生代码。java
这里定义了两个普通成员变量和一个静态成员变量。python
就像C不能直接使用Java的引用类型同样,C也不能直接的访问Java成员变量,而是经过JNI所封装的API来调用Java成员。一般会有以下的步骤:android
1:获取java实例对象的引用
2:经过实例对象获取java成员变量ID
3:经过变量ID获取java成员变量数组
那么咱们如今分步的讲下学习以上的步骤。微信
获取实例对象的引用JNI已为咱们封装好了方法,咱们可使用GetObjectClass函数来获取class对象:
jclass (GetObjectClass)(JNIEnv, jobject);app
例如:函数
jclass class = (*env)->GetObjectClass(env, jobj);
jobj对象咱们在上一节也讲过,这个是Java调用本地方法时,JNI会封装调用类的一个实例,在这里就是Java2CJNI类的引用。性能
另一种方法也是能够获取到class对象,就是经过反射机制来获取对象:
jclass (FindClass)(JNIEnv, const char*);学习
例如:
jclass class = (*env)->FindClass(env, "com/sanhui/ndkdemo/Java2CJNI");
同上面的方法同样,均可以获取Java对象引用。
由第一步咱们获取到了实例对象的引用,那么咱们能够经过JNI封装的方法来获取实例内的变量ID:
①:获取普通成员变量ID
经过jfieldID (GetFieldID)(JNIEnv, jclass, const char, const char);能够获取到一个jfieldID 类型的ID。
GetFieldID函数中第三个参数是Java类中的成员变量的名称,若是在Java2CJNI类中定义的成员private String codeError = "验证码错误 !"中codeError 。第四个参数是变量签名,说白点就是Java类中成员变量的返回类型,如变量codeError 的返回类型是String ,可是String在原生代码中属于引用类型,不能直接识别,因此在JNI中有相应的签名映射,以下表:
Java 类型 | JNI 签名映射 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
fully-qualified-class | Lfully-qualified-class ; |
type[] | [type |
method type | ( arg-types ) ret-type |
上述中基本数据类型的签名多以大写类型首字母为主,可是引用类型是使用“L”+ 类型路径 + “;”,如String类型则是“Ljava/lang/String;”,数组则是"[" + 类型,如“[I”表示整形数组,若是是Java方法则是“(参数的类型) + 返回值类型” 。
ok,经过GetFieldID获取成员变量ID,如:
jfieldID codeErrorID = (*env)->GetFieldID(env,jclazz,"codeError","Ljava/lang/String;");
②:获取静态成员变量ID
成员变量分为普通和静态变量,那获取静态变量该如何呢?JNI也为咱们封装好了方法:
jfieldID (GetStaticFieldID)(JNIEnv, jclass, const char,
const char);
经过GetStaticFieldID函数能够获取到静态变量ID,参数如①同样。举例:
jfieldID loginSuccID = (*env)->GetStaticFieldID(env,jclazz,"loginSucc","Ljava/lang/String;");
ok,获取完变量ID,咱们就能够经过ID来取得变量了,这里获取成员变量也是分为静态和普通,分别使用:
jobject (GetObjectField)(JNIEnv, jobject, jfieldID);和jobject (GetStaticObjectField)(JNIEnv, jclass, jfieldID);函数。
例如:
jstring jcodeError = (*env)->GetObjectField(env,jobj,codeErrorID); jstring juserNameError = (*env)->GetObjectField(env,jobj,userNameErrorID); jstring jloginSucc = (*env)->GetStaticObjectField(env,jclazz,loginSuccID);
ok,到这里咱们就获取到了Java类中的成员变量,来看下总体代码:
获取到Java中的成员变量,而后返回到Java中,而后咱们经过Toast给打印出来:
来看下执行结果,是否如咱们多料想的同样:
从上图中咱们看到的执行结果显然和咱们在Java2CJNI中定义的loginSucc成员变量是同样的,由此能够得出结论就是C成功的调用了Java类中的变量。
注意:因为获取Java类中的变量须要在原生代码中调用几个方法才能获取到最终的结果,对于性能来讲要求的开销过大,因此建议通常不这样直接轰C中调用Java的成员变量,若是有须要建议以参数的形式传递给原生代码。
C调用Java方法和调用成员变量基本是同样的,首先咱们如今Java类中定义一个方法,用Toast来显示信息,如:
上面也说过C调用Java方法和变量步骤基本同样,下面来看下基本步骤:
1:获取java实例对象的引用
2:经过实例对象获取实例方法ID
3:经过方法ID调用实际的Java方法
这一步和C获取变量所介绍的获取方式是同样的,都是经过GetObjectClass或是FindClass函数来获取的,这里就再也不赘述,能够参考上面的实力。
java中方法分为两类,一类是普通的方法,一类是静态方法。下面来逐一的介绍。
①:获取普通方法ID:
能够经过jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);来获取方法ID,这也是JNI已经封装好的原生方法,来解释下这个函数:
GetMethodID函数前两个参数就没必要多介绍了,其中第三个参数是Java类中的方法名称,对应的是Java2CJNI类中定义的方法:public void showMessage(String message){}中的showMessage。第四个参数是方法签名,也就是Java类中方法的返回类型,至于什么是签名上面已介绍清楚。
获取方法ID实例:
jmethodID showMessage = (*env)->GetMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V");
这里和变量惟一不一样的是,方法有可能带参数,那么签名就须要带上参数签名和返回值签名,也就是在()里的是参数签名,()外的是返回值签名,如“(Ljava/lang/String;)V”表示是含有一个String类型的参数和一个void的无返回类型。
②:获取静态方法ID:
获取静态方法ID会使用JNI的 jmethodID (GetStaticMethodID)(JNIEnv, jclass, const char, const char);函数,它的使用和参数与GetMethodID同样,并无什么差异。
例如:
jmethodID showMessage = (*env)->GetStaticMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V")
获取到方法ID后,咱们能够经过JNI提供的回调函数来真正的调用Java方法,这里也是分为回调普通方法和静态方法,因为二者基本没什么差异,咱们这里就只讲下普通方法的回到。
C回调Java方法会使用Call< type >Method函数来回调实际的方法,例如,咱们调用咱们显示Toast的无返回值方法:
(*env)->CallVoidMethod(env,jobj,showMessage,jloginSucc);
直接的调用CallVoidMethod,它第三个参数传入的是jmethodID类型的方法ID,由以前获取到的,第四个参数是要传递给Java的参数,这里接受的是一个String累的字符串。
ok,经过上面的三个步骤,咱们已调用了Java的方法了,来看下总体的C代码实现吧。
好,来看下执行结果:
ok,到这里C调用Java就讲完了,下面讲下实用的C原生代码怎么断点调试。
在androidstudio->File->settings->androidSDK->SDK Tools中下载LLDB:
用项目对gradle-experimental的依赖代替本来项目对gradle的依赖。
① 使用com.android.model.application替代原来的com.android.application
② 把原有的配置存放在model{}中
③ 全部的配置属性使用等号(=)链接
原生C代码中断点,而后执行dug运行模式。
ok,接下来就能够执行你的源代码了。
好了,今天就讲到这里吧。
请关注微信公众号。谢谢