對應於不一樣類型的 Java 對象, JNI 包含大量的引用類型
Java 的類類型 | JNI 的引用類型 | 類型描述 |
---|---|---|
java.lang.Object | jobject | 能夠表示任何 Java 的對象,或者沒有 JNI 對應類型的 Java 對象(實例方法的強制參數) |
java.lang.String | jstring | Java 的 String 字符串類型的對象 |
java.lang.Class | jclass | Java 的 Class 類型對象(靜態方法的強制參數) |
Object[] | jobjectArray | Java 任何對象的數組表示形式 |
boolean[] | jbooleanArray | Java 基本類型 boolean 的數組表示形式 |
byte[] | jbyteArray | Java 基本類型 byte 的數組表示形式 |
char[] | jcharArray | Java 基本類型 char 的數組表示形式 |
short[] | jshortArray | Java 基本類型 short 的數組表示形式 |
int[] | jintArray | Java 基本類型 int 的數組表示形式 |
long[] | jlongArray | Java 基本類型 long 的數組表示形式 |
float[] | jfloatArray | Java 基本類型 float 的數組表示形式 |
double[] | jdoubleArray | Java 基本類型 double 的數組表示形式 |
java.lang.Throwable | jthrowable | Java 的 Throwable 類型,表示異常的全部類型和子類 |
寫幾個示例感覺一下 JNI 對數組的操做。java
從 Native 獲取數組,Java 進行格式化
/** * 從 Native 獲取數組,Java 進行格式化 * @param view */ public void getNativeArray(View view) { boolean[] nativeArray = NativeUtil.getNativeArray(); StringBuilder stringBuilder = new StringBuilder("["); for(int i = 0; i < nativeArray.length; i++) { stringBuilder.append(nativeArray[i]); if(i != nativeArray.length -1) { stringBuilder.append(", "); } } stringBuilder.append("]"); getNativeArrayText.setText(stringBuilder.toString()); } // JNI 對數組的操做 extern "C" JNIEXPORT jbooleanArray JNICALL Java_com_ihubin_ndkjni_NativeUtil_getNativeArray(JNIEnv *env, jclass clazz) { jboolean* jb = new jboolean[5]; jb[0] = JNI_TRUE; jb[1] = JNI_FALSE; jb[2] = JNI_TRUE; jb[3] = JNI_FALSE; jb[4] = JNI_TRUE; jbooleanArray jba = env->NewBooleanArray(5); env->SetBooleanArrayRegion(jba, 0, 5, jb); return jba; }
將 Java 數組傳入 Native,Native 格式化
/** * 將 Java 數組傳入 Native,Native 格式化 * @param view */ public void formatArray(View view) { int[] intArray = {11, 22, 33, 44, 55}; String formatArrayStr = NativeUtil.formatArray(intArray); formatArrayText.setText(formatArrayStr); } // JNI 對數組的操做 Java_com_ihubin_ndkjni_NativeUtil_formatArray(JNIEnv *env, jclass clazz, jintArray int_array) { jint array[5]; env->GetIntArrayRegion(int_array, 0, 5, array); jsize size = env->GetArrayLength(int_array); char resutStr[100] = {0}; char str[10] = {0}; strcat(resutStr, "["); for(int i = 0; i < size; i++) { sprintf(str, "%d", array[i]); strcat(resutStr, str); if(i != size - 1) { strcat(resutStr, ", "); } } strcat(resutStr, "]"); return env->NewStringUTF(resutStr); }
Native 計算商品總價
/** * Native 計算商品總價 * @param view */ public void calcTotalMoney(View view) { double[] price = {5.5, 6.6, 7.7, 8.8, 9.9}; String resultStr = NativeUtil.calcTotalMoney(price); calcTotalMoneyText.setText(resultStr); } // JNI 對數組的操做 extern "C" JNIEXPORT jstring JNICALL Java_com_ihubin_ndkjni_NativeUtil_calcTotalMoney(JNIEnv *env, jclass clazz, jdoubleArray price) { jdouble array[5]; env->GetDoubleArrayRegion(price, 0, 5, array); jsize size = env->GetArrayLength(price); char resutStr[255] = {0}; char str[20] = {0}; strcat(resutStr, "sum("); jdouble totalMoney = 0.0; for(int i = 0; i < size; i++) { sprintf(str, "%.1f", array[i]); strcat(resutStr, str); if(i != size - 1) { strcat(resutStr, ", "); } totalMoney += array[i]; } strcat(resutStr, ")"); strcat(resutStr, "\n="); sprintf(str, "%.1f", totalMoney); strcat(resutStr, str); return env->NewStringUTF(resutStr); }
Native 計算各科成績是否經過
/** * 計算各科成績是否經過 * @param view */ public void calcScorePass(View view) { float[] yourScore = {59.0F, 88.0F, 76.5F, 45.0F, 98.0F}; String[] yourScoreResult = NativeUtil.calcScorePass(yourScore); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("["); for(int i = 0; i < yourScore.length; i++) { stringBuilder.append(yourScore[i]); if(i != yourScore.length - 1) { stringBuilder.append(", "); } } stringBuilder.append("]"); stringBuilder.append("\n"); stringBuilder.append("["); for(int i = 0; i < yourScoreResult.length; i++) { stringBuilder.append(yourScoreResult[i]); if(i != yourScoreResult.length - 1) { stringBuilder.append(", "); } } stringBuilder.append("]"); calcScorePassText.setText(stringBuilder.toString()); } extern "C" JNIEXPORT jobjectArray JNICALL Java_com_ihubin_ndkjni_NativeUtil_calcScorePass(JNIEnv *env, jclass clazz, jfloatArray your_score) { jfloat array[5]; env->GetFloatArrayRegion(your_score, 0, 5, array); jsize size = env->GetArrayLength(your_score); jclass objClass = env->FindClass("java/lang/String"); jobjectArray objArray = env->NewObjectArray(5, objClass, 0); jstring jstr; for(int i = 0; i < size; i++) { if(array[i] >= 60.0) { jstr = env->NewStringUTF("√"); } else { jstr = env->NewStringUTF("×"); } env->SetObjectArrayElement(objArray, i, jstr); } return objArray; }
查看一下結果:android
到這裏,咱們已經‘熟練掌握’了 JNI 對 Java 基本數據類型數組的各類操做。在
Native 計算各科成績是否經過
這個示例中,出現了一段代碼jclass objClass = env->FindClass("java/lang/String");
下面就來探索一下。git
以前,都是在 Java 調用 Native 中的方法,如今,Native 代碼反調用 Java 層代碼。
1、獲取 Class 對象
爲了可以在 C/C++ 中調用 Java 中的類,jni.h 的頭文件專門定義了 jclass 類型表示 Java 中 Class 類。JNIEnv 中有 3 個函數能夠獲取 jclass。github
// 經過類的名稱(類的全名,這時候包名不是用'"."點號而是用"/"來區分的)來獲取 jclass jclass FindClass(const char* clsName) // 經過對象實例來獲取 jclass,至關於 Java 中的 getClass() 函數 jclass GetObjectClass(jobject obj) // 經過 jclass 能夠獲取其父類的 jclass 對象 jclass getSuperClass(jclass obj)
例:數組
// 獲取 Java 中的類 java.lang.String jclass objClass = env->FindClass("java/lang/String"); // 獲取一個類的父類 jclass superClass = env->getSuperClass(objClass);
2、獲取屬性方法
在 C/C++ 獲取 Java 層的屬性和方法,JNI 在 jni.h 頭文件中定義了 jfieldID 和 jmethodID 這兩種類型來分別表明 Java 端的屬性和方法。oracle
// 獲取屬性 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)
例:app
package com.ihubin.ndkjni; public class User { public static int staticField = 88; public int normalField = 99; public static String getStaticUserInfo() { return "[name:hubin, age:18]"; } public String getNormalUserInfo() { return "[name:hubin, age:28]"; } private String name; private int age; public User() {} public User(String name, int age) { this.name = name; this.age = age; } public String getFormatInfo() { return String.format("[name:%s, age:%d]", name, age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } // 獲取 jclass jclass userClass = env->FindClass("com/ihubin/ndkjni/User"); // 獲取屬性 ID jfieldID normalField = env->GetFieldID(userClass, "normalField", "I"); // 獲取靜態屬性 ID jfieldID staticField = env->GetStaticFieldID(userClass, "staticField", "I"); // 獲取方法 ID jmethodID normalMethod = env->GetMethodID(userClass, "getNormalUserInfo", "()Ljava/lang/String;"); // 獲取靜態方法 ID jmethodID staticMethod = env->GetStaticMethodID(userClass, "getStaticUserInfo", "()Ljava/lang/String;"); // 獲取無參構造函數 jmethodID voidInitMethod = env->GetMethodID(userClass, "<init>", "()V"); // 獲取有參構造函數 jmethodID paramInitMethod = env->GetMethodID(userClass, "<init>", "(Ljava/lang/String;I)V");
3、構造對象
類實例化之後才能訪問裏面的非靜態屬性、方法,下面經過上面獲取到的 jclass 和構造函數 jmethodID 構造對象。ide
// 經過 clazz/methodID/...(可變參數列表) 建立一個對象 jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...); // args(參數數組) jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); // args(指向變參列表的指針) jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); // 經過一個類建立一個對象,默認構造函數 jobject AllocObject(JNIEnv *env, jclass clazz)
例:函數
// 經過無參構造函數 jobject userOne = env->NewObject(userClass, voidInitMethod); // 經過有參構造函數 jstring name = env->NewStringUTF("HUBIN"); jint age = 8; jobject userTwo = env->NewObject(userClass, paramInitMethod, name, age); // 默認構造函數 jobject userThree = env->AllocObject(userClass);
4、獲取屬性、調用方法
以前的準備都是爲了能使用 Java 對象中的屬性、方法。
獲取、設置屬性值:XXX Get<type>Field(jobject obj, jfieldID fieldID)
Set<type>Field(jobject obj, jfieldID fieldID, XXX value)
獲取、設置靜態屬性值:XXX GetStatic<type>Field(jclass clazz, jfieldID fieldID)
SetStatic<type>Field(jclass clazz, jfieldID fieldID, XXX value)
調用方法:NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...)
NativeType Call<type>MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)
NativeType Call<type>MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)
調用靜態方法:NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...)
NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args)
NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args)
例:
// 獲取對象中的屬性 jint normalFieldValue = env->GetIntField(userOne, normalField); LOGD("normalField: %d", normalFieldValue); // 獲取 class 中靜態屬性 jint staticFieldValue = env->GetStaticIntField(userClass, staticField); LOGD("staticField: %d", staticFieldValue); // 調用對象中的方法 jobject normalMethodResultObj = env->CallObjectMethod(userOne, normalMethod); jstring normalMethodResult = static_cast<jstring>(normalMethodResultObj); const char *normalMethodResultNativeString = env->GetStringUTFChars(normalMethodResult, 0); LOGD("normalMethodResult: %s", normalMethodResultNativeString); // 調用 class 中的靜態方法 jobject staticMethodResultObj = env->CallStaticObjectMethod(userClass, staticMethod); jstring staticMethodResult = static_cast<jstring>(staticMethodResultObj); const char *staticMethodResultNativeString = env->GetStringUTFChars(staticMethodResult, 0); LOGD("staticMethodResult: %s", staticMethodResultNativeString); // 調用對象中的方法 jobject getFormatInfoMethodResultObj = env->CallObjectMethod(userTwo, getFormatInfoMethod); jstring getFormatInfoMethodResult = static_cast<jstring>(getFormatInfoMethodResultObj); const char *getFormatInfoMethodResultNativeString = env->GetStringUTFChars(getFormatInfoMethodResult, 0); LOGD("getFormatInfoMethodResult: %s", getFormatInfoMethodResultNativeString); // 測試 jobject AllocObject(JNIEnv *env, jclass clazz) 建立的對象 jobject userThreeMethodResultObj = env->CallObjectMethod(userThree, normalMethod); jstring userThreeMethodResult = static_cast<jstring>(userThreeMethodResultObj); const char *userThreeMethodResultNativeString = env->GetStringUTFChars(userThreeMethodResult, 0); LOGD("userThreeMethodResult: %s", userThreeMethodResultNativeString);
在獲取 jfieldID 、 jmethodID 時,出現了一些奇怪的字符串I
()Ljava/lang/String;
()V
(Ljava/lang/String;I)V
,下面就來研究一下。
在 JVM 虛擬機中,存儲數據類型的名稱時,是使用指定的類型簽名來存儲,而不是咱們習慣的 int,float 等。
JNI 使用的就是這種類型簽名。
Java 類型 | 類型簽名 |
---|---|
int | I |
long | J |
byte | B |
short | S |
char | C |
float | F |
double | D |
boolean | Z |
void | V |
其餘引用類型 | L+類全名+; |
數組 | [ |
方法 | (參數)返回值 |
例子1
Java 類型:java.lang.String 類型簽名:Ljava/lang/String; 即一個 Java 類對應的簽名,就是 L 加上類的全名,其中 . 要換成 / ,最後不要忘掉末尾的分號。
例子2
Java 類型:String[] 類型簽名:[Ljava/lang/String; Java 類型:int[][] 類型簽名:[[I 數組就是簡單的在類型描述符前加 [ 便可,二維數組就是兩個 [ ,以此類推。
例子3
Java 方法:long f (int n, String s, int[] arr); 類型簽名:(ILjava/lang/String;[I)J Java 方法:void f (); 類型簽名:()V 括號內是每一個參數的類型符,括號外就是返回值的類型符。
使用
javap -s <Class>
查看簽名
至此,咱們已經學會了在 Android 項目中 Native 操做 Java 對象。
代碼:
參考資料:
Oracle - JNI Types and Data Structures