Android-NDK學習記錄5-Jni呼叫例項方法
阿新 • • 發佈:2018-11-25
上一篇看了jni呼叫靜態方法和修改靜態欄位,這一篇學習了jni呼叫例項方法和修改例項欄位
- 呼叫例項方法,步驟:
- 找到類:利用FindClass,找到類
- 找到要呼叫的方法id:利用GetMethodID,找到方法id
- 建立例項物件:利用例項物件的構造方法id來建立
- 使用例項物件去呼叫對應的Method:CallVoidMethod
- 修改例項欄位,步驟:
- 找到類:利用FindClass,找到類
- 找到要修改的欄位id:利用GetFieldID,找到欄位id
- 建立例項物件:利用例項物件的構造方法id來建立
- 設定欄位值:SetIntField
有上一篇做基礎,咱們直接來看看:
- 先來一個Java類,後面使用它來建立例項,和使用其中欄位與方法
/** * jni例項呼叫測試: * 1. 測試呼叫列印年齡和姓名,預期列印 0 和null * 2. 通過修改欄位,設定age和name的值,然後再列印 * 3. 重新建立一個物件,初始化方法中傳入性別,呼叫列印性別的方法 */ public class ObjectTest { private static final String TAG = ObjectTest.class.getSimpleName(); /** * 性別 */ private String sex; /** * 年齡 */ private int age; /** * 姓名 */ private String name; /** * 無引數構造方法 */ public ObjectTest() { } /** * 帶引數構造方法 * * @param sex 性別 */ public ObjectTest(String sex) { this.sex = sex; } /** * 用於列印age和name */ public void logInfo() { Log.i(TAG, "age:" + age + ",name:" + name); } /** * 用於列印性別 */ public void logSex() { Log.i(TAG, "性別:" + sex); } }
一. 呼叫例項方法
(1) 呼叫無參構造方法建立物件方式
- 找到jclass:依舊是使用全路徑名
jclass class_Objtest = env->FindClass("shixin/ndkdemo/obj_test/ObjectTest");
if (class_Objtest == NULL) {
LOGE("沒找到類");
return;
}
- 找到方法id:一會要呼叫的logInfo的方法id
jmethodID meth_id_logInfo = env->GetMethodID(class_Objtest, "logInfo", "()V");
- 建立例項
- jni沒有直接方法建立Java物件,那麼就通過使用它構造方法來建立,構造方法的方法名為:
<init>
- 建立例項使用NewObject方法來建立,具體程式碼如下:
/*由於jni沒有直接方法建立java物件,所以通過構造方法來建立*/
//獲取構造方法,這裡使用的是無參構造方法
jmethodID mtheod_construct_init = env->GetMethodID(class_Objtest, "<init>", "()V");
//建立例項Object,NewObject,引數:類、構造方法id
jobject object_ = env->NewObject(class_Objtest, mtheod_construct_init);
if (object_ == NULL) {
LOGE("建立物件失敗");
return;
}
- 呼叫例項方法:物件也建立好了,那麼就可以呼叫它的方法了咯,使用CallVoidMethod就行:
//呼叫方法,CallVoidMethod,引數:例項、要呼叫的方法id
env->CallVoidMethod(object_, meth_id_logInfo);
就這樣,就能呼叫物件的方法了,結合上面的Java的logInfo情況,大家應該也能猜到這次的列印是什麼,因為age和name都是初始值:
ndkdemo I/ObjectTest: age:0,name:null
(2) 呼叫有參構造方法建立物件方式
呼叫有引數構造方法來初始化:除了無引數構造方法,也可以呼叫有引數的構造方法,區別就是在建立物件的時候,比如這裡通過呼叫物件的有sex引數的構造方法來初始化物件:
- 找到有引數構造方法的id:
//這裡呼叫sex的構造方法:引數:類、構造方法名、簽名
jmethodID mtheod_construct_init_sex = env->GetMethodID(class_Objtest, "<init>",
"(Ljava/lang/String;)V");
- 建立物件:
//建立一個字串物件,女
jstring string_sex = env->NewStringUTF("女");
//使用NewObject來建立物件,引數:類、構造方法id、引數值
jobject object_sex = env->NewObject(class_Objtest, mtheod_construct_init_sex, string_sex);
if (object_sex == NULL) {
LOGE("帶參構造方法建立物件失敗");
return;
}
- 找到列印性別的方法id,再呼叫:
//6.1 找到列印性別的方法id
jmethodID mid_log_sex = env->GetMethodID(class_Objtest, "logSex", "()V");
if (mid_log_sex == NULL) {
LOGE("沒找到列印性別這個方法");
return;
}
//6.2 開始呼叫
env->CallVoidMethod(object_sex, mid_log_sex);
- 列印:
ndkdemo I/ObjectTest: 性別:女
二. 修改例項欄位
前面提到了無引數構造的時候,因為age和name都是初始值,正好,這裡來給他們賦值一下
- 找到欄位id:
//先找到欄位的id,引數:類、欄位名、欄位的簽名,這裡age是int的,所以是I
jfieldID f_id_age = env->GetFieldID(class_Objtest, "age", "I");
if (f_id_age == NULL) {
LOGE("沒找到age欄位");
return;
}
//同樣的方式,獲取name的欄位id
jfieldID f_id_name = env->GetFieldID(class_Objtest, "name", "Ljava/lang/String;");
if (f_id_name == NULL) {
LOGE("沒找到name欄位");
return;
}
- 給age和name欄位賦值:
這裡的age是12,name是"小寶貝"
//設定age值,引數:例項物件、欄位id、值
env->SetIntField(object_, f_id_age, age);
//因為String是Object型別,所以使用SetObjectField
env->SetObjectField(object_, f_id_name, name_);
- 再試試列印age和name方法:
meth_id_logInfo是之前的logInfo方法id
//修改了值之後,再來驗證呼叫一次logInfo,檢視列印
env->CallVoidMethod(object_, meth_id_logInfo);
- 列印:
ndkdemo I/ObjectTest: age:12,name:小寶貝
看列印,這就能看到,咱們是賦值成功了
三. 整體程式碼
- java中的native方法,與呼叫傳值:
//測試呼叫事例方法
testObjectMethodUse(12,"小寶貝");
/**
* 例項方法呼叫
*/
private native void testObjectMethodUse(int age, String name);
- jni完整實現:
/**
* 例項方法呼叫測試
*/
extern "C"
JNIEXPORT void JNICALL
Java_shixin_ndkdemo_MainActivity_testObjectMethodUse(JNIEnv *env, jobject instance, jint age,
jstring name_) {
//1. 利用FindClass,找到jclass
jclass class_Objtest = env->FindClass("shixin/ndkdemo/obj_test/ObjectTest");
if (class_Objtest == NULL) {
LOGE("沒找到類");
return;
}
//2. 利用GetMethodID,找到方法id
jmethodID meth_id_logInfo = env->GetMethodID(class_Objtest, "logInfo", "()V");
/*3. 建立例項物件,由於jni沒有直接方法建立java物件,所以通過構造方法來建立*/
//3.1 獲取構造方法,這裡使用的是無參構造方法
jmethodID mtheod_construct_init = env->GetMethodID(class_Objtest, "<init>", "()V");
//3.2 建立例項Object,NewObject,引數:類、構造方法id
jobject object_ = env->NewObject(class_Objtest, mtheod_construct_init);
if (object_ == NULL) {
LOGE("建立物件失敗");
return;
}
//4. 呼叫方法,CallVoidMethod,引數:例項、要呼叫的方法id
env->CallVoidMethod(object_, meth_id_logInfo);
/*5. 試試修改欄位.這裡我們利用java呼叫傳進來的age和name值來修改ObjectTest中的age和name*/
//5.1 先找到欄位的id,引數:類、欄位名、欄位的簽名,這裡age是int的,所以是I
jfieldID f_id_age = env->GetFieldID(class_Objtest, "age", "I");
if (f_id_age == NULL) {
LOGE("沒找到age欄位");
return;
}
//5.2 設定age值,引數:例項物件、欄位id、值
env->SetIntField(object_, f_id_age, age);
//5.3 同樣的方式,獲取name的欄位id和設定值
jfieldID f_id_name = env->GetFieldID(class_Objtest, "name", "Ljava/lang/String;");
if (f_id_name == NULL) {
LOGE("沒找到name欄位");
return;
}
//因為String是Object型別,所以使用SetObjectField
env->SetObjectField(object_, f_id_name, name_);
//5.4 修改了值之後,再來驗證呼叫一次logInfo,檢視列印
env->CallVoidMethod(object_, meth_id_logInfo);
/*6. 測試下帶引數的構造方法,傳入性別,再呼叫列印性別的方法*/
jmethodID mtheod_construct_init_sex = env->GetMethodID(class_Objtest, "<init>",
"(Ljava/lang/String;)V");
jstring string_sex = env->NewStringUTF("女");
jobject object_sex = env->NewObject(class_Objtest, mtheod_construct_init_sex, string_sex);
if (object_sex == NULL) {
LOGE("帶參構造方法建立物件失敗");
return;
}
//6.1 找到列印性別的方法id
jmethodID mid_log_sex = env->GetMethodID(class_Objtest, "logSex", "()V");
if (mid_log_sex == NULL) {
LOGE("沒找到列印性別這個方法");
return;
}
//6.2 開始呼叫
env->CallVoidMethod(object_sex, mid_log_sex);
/*釋放類和物件*/
env->DeleteLocalRef(class_Objtest);
env->DeleteLocalRef(object_);
env->DeleteLocalRef(string_sex);
env->DeleteLocalRef(object_sex);
}
- 輸出:
ndkdemo I/ObjectTest: age:0,name:null
ndkdemo I/ObjectTest: age:12,name:小寶貝
ndkdemo I/ObjectTest: 性別:女
四. 總結
- 呼叫例項方法和修改欄位與靜態的挺類似,區別就在於要建立例項,使用建立的例項去訪問其中的方法和欄位,而靜態的則只需要類,這個我想不用過多解釋,Java特性大家都應該瞭解
- 呼叫方法:CallXXMethod。其中XX代表返回值,Void代表無返回,Int代表返回Int
- 修改欄位:SetXXField。其中XX代表欄位型別,Int代表int型別,Object代表物件型別。
- 另外jni去呼叫例項方法、欄位的時候,java中的private這種限定符就沒意義了,private的欄位jni中一樣可以直接修改,或許jni的方式就是認為內部呼叫的吧。還好,final欄位的修改不會成功,不然顛覆了,哈哈