一. 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爲要設置字段的類型
- 使用類、方法名稱、(參數簽名)返回簽名來定位到一個具體的方法
- 使用類、字段名稱、字段簽名來定位到一個具體的字段