Android-NDK學習記錄4-C呼叫Java靜態方法修改靜態欄位
阿新 • • 發佈:2018-11-25
一. jni互動相關-方法簽名
方法簽名在jni的使用中經常都會用到,在java中會有過載,那麼定位到一個方法的方式:類+方法名稱+方法簽名,那麼我們先學習下簽名規則:
- 基本型別簽名:
咱們基本型別有各自的簽名,如下表
型別名 | 簽名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
看錶就能知道,大多數基本型別的前面其實就是首字母的大寫,有兩個特殊的,boolean的簽名是‘Z’,long的簽名是‘J’
- 類型別簽名:
前面說到了基本型別,再來看看類的簽名,比如:
類 | 全路徑名 | 簽名 |
---|---|---|
String | java.lang.String | Ljava/lang/String; |
Bundle | android.os.Bundle | Landroid/os/Bundle; |
Integer | java.lang.Integer | Ljava/lang/Integer; |
List | java.util.List | Ljava/util/List; |
我想看看上面,大家已經知道了他的規則,那就是:
L+全路徑名(.需要改為/)+;
PS:對於list,它的簽名依舊是list的類簽名,對於範型值沒有在簽名中,大家可以試試,兩個方法過載如果只改範型引數編譯器是會報錯的哦。
- 陣列型別簽名:
前面說了類的簽名,咱們陣列中的裝載物也是類,那它的簽名又是怎樣呢?,再看看:
類 | 全路徑名 | 簽名 |
---|---|---|
String[] | java.lang.String | [Ljava/lang/String; |
Integer[] | java.lang.Integer | [Ljava/lang/Integer; |
Integer[][] | java.lang.Integer | [[Ljava/lang/Integer; |
看看上面表格,我想大家也看出來它的規則了:
[+類路徑名。//對於前面的[,是幾維陣列就有幾個[
- 檢視方法簽名:javap -s命令
ndkdemo xin$ javap -s ./MainActivity.class
Compiled from "MainActivity.java"
public class shixin.ndkdemo.MainActivity extends android.support.v7.app.AppCompatActivity {
public shixin.ndkdemo.MainActivity();
descriptor: ()V
protected void onCreate(android.os.Bundle);
descriptor: (Landroid/os/Bundle;)V
public native java.lang.String stringFromJNI();
descriptor: ()Ljava/lang/String;
public native int intFromJni();
descriptor: ()I
static {};
descriptor: ()V
}
看了這個輸出,我想大家知道了方法簽名的表述了
名稱不說了,對於描述,可以看到,包含了引數與返回。規則:
(引數簽名)返回簽名
二. jni呼叫靜態方法與修改靜態欄位值
- jni呼叫靜態方法主要步驟,比如:
- 找到靜態類:jclass = env->FindClass
- 通過靜態類找到要使用的靜態方法id:jmethodID = env->GetStaticMethodID
- 呼叫該方法:env->CallStaticVoidMethod
- 修改靜態欄位類似,比如:
- 找到靜態類:jclass = env->FindClass
- 通過靜態類找到要使用的靜態方法id:jfieldID = env->GetStaticFieldID
- 呼叫該方法:env->SetStaticIntField
知道了步驟,那麼試試實現:
- 準備的靜態類:提供一個無引數無返回值的方法 和 一個帶引數帶返回值的方法
/**
* 靜態方法測試類
*/
public class StaticClassTest {
private static final String TAG = StaticClassTest.class.getSimpleName();
/**
* 名稱,用於測試被jni修改
*/
private static int age = 5;
/**
* 呼叫靜態方法測試
*/
public static void staticMethodTest() {
Log.d(TAG, "1 呼叫到靜態方法了");
Log.d(TAG, "2 呼叫到靜態方法了,age:" + age);
}
/**
* 呼叫帶引數靜態方法測試
*/
public static int staticMethodTest(String name) {
Log.d(TAG, "3 呼叫到帶參靜態方法了,name:" + name);
Log.d(TAG, "4 呼叫到帶參靜態方法了,修改後age:" + age);
return 1;
}
}
在前面基本步驟的第二步呢,我們會使用到方法的簽名,前面說了,用於區分方法引數等,可以看作是java中的區分過載的體現。那麼先來獲取下方法的簽名:
- 檢視靜態類的簽名:
MBP:static_test shixin$ javap -s StaticClassTest.class
Compiled from "StaticClassTest.java"
public class shixin.ndkdemo.static_test.StaticClassTest {
public shixin.ndkdemo.static_test.StaticClassTest();
descriptor: ()V
public static void staticMethodTest();
descriptor: ()V
public static int staticMethodTest(java.lang.String);
descriptor: (Ljava/lang/String;)I
static {};
descriptor: ()V
}
這樣就得到了簽名分別是:
- 無引數無返回:()V
- 帶引數帶返回:(Ljava/lang/String;)I
- 建立jni方法來呼叫咱們的靜態類方法與修改靜態欄位:
/**
* 測試靜態方法呼叫
*/
extern "C"
JNIEXPORT void JNICALL
Java_shixin_ndkdemo_MainActivity_testStaticMethodUse(JNIEnv *env, jobject instance) {
//1. 使用FindClass方法找到類,引數:全路徑
jclass class_static = env->FindClass("shixin/ndkdemo/static_test/StaticClassTest");
if (class_static == NULL) {
LOGE("沒找到StaticClassTest");
return;
}
//2. 找到方法,無引數,無返回。引數:class、方法名稱、方法簽名
jmethodID jme_staticMethodTest = env->GetStaticMethodID(class_static, "staticMethodTest",
"()V");
if (jme_staticMethodTest == NULL) {
LOGE("沒找到jme_staticMethodTest");
return;
}
//3. 開始呼叫,因為是返回void的,所以呼叫CallStaticVoidMethod
env->CallStaticVoidMethod(class_static, jme_staticMethodTest);
//帶引數和返回值的靜態方法
jmethodID jme_staticMethodTest_Have_Back = env->GetStaticMethodID(class_static,
"staticMethodTest",
"(Ljava/lang/String;)I");
if (jme_staticMethodTest_Have_Back == NULL) {
LOGE("沒找到jme_staticMethodTest_Have_Back");
return;
}
//4. 在呼叫帶引數方法前,我們試著修改下fild,即靜態類中的age
jfieldID jfieldID_age = env->GetStaticFieldID(class_static, "age", "I");
if(jfieldID_age==NULL){
LOGE("沒找到age欄位");
return;
}
//給age欄位賦值為7
env->SetStaticIntField(class_static,jfieldID_age,7);
jstring string_para = env->NewStringUTF("北鼻");
int a = env->CallStaticIntMethod(class_static, jme_staticMethodTest_Have_Back, string_para);
LOGE("呼叫後返回: %d", a);
//使用完,釋放本地變數,這裡有兩個,class和後面用到的string字串
env->DeleteLocalRef(class_static);
env->DeleteLocalRef(string_para);
}
檢視註釋就清楚步驟了。
- 輸出:
D/StaticClassTest: 1 呼叫到靜態方法了
D/StaticClassTest: 2 呼叫到靜態方法了,age:5
D/StaticClassTest: 3 呼叫到帶參靜態方法了,name:北鼻
D/StaticClassTest: 4 呼叫到帶參靜態方法了,修改後age:7
I/Native-lib: 呼叫後返回: 1
三. 總結:
- 呼叫方法的時候有多種呼叫方法:CallStaticXXMethod,其中XX為返回值型別
- 設定靜態欄位值的時候:SetStaticXXField,其中XX為要設定欄位的型別
- 使用類、方法名稱、(引數簽名)返回簽名來定位到一個具體的方法
- 使用類、欄位名稱、欄位簽名來定位到一個具體的欄位