打通了鏈接Java世界和native世界的通道以後,擺在咱們面前的問題,就變爲了,如何在native code中,來操做Java object呢?Java object能夠分爲以下3種: java
Java Language的基本數據類型,與C/C++中的那些基本數據類型並沒有太大的差異,不管是做爲參數傳遞,仍是要做爲返回值。於是此處,對於Java Language的基本數據類型就再也不多作描述。下面主要說明一下在native code中訪問Java Language中內置的引用數據類型和非Java Language內置的引用數據類型。 android
Java Language中內置的引用數據類型,又主要包括String和原始數據類型的數組。 數組
首先來看String。String所對應的native 類型爲jstring。jstring既不一樣於C風格的一‘\0’結尾的char *的string,也不一樣於C++標準庫中的string類型。咱們不能像操做C風格的string那樣來操做jstring,而必須首先通過JNI函數的轉換,轉換爲UTF-16編碼的jchar array或者UTF-8編碼的char array以後,再來作操做。JNI有專門提供一組函數來來完成這樣的轉換。這組函數以下: ide
能夠看到這組函數大致上能夠分爲兩個族,String*的那一族用於操做UTF-16的字串串,如將jstring轉換爲UTF-16編碼的jchar 數組,獲取jchar數組長度等;而StringUTF*的那一族,則用於操做UTF-8的字串。 函數
接下來,能夠看一段code。首先,是在Java code中調用native 方法的部分: this
char[] text = new char[] { 0xE01, 0xE49, 0xE33, 0xE43, 0xE37, 0xE27, 0xE64 }; stringToJNI(String.valueOf(text, 0, text.length)); } public native String stringToJNI(String text);
而後是C/C++ code中對於native method的實現部分: 編碼
static jstring HelloJni_stringToJNI( JNIEnv* env, jobject thiz, jstring text) { JniDebug("from HelloJni_stringToJNI"); const char* utf8Chars = env->GetStringUTFChars(text, NULL); jsize utf8Length = env->GetStringUTFLength(text); JniDebug("utf8Length = %d", utf8Length); if (utf8Chars == NULL) { return NULL; } JniDebug("String got from java world: %s", utf8Chars); env->ReleaseStringUTFChars(text, utf8Chars); const jchar* chars = env->GetStringChars(text, NULL); jsize unicodeLength = env->GetStringLength(text); JniDebug("unicodeLength = %d", unicodeLength); env->ReleaseStringChars(text, chars); return text; }
上面那段code的運行結果: spa
這個地方咱們看到,有用到GetStringChars和GetStringUTFChars及ReleaseStringChars和ReleaseStringUTFChars這幾個函數,GetStringChars和GetStringUTFChars返回jstring通過轉換的結果,對於這兩個函數的第三個參數,若是isCopy不是NULL,則當作了copy時,*isCopy會被設置爲JNI_TRUE,而沒有作copy時,它被設置爲JNI_FALSE。尤爲值得咱們注意的是,每次調用GetString*函數以後,老是須要在適當的時候,來相應的調用ReleaseString*函數。不然,則會出現Leak,而產生一些莫名其妙的NE。另外,在Java code中,咱們看到String不是由ASCII範圍內的字符,而是泰語字符的char[]轉換而來。再來看native code中,傳遞給android log函數的參數,是java層的字串轉換爲UTF-8編碼的結果。於是,咱們瞭解到一點,傳遞給android log函數參數,應該是一個UTF-8編碼的字串。咱們知道,UTF-8不只能夠兼容ASCII,還能夠支持完整的Unicode字符。 指針
GetStringRegion和GetStringUTFRegion這兩個函數,所作的事情,與GetStringChars和GetStringUTFChars所作的事情,並無很大的區別。最大的區別在於,前者會將jstring轉換的結果copy進做爲參數傳遞進去的buffer中。
code
GetStringCritical和ReleaseStringCritical這兩個調用之間的code,則應被看成是處於一個臨界區(「critical region」)。處於臨界區的code,會有額外的一些限制。Inside a critical region, native code must not call other JNI functions, nor may the native code make any system call that may cause the current thread to block and wait for another thread in the virtual machine instance。固然,這兩個函數的具體實現,則因Java VM的實現而異。
建立jstring的函數看起來也還清晰明確,不管是從UTF-8編碼的char *建立,仍是從UTF-16編碼的jchar *建立,JNI提供的接口都簡單易用。
而後來看數組。對於基本數據類型的數組的訪問,與對於String的訪問,有不少類似的地方。咱們一樣不能直接訪問這些數組,而必需要通過JNI的轉換。這種轉換的接口也是分爲3組,Get<Type>ArrayElements()這一組,會直接返回一個<NativeType>的指針,以後咱們就可使用C/C++中常規的訪問數組的方法來訪問基本數據類型的數組了。一樣的,咱們同樣須要在適當的地方調用相應的Release<Type>ArrayElements()函數以防止Leak;Get<Type>ArrayRegion()這一組,則能夠將數組的成員copy進咱們傳入的一個buffer中;而GetPrimitiveArrayCritical()則與前面的那個GetStringCritical()相似。咱們能夠來看一下JNI提供的那些函數的原型,及簡單的說明:
接下來咱們能夠看一些code。首先,是Java code中對於native方法的調用的部分:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* Create a TextView and set its content. * the text is retrieved by calling a native * function. */ TextView tv = new TextView(this); tv.setText(stringFromJNI()); setContentView(tv); int[] dataElement = new int[] { 2, 3, 4, 6 }; int sum = sumIntWithNative(dataElement, 0, dataElement.length); Log.d(TAG, "sum = " + sum); double[] doubleElement = new double[] { 3.4, 5.3, 7.6, 9.2 }; double doubleSum = sumDoubleWithNative(doubleElement, 0, doubleElement.length); Log.d(TAG, "doubleSum = " + doubleSum); char[] text = new char[] { 0xE01, 0xE49, 0xE33, 0xE43, 0xE37, 0xE27, 0xE64 }; stringToJNI(String.valueOf(text, 0, text.length)); } public native String stringToJNI(String text); public native int sumIntWithNative(int[] dataElement, int start, int end); public native double sumDoubleWithNative (double[] dataElement, int start, int end);
而後,是native code中對於native方法的實現的部分:
static jint HelloJni_sumIntWithNative( JNIEnv* env, jobject thiz, jintArray dataElement, jint start, jint end) { JniDebug("from HelloJni_sumIntWithNative"); jint sum = 0; jint*dateArray = env->GetIntArrayElements(dataElement, NULL); for (int i = start; i < end; ++ i) { sum += dateArray[i]; } env->ReleaseIntArrayElements(dataElement, dateArray, 0); return sum; } static jdouble HelloJni_sumDoubleWithNative( JNIEnv* env, jobject thiz, jdoubleArray dataElement, jint start, jint end) { JniDebug("from HelloJni_sumDoubleWithNative"); jdouble sum = 0.0; jdouble *dataArray = env->GetDoubleArrayElements(dataElement, NULL); for (int i = start; i < end; ++ i) { sum += dataArray[i]; } env->ReleaseDoubleArrayElements(dataElement, dataArray, 0); return sum; }
程序運行過程當中所打的log:
待續...