前言
前面我們已經(jīng)在Android Studio中創(chuàng)建支持C/C++的項(xiàng)目了,并能調(diào)用native方法,返回字符串,展示在界面中,如果對(duì)此不了解的話,可以查看之前的博客:Android Studio NDK開發(fā)(二):Welcome to JNI
下面我將介紹native函數(shù)訪問Java中的屬性
Java基本數(shù)據(jù)類型與JNI的映射關(guān)系
Java類型<-->JNI類型<-->C類型
JNI基本數(shù)據(jù)類型(左邊是Java,右邊是JNI)
boolean jboolean;
byte jbyte;
char jchar;
short jshort;
int jint;
long jlong;
float jfloat;
double jdouble;
void void
JNI引用數(shù)據(jù)類型(左邊是Java,右邊是JNI)
String jstring
Object jobject
//基本數(shù)據(jù)類型的數(shù)組
byte[] jByteArray
//對(duì)象數(shù)組
Object[](String[]) jobjectArray
由于在屬性、方法訪問當(dāng)中需要用到簽名,接下來我們來看看簽名:
屬性與方法的簽名
屬性的簽名

方法的簽名
方法的簽名稍顯麻煩,我們可以在Android Studio中配置javap -s -p,來生成某個(gè)類所有的屬性、方法的簽名。
在Android Studio中,選擇file -> settings -> 輸入tools -> 選擇External Tools:

這里由于我已經(jīng)添加了javap -s -p,所以這里已經(jīng)有了相關(guān)配置,可不必理睬。現(xiàn)在我們來進(jìn)行配置,點(diǎn)擊第三步,出現(xiàn):

配置后:

點(diǎn)擊ok,這樣我們就可以查看某個(gè)方法的簽名了,選擇該方法所屬類,比如MainActivity,點(diǎn)擊Tools -> External Tools -> javap -s -p

可以看到生成了該類所有的簽名(如果不行,可以Rebuild Project或者M(jìn)ake Project,再執(zhí)行javap -s -p命令):

訪問Java非靜態(tài)屬性
我們需要做的是native函數(shù)訪問Java的非靜態(tài)屬性,并改變Java中非靜態(tài)屬性的值。
首先在MainActivity(可自己起名字,也可以是Java類)中定義一個(gè)非靜態(tài)屬性key,也就是native函數(shù)需要訪問的Java屬性
public String key = "john";
在MainActivity中定義一個(gè)native方法
//訪問屬性
public native void accessField();
由于需要判斷更改后的值是否一樣,所以在MainActivity中的onCreate()中調(diào)用并打印
System.out.println("修改前:" + key);
this.accessField();
System.out.println("修改后:" + key);
當(dāng)然基本的配置,比如System.loadLibray,CMakeLists的配置是少不了的,有不清楚的,可以查看我的NDK開發(fā)系列的Android NDK開發(fā)(一)、(二),這里就不做贅述了。
再來看看native函數(shù)的實(shí)現(xiàn):
//C/C++訪問Java的成員
//訪問非靜態(tài)屬性
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessField(JNIEnv *env, jobject jobj) {
//得到j(luò)class
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到j(luò)fieldID,最后一個(gè)參數(shù)是簽名,String對(duì)應(yīng)的簽名是Ljava/lang/String;(注意最后的分號(hào))
jfieldID jfID = (*env)->GetFieldID(env, jcla, "key", "Ljava/lang/String;");
//得到key屬性的值jstring
jstring jstr = (*env)->GetObjectField(env, jobj, jfID);
//jstring轉(zhuǎn)化為C中的char*
char* oriText = (*env)->GetStringUTFChars(env, jstr, NULL);
//拼接得到新的字符串text="China John"
char text[20] = "China ";
strcat(text, oriText);
//C中的char*轉(zhuǎn)化為JNI中的jstring
jstring jstrMod = (*env)->NewStringUTF(env, text);
//修改key
(*env)->SetObjectField(env, jobj, jfID, jstrMod);
//只要使用了GetStringUTFChars,就需要釋放
(*env)->ReleaseStringUTFChars(env, jstr, oriText);
}
具體JNI的api可看上面的注釋,這里就不細(xì)說了,有一定的規(guī)律可尋,大家動(dòng)手做做,相信問題不大。
打印輸出:
修改前:john
修改后:China john
訪問Java靜態(tài)屬性
首先定義一個(gè)Java靜態(tài)屬性
private static int count = 10;
再定義一個(gè)native方法
//訪問靜態(tài)屬性
public native void accessStaticField();
在MainActivity onCreate中調(diào)用打印
System.out.println("修改前count:" + count);
accessStaticField();
System.out.println("修改后count:" + count);
native函數(shù)的實(shí)現(xiàn)
//訪問靜態(tài)屬性
JNIEXPORT void JNICALL
Java_com_zhangpan_myjnicmake_MainActivity_accessStaticField(JNIEnv *env, jobject jobj) {
//得到j(luò)class
jclass jcla = (*env)->GetObjectClass(env, jobj);
//得到j(luò)fieldID
jfieldID jfid = (*env)->GetStaticFieldID(env, jcla, "count", "I");
//得到靜態(tài)屬性的值count
jint count = (*env)->GetStaticIntField(env, jcla, jfid);
//自增
count++;
//修改count的值
(*env)->SetStaticIntField(env, jcla, jfid, count);
}
項(xiàng)目地址
https://github.com/fsrmeng/MyJniCmake-Master
展望
本篇博客介紹了如何在native函數(shù)中訪問Java的屬性,下一篇博客我們將介紹如何在native函數(shù)中訪問Java的方法,敬請(qǐng)期待!