Android NDK 9 JNI 數據類型和方法調用

一、基本類型

Java 類型 本地類型 描述 C 類型
int jint signed 32 bits 根據平臺不同
long jlong signed 64 bits 根據平臺不同
byte jbyte signed 8 bits 根據平臺不同
char jchar unsigned 16 bits typedef unsigned short
short jshort singed 16 bits typedef short
boolean jboolean unsigned 8 bits typedef unsigned char
float jfloat 32 bits typedef float
double jdouble 64 bits typedef double
void void N/A N/A

二、引用類型性

JNI 中的引用類型主要包括:

  • 類;
  • 對象;
  • 數組。

和 Java 中的引用類型的對應關系如下表所示:

Java 類型 JNI 類型 描述
Object jobject Object 類型
Class jclass Class 類型
String jstring String 類型
Object[] jobjectArray 對象數組
boolean[] jbooleanArray boolean 數組
byte[] jbyteArray byte 數組
char[] jcharArray char 數組
short[] jshortArray short 數組
int[] jintArray int 數組
long[] jlongArray long 數組
float[] jfloatArray float 數組
double[] jdoubleArray double 數組
Throwable jthrowable Throwable

三、native 函數參數

native 函數參數說明:

每個 native 函數,都至少有兩個參數:

  1. JNIEnv*;

  2. jclass 或 jobject:

  • 當 native 方法為靜態方法時:jclass 代表 native 方法所屬類的 class 對象
  • 當 native 方法為非靜態方法時:jobject 代表 native 方法所屬的對象。

四、屬性與方法簽名

數據類型 簽名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
reference LClassname;
type[] [type
method type(arg-types)ret-type

熟悉 JVM 的一定會發現這就是 JVM 中的字段描述符和方法描述符。

需要注意的是:

  • 類描述符開頭的 'L' 與結尾的 ';' 必須要有;
  • 數組描述符,開頭的 '[' 必須要有;
  • 方法描述符規則: "(各參數描述符)返回值描述符",其中參數描述符間沒有任何分隔符號。

五、C/C++ 訪問 Java 的屬性、方法

為了能夠在 C/C++ 中調用 Java 中的類,jni.h 的頭文件專門定義了 jclass 類型表示 Java 中 Class 類。JNIEnv 中有 3 個
函數可以獲取 jclass。

  1. jclass FindClass(const char* clsName)
    通過類的名稱(類的全名,這時候包名不是用"."點號而是用"/"來區分的)來獲取 jclass。

  2. jclass GetObjectClass(jobject obj)
    通過對象實例來獲取 jclass,相當于 Java 中的 getClass() 函數

  3. jclass getSuperClass(jclass obj)
    通過 jclass 可以獲取其父類的 jclass 對象

在 JNI 調用中,肯定會涉及到本地方法操作 Java 類中數據和方法。JNI 要求程序員通過特殊的 JNI 函數來獲取和設置數據以及調用 java 方法。

有以下幾種情況:

  1. 訪問 Java 類的非靜態屬性;
  2. 訪問 Java 類的靜態屬性;
  3. 訪問 Java 類的非靜態方法;
  4. 訪問 Java 類的靜態方法;
  5. 間接訪問 Java 類的父類的方法;
  6. 訪問 Java 類的構造方法。

在 Native 本地代碼中訪問 Java 層的代碼,一個常用的常見的場景就是獲取 Java 類的屬性和方法。所以為了在 C/C++ 獲取 Java 層的屬性和方法,JNI 在 jni.h 頭文件中定義了 jfieldID 和 jmethodID 這兩種類型來分別代表 Java 端的屬性和方法。

在訪問或者設置 Java 某個屬性的時候,首先就要現在本地代碼中取得代表該 Java 類的屬性的 jfieldID,然后才能在本地代碼中進行 Java 屬性的操作,同樣,在需要調用 Java 類的某個方法時,也是需要取得代表該方法的 jmethodID 才能進行 Java 方法操作。

5.1、訪問 Java 的非靜態屬性

Java 聲明如下:

public String name = "Jagger";

// 訪問非靜態屬性 name,修改它的值
// accessField 自定義的一個方法
public native void accessField();

C 代碼如下:

JNIEXPORT jstring JNICALL Java_com_haocai_jni_JniTest_accessField
(JNIEnv *env, jobject jobject) {
    // Jclass
    jclass cls = (*env)->GetObjectClass(env, jobject);
    // jfieldID 屬性名稱,屬性簽名
    jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
    // 獲取屬性值
    jstring jstr = (*env)->GetObjectField(env, jobject, fid);
    ...
}

5.2、訪問 Java 的靜態屬性

Java 聲明如下:

public static int count = 13;
public native void accessStaticField();

C 代碼如下:

JNIEXPORT void JNICALL Java_com_haocai_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj) {
    // jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    // jfieldID
    jfieldID fid =(*env)->GetStaticFieldID(env, cls, "count", "I");
    // GetStatic<Type>Field
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    ...
}

