<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                接續上篇[JNI開發系列②.h頭文件分析](http://www.jianshu.com/p/cba836f6a08c) ### 前情提要 在前面 , 我們已經熟悉了JNI的開發流程 , .h頭文件的分析 , 生成頭文件`javah`命令 , 以及java類型在C語言中的表現形式 , 值得注意的是 , java中的所有引用類型都是`jobject`類型 , `native`生成的函數 , 以`Java_全類名_方法名`表示,包名的`.`以`_`表示 。 ### 概述 在開篇的時候 ,我們就使用java的`native`方法調用過C函數 , 返回了一個String類型的字符串 , 使用`(*Env)->NewStringUTF(Env, "Jni C String");`函數 , 我們將字符指針轉換成`jstring` , java類型的字符串返回給了我們的java層 。今天我們來學習 , 使用C語言來調用Java的字段與方法 。 ### part 1 : C 函數訪問java字段 >一 , 定義Java 的`String`類型字段與修改字段的`native`方法 ```java // 使用C語言修改java字段 public String name = "zeno" ; // C語言修改java String 類型字段本地方法 public native void accessJavaStringField() ; // 調用 HelloJni jni = new HelloJni() ; System.out.println("修改前 name 的值:"+jni.name); //C語言修改java字段本地方法 jni.accessJavaStringField(); System.out.println("修改后 name 的值:"+jni.name); ``` > 二 , 在C語言頭文件中定義`native`方法的實現函數 , 并實現 ```c // com.zeno.jni_HelloJNI.h JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField (JNIEnv *, jobject); // Hello_JNI.c /*C語言訪問java String類型字段*/ JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField (JNIEnv *env, jobject jobj) { // 得到jclass jclass jcls = (*env)->GetObjectClass(env, jobj); // 得到字段ID jfieldID jfID = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;"); // 得到字段的值 jstring jstr = (*env)->GetObjectField(env, jobj, jfID); // 將jstring類型轉換成字符指針 char* cstr = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE); //printf("is vaule:%s\n", cstr); // 拼接字符 char text[30] = " xiaojiu and "; strcat(text, cstr); //printf("modify value %s\n", text); // 將字符指針轉換成jstring類型 jstring new_str = (*env)->NewStringUTF(env, text); // 將jstring類型的變量 , 設置到java 字段中 (*env)->SetObjectField(env, jobj, jfID, new_str); } ``` > 三 , 輸出 ```java 修改前 name 的值:zeno 修改后 name 的值: xiaojiu and zeno ``` > 四 , 分析 首先來分析C函數: ```c JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField (JNIEnv *env, jobject jobj) JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC (JNIEnv *Env, jclass jclazz) ``` 我們可以看出兩處不同 , 一處是返回值類型 , 一處是函數參數類型 ,返回值類型沒什么可講的 , 在分析.h頭文件的時候 , 已經詳細講述了 。那 , 這兩個函數參數類型`jobject`與`jclass`有什么區別呢 ? 這兩個類型表示 , Java的`native`函數 , 是成員方法還是類方法 , 成員方法需要對象.方法名 , 類方法則類名.方法名 , 可以在main方法里面直接使用 。 接下來是: ```c // 得到jclass jclass就好比java的.class對象 jclass jcls = (*env)->GetObjectClass(env, jobj); ``` 為什么要得到jclass呢 ? 因為 ,我們要獲取字段ID , 在JNI中 , 獲取java字段與方法都需要簽名。而簽名是在類加載的時候完成 , 所以在獲取字段ID的時候需要傳入jclass 。 // 得到字段ID ```c jfieldID jfID = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;"); ``` 通過傳入jclass , 字段名稱 , 字段簽名 , 就可以得到字段ID ,也可使用GetMethodID函數得到方法ID 。 為什么傳入了字段名稱,還需要簽名呢 ? 因為java支持重載 , 一個方法名稱可以有多個不同實現 , 根據傳入的參數不同 ,所以C語言調用函數為了區分不同的方法, 而對每個方法做了簽名 , 而字段則可用來標識類型 (僅個人理解)。 獲取字段與函數簽名的方式: ``` 在.class的文件目錄下 ,使用`javap -s -p className` 就可以列舉出 , 所有的字段與方法簽名 ``` ```c // 得到字段的值 , 類比java中的 對象.字段名得到值 , 這里是字段的ID jstring jstr = (*env)->GetObjectField(env, jobj, jfID); // 將jstring類型轉換成字符指針 char* cstr = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE); //printf("is vaule:%s\n", cstr); // 拼接字符 char text[30] = " xiaojiu and "; strcat(text, cstr); // 將字符指針轉換成jstring類型 jstring new_str = (*env)->NewStringUTF(env, text); ``` 因為java類型與C語言類型不是相通的 , 所有需要一個轉換 , 類型的介紹在上一篇已經詳細說明 。 ```c // 將jstring類型的變量 , 設置到java 字段中 // 類比java中的 對象.字段名得到值 , 這里是字段的ID (*env)->SetObjectField(env, jobj, jfID, new_str); ``` > 畫龍點睛: 上述中 , 我們訪問修改了String類型的字段 , 也基本上能看出訪問字段的基本套路 , 首先得到jclass , 再得到字段ID , 繼而得到字段的值 , 進行類型轉換 , 最后將變化的值設置給Java字段 。由此可以推出 , 訪問其他類型的字段 , 也是這樣的套路 , 只不過類型變了 , 值得注意的是 , java中的引用類型是需要進行類型轉換的 。 ### part 2 : C函數訪問Java方法 > 一 , ?定義Java 方法與調用方法的native方法 ```java // C語言調用java方法 private native void accessJavaRandomNumberMethod() ; // 調用 jni.accessJavaRandomNumberMethod() ; ``` > 二 , ?在C語言頭文件中定義native方法的實現函數 , 并實現 ```c // com.zeno.jni_HelloJNI.h JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaRandomNumberMethod (JNIEnv *, jobject); // Hello_JNI.c // 訪問java方法 JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaRandomNumberMethod (JNIEnv *env, jobject jobj) { // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); // 得到方法ID jmethodID jmtdId = (*env)->GetMethodID(env, jclazz, "getRandomNumber", "(I)I"); // 調用方法 jint jRandomNum = (*env)->CallIntMethod(env, jobj, jmtdId, 10); // 打印 printf("得到java方法的隨機數:%ld\n", jRandomNum); } ``` > 三 , 輸出 ```java 得到java方法的隨機數:6 ``` > 四 , 分析 ``` 不論是字段訪問還是方法的調用 , 其基本的套路不變 ,調用Java方法與調用字段不同的是 , 將得到字段ID改成得到方法ID , 得到字段的值改成調用方法`CallXXX` , 通過調用調用Java方法得到方法返回值 。 ``` ### part 3 : C函數訪問Java字段與方法(靜態) > 套路都是一樣的 , 這里僅給出代碼 , 不做詳細分析(本階段全部代碼) 。 ```java /** * * @author Zeno * * JNI (Java Native Interface) java本地化接口 * * Android Framework層與Native層相互通信的基石 * * */ public class HelloJni { // 使用C語言修改java字段 public String name = "zeno" ; // 使用C語言修改java int 類型字段 private int age = 20 ; public static String flag = "flag1" ; // 調用C語言函數方法 public static native String getStringFromC() ; // 調用C++語言函數方法 public static native String getStringFromCPP() ; // C語言修改java String 類型字段本地方法 public native void accessJavaStringField() ; // C語言修改java String static 類型字段本地方法 public native void accessJavaStaticStringField() ; // C語言修改java int 類型字段本地方法 public native void accessJavaIntField() ; // C語言調用java方法 private native void accessJavaRandomNumberMethod() ; // 調用Java靜態方法 private native void accessJavaStaticMethod() ; // 靜態native方法訪問字段 private static native void staticAccessJavaField() ; public static void main(String[] args) { System.out.println("getStringFormC == "+getStringFromC()); System.out.println("getStringFormC == "+getStringFromCPP()); HelloJni jni = new HelloJni() ; System.out.println("修改前 name 的值:"+jni.name); //C語言修改java字段本地方法 jni.accessJavaStringField(); System.out.println("修改后 name 的值:"+jni.name); System.out.println("修改前 flag 的值:"+flag); //C語言修改java static 字段本地方法 jni.accessJavaStaticStringField(); System.out.println("修改后 flag 的值:"+flag); System.out.println("修改前 age 的值:"+jni.age); //C語言修改java字段本地方法 jni.accessJavaIntField(); System.out.println("修改后 age 的值:"+jni.age); jni.accessJavaRandomNumberMethod() ; jni.accessJavaStaticMethod() ; // 靜態native方法 ,訪問java字段 System.out.println("修改前 flag 的值:"+flag); staticAccessJavaField(); System.out.println("修改后 flag 的值:"+flag); } static{ // 加載動態庫 System.loadLibrary("Hello_JNI") ; } // 調用java方法 private int getRandomNumber(int bound) { return new Random().nextInt(bound) ; } // 調用java靜態方法 private static String getUUID() { return UUID.randomUUID().toString(); } } ``` > C實現 , 這里就不貼頭文件了 。 調用靜態的Java字段與方法 , 在C語言中調用相應的static函數 , 例如:獲取靜態字段ID `GetStaticFieldID` 。 ```c #define _CRT_SECURE_NO_WARNINGS #include "com_zeno_jni_HelloJni.h" #include <string.h> #include <stdio.h> #include <stdlib.h> /* C/C++動態庫 , 在win平臺下以.dll文件標識 , 在linux下面以.so文件表示 在Android中 , 以.so文件表示 , 因為Android使用的是linux內核 。 */ /* * Class: com_zeno_jni_HelloJni * Method: getStringFormC * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC (JNIEnv *Env, jclass jclazz) { return (*Env)->NewStringUTF(Env, "Jni C String"); } /*C語言訪問java String類型字段*/ JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStringField (JNIEnv *env, jobject jobj) { // 得到jclass , jclass就好比java的.class對象 jclass jcls = (*env)->GetObjectClass(env, jobj); // 得到字段ID , jfieldID jfID = (*env)->GetFieldID(env, jcls, "name", "Ljava/lang/String;"); // 得到字段的值 jstring jstr = (*env)->GetObjectField(env, jobj, jfID); // 將jstring類型轉換成字符指針 char* cstr = (*env)->GetStringUTFChars(env, jstr, JNI_FALSE); //printf("is vaule:%s\n", cstr); // 拼接字符 char text[30] = " xiaojiu and "; strcat(text, cstr); //printf("modify value %s\n", text); // 將字符指針轉換成jstring類型 jstring new_str = (*env)->NewStringUTF(env, text); // 將jstring類型的變量 , 設置到java 字段中 (*env)->SetObjectField(env, jobj, jfID, new_str); } /*C語言訪問java int 類型字段*/ JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaIntField (JNIEnv *env, jobject jobj) { // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); // 得到字段ID jfieldID jfid = (*env)->GetFieldID(env, jclazz, "age", "I"); // 得到字段值 jint jAge = (*env)->GetIntField(env, jobj, jfid); jAge++; (*env)->SetIntField(env, jobj, jfid, jAge); } // 訪問java方法 JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaRandomNumberMethod (JNIEnv *env, jobject jobj) { // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); // 得到方法ID jmethodID jmtdId = (*env)->GetMethodID(env, jclazz, "getRandomNumber", "(I)I"); // 調用方法 jint jRandomNum = (*env)->CallIntMethod(env, jobj, jmtdId, 10); // 打印 printf("得到java方法的隨機數:%ld\n", jRandomNum); } // 訪問java靜態字段 JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStaticStringField (JNIEnv *env, jobject jobj) { // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); // 得到字段ID jfieldID jfid = (*env)->GetStaticFieldID(env, jclazz, "flag", "Ljava/lang/String;"); // 得到字段的值 jobject jFLagStr = (*env)->GetStaticObjectField(env, jclazz, jfid); // 將java字符串轉換成C字符指針 char* cFlagStr = (*env)->GetStringUTFChars(env, jFLagStr, JNI_FALSE); //printf("is vaule:%s\n", cFlagStr); char newStr[30] = " access static field "; strcat(newStr, cFlagStr); // 將C字符指針 , 轉換成java字符串 jstring jNewStr = (*env)->NewStringUTF(env, newStr); // 將字符串設置到java字段上 (*env)->SetStaticObjectField(env, jclazz, jfid, jNewStr); } // 訪問java靜態方法 JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_accessJavaStaticMethod (JNIEnv *env, jobject jobj) { // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); // 得到靜態方法ID jmethodID mtdid = (*env)->GetStaticMethodID(env, jclazz, "getUUID", "()Ljava/lang/String;"); // 調用靜態方法 jobject jUUIDStr = (*env)->CallStaticObjectMethod(env, jclazz, mtdid); // 將java字符串轉換成C字符指針 char* cUUIDStr = (*env)->GetStringUTFChars(env, jUUIDStr, JNI_FALSE); printf("is vaule:%s\n", cUUIDStr); // 根據UUID生成臨時文件 char file_path[100] ; sprintf(file_path, "e:\\dn\\%s.txt", cUUIDStr); printf("is address:%s\n", file_path); FILE* fp = fopen(file_path, "w"); if (fp == NULL) { printf("文件創建失敗\n"); } char* content = "落花有意流水無情"; // 寫入內容 fputs(content, fp); // 關閉流 fclose(fp); } // 靜態native方法 , 訪問java字段 JNIEXPORT void JNICALL Java_com_zeno_jni_HelloJni_staticAccessJavaField (JNIEnv *env, jclass jclazz) { // 得到字段ID jfieldID jfid = (*env)->GetStaticFieldID(env, jclazz, "flag", "Ljava/lang/String;"); // 得到字段的值 jstring jflag = (*env)->GetStaticObjectField(env, jclazz, jfid); // 將java字符串轉換成字符指針 char* cXj = (*env)->GetStringUTFChars(env, jflag, JNI_FALSE); printf("is value:%s\n", cflag); char newName[100] = "xiaojiu love "; char* cNewName = strcat(newName, cflag); // 將字符指針轉換成java類型 jstring newStr = (*env)->NewStringUTF(env, cNewName); // 設置 (*env)->SetStaticObjectField(env, jclazz, jfid, newStr); } ``` ### 編寫套路 C語言訪問Java語言的字段與方法 , 只要理解了一種 , 其他的都是套路 , 根據步驟一步一步來就可以了 。 > 步驟一 、 得到`jclass` , 字節碼對象 , 如果是`static native`修飾 , 則函數會以`jclass`類型傳入 , 非靜態則需要得到`jclass`類型 。 ```c // 得到jclass jclass jclazz = (*env)->GetObjectClass(env, jobj); ``` > 步驟二 、得到字段或方法ID , 區分靜態字段與對象字段 , 靜態字段或方法調用`(*env)->GetStaticFieldID`,`(*env)->GetMethodID`函數得到ID , 對象字段調用`(*env)->GetFieldID`,`(*env)->GetStaticMethodID`得到ID 。 可以得到一個套路 , 靜態修飾的 , 則調用`static`標識的函數 , 非靜態的則調用常規函數 。 ```c // 得到字段ID , 對象字段 jfieldID jfid = (*env)->GetFieldID(env, jclazz, "age", "I"); // 得到字段ID , 靜態字段 jfieldID jfid = (*env)->GetStaticFieldID(env, jclazz, "flag", "Ljava/lang/String;"); ``` > 步驟三 、 取得字段的值或調用方法 , 需要注意的是, 得到字段的值與調用方法 , 都有類型的區分 。引用類型則使用`GetObjectField` , `CallStaticObjectMethod` , 其他類型 , 則有對于的jxxx類型對應 。套路簡寫:`Get<Type>Field` , `GetStatic<Type>Field` , `Call<Type>Method` , `CallStatic<Type>Method` 。 ```c // 得到字段的值 jstring jstr = (*env)->GetObjectField(env, jobj, jfID); // 得到字段值 jint jAge = (*env)->GetIntField(env, jobj, jfid); // 調用方法 jint jRandomNum = (*env)->CallIntMethod(env, jobj, jmtdId, 10); // 調用靜態方法 jobject jUUIDStr = (*env)->CallStaticObjectMethod(env, jclazz, mtdid); ``` > 步驟四 、 類型轉換 , 如果是Java引用類型 , 則需要進行類型轉換 ``` // 將java字符串轉換成字符指針 char* cflag = (*env)->GetStringUTFChars(env, jflag, JNI_FALSE); ``` ### 結語 真正的高手 , 不是樂而學得的 , 真正的學習 , 不是輕輕松松的 。高手 , 需要刻意練習 , 刻意練習不是重復相同的動作 , 而是跳出舒適區熟悉區域 , 刻意練習自己不熟悉感覺艱難的事情 。感謝[動腦學院 ](http://www.dongnaoedu.com/)。 ### 本文由老司機學院【[動腦學院](http://www.dongnaoedu.com/)】特約提供。 做一家受人尊敬的企業,做一位令人尊敬的老師 參考資料: [Java Native Interface 6.0 Specification](http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看