常見的調用 Java 層的方法一般是使用 JNIEnv 來進行操作:

  • GetFieldID/GetMethodID:獲取某個屬性/某個方法;

  • GetStaticFieldID/GetStaticMethodID:獲取某個靜態屬性/靜態方法。

方法的具體實現如下:

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

以上四個函數都是 4 個入參,而且每個入參的都是:

  • *JNIEnv *env;
  • jclass clazz;
  • const char *name;
  • const char *sig。

JNIEnv 代表一個 JNI 環境接口,jclass 上面也說了代表 Java 層中的“類”,name 則代表方法名或者屬性名。char *sig 代表代
表了 JNI 中的一個特殊字段 —— 簽名。

5.3、訪問 Java 的非靜態方法

Java 聲明如下:

public int genRandomInt(int max){
    System.out.println("genRandomInt 執行了..");
    return new Random().nextInt(max);
}

C 代碼如下:

JNIEXPORT void JNICALL Java_com_haocai_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj) {

    //Jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    // JmethodID
    jfieldID mFid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    // 調用
    // Call<Type>Method
    jint random = (*env)->CallIntMethod(env, jobj, mFid, 200);
    ...
}

5.4、訪問 Java 的靜態方法

Java 聲明如下:

public static String getUUID(){
  return  UUID.randomUUID().toString();
}

C 代碼如下:

// 訪問 Java 靜態方法
JNIEXPORT void JNICALL Java_com_haocai_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj) {
    // Jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    // JmethodID
    jfieldID mFid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
    // CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, jobj, mFid);
    ...
}

5.5、訪問 Java 類的構造方法

Java 聲明如下:

public native long accessConstructor();

C 代碼如下:

JNIEXPORT jlong  JNICALL Java_com_haocai_jni_JniTest_accesssConstructor
(JNIEnv *env, jobject jobj) {
    // jclass
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    // jmethodID
    jmethodID  constructor_mid= (*env)->GetMethodID(env, cls,"<init>","()V");
    // 實例化一個 Date 對象(可以在 constructor_mid 后加參)
    jobject date_obj =  (*env)->NewObject(env, cls, constructor_mid);
    // 調用 getTime 方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);

    printf("time:%lld\n",time);

    return time;
}

這里還有另一種方法來調用構造函數,方法如下:

jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);

這里多了一個參數,即 jvalue *args,這里是 args 代表的是對應構造函數的所有參數的,我們可以應將傳遞給構造函數的所有參數放在 jvalues 類型的數組 args 中,該數組緊跟著放在 methodID 參數的后面。

參考

Android JNI學習(三)——Java與Native相互調用

Android NDK開發之旅11--JNI--JNI數據類型與方法屬性訪問

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,214評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,315評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,699評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,882評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,441評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,189評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,388評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,613評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,112評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,334評論 2 377

推薦閱讀更多精彩內容