Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android NDK開發總結

Android NDK開發總結

編輯:關於Android編程

1,搭建本地NDK環境

build path

Build path中設置C/C++ build Build command ndk-build NDK_DEBUG=1

C/C++ General 中設置 path and symbols為

在AndroidManifest的Application中設置Debuggable的值為true,此時可能有錯誤提示不能設置true,打開Problem 右鍵Quick Fix–>Disable check in this file only就可以了

打開Cygwin,用cd命令定位到工程目錄下,我的是 cd /cygdrive/f/練習/androidTest
然後執行ndk-gdb命令,如果提示有沖突,則先關閉eclipse再執行

然後設置相應斷點 ,Debug as Android Native Application就可以進入C/C++高度的模式了

2.NDK總結,需要引用第三方庫時
D:\android NDK開發、編譯、調試環境搭建與操作入門 - qiang106 - ITeye技術網站.mht—比較全面的總結
當進入JNI調試狀態 ,又需要引用第三方so庫時,此時編譯會自動清掉第三方庫,解決方法如下
1. 在jni目錄下添加需要導入的.so文件,這裡以ibtpnsSecurity.so為例

在jni目錄下的Android.mk文件中下添加腳本
[html] view plaincopy
include $(CLEAR_VARS)  
LOCAL_MODULE := libtpnsSecurity  
LOCAL_SRC_FILES := libtpnsSecurity.so  
include $(PREBUILT_SHARED_LIBRARY)  

問題解決!

將第三方so庫導入到jni目錄下,然後配置編譯文件,將so文件還原編譯到libs下面

NDK JNI開發中內存管理和釋放

1、什麼需要釋放? 

什麼需要什麼呢 ? JNI 基本數據類型是不需要釋放的 , 如 jint , jlong , jchar 等等 。 我們需要釋放是引用數據類型,當然也包括數組家族。如:jstring,jobject ,jobjectArray,jintArray 等等。
當然,大家可能經常忽略掉的是 jclass ,jmethodID , 這些也是需要釋放的哦

2、如何去釋放?

1) 釋放

jstring jstr = NULL; 
char* cstr = NULL;
//調用方法
jstr = (*jniEnv)->CallObjectMethod(jniEnv, mPerson, getName);
cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);
//釋放資源
(*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);
(*jniEnv)->DeleteLocalRef(jniEnv, jstr);  釋放 類 、對象、方法

3) 釋放 數組家族

jobjectArray arrays = NULL;
jclass jclsStr = NULL;
jclsStr = (*jniEnv)->FindClass(jniEnv, "java/lang/String");
arrays = (*jniEnv)->NewObjectArray(jniEnv, len, jclsStr, 0);
(*jniEnv)->DeleteLocalRef(jniEnv, jclsStr);  //釋放String
(*jniEnv)->DeleteLocalRef(jniEnv, arrays); //釋放jobjectArray數組

native method 調用 DeleteLocalRef() 釋放某個 JNI Local Reference 時,首先通過指針 p 定位相應的 Local Reference 在 Local Ref 表中的位置,然後從Local Ref 表中刪除該 Local Reference,也就取消了對相應 Java 對象的引用(Ref count 減 1)

5.2.1 釋放局部引用
大部分情況下,你在實現一個本地方法時不必擔心局部引用的釋放問題,因為本地方法被調用完成後,JVM會自動回收這些局部引用。盡管如此,以下幾種情況下,為了避免內存溢出,JNI程序員應該手動釋放局部引用:
1、 在實現一個本地方法調用時,你需要創建大量的局部引用。這種情況可能會導致JNI局部引用表的溢出,所以,最好是在局部引用不需要時立即手動刪除。比如,在下面的代碼中,本地代碼遍歷一個大的字符串數組,每遍歷一個元素,都會創建一個局部引用,當對這個元素的遍歷完成時,這個局部引用就不再需要了,你應該手動釋放它:

for (i = 0; i < len; i++) {
     jstring jstr = (*env)->GetObjectArrayElement(env, arr, i);
     ... /* process jstr */
     (*env)->DeleteLocalRef(env, jstr);
 }

2、 你想寫一個工具函數,這個函數被誰調用你是不知道的。4.3節中的MyNewString演示了怎麼樣在工具函數中使用引用後,使用DeleteLocalRef刪除。不這樣做的話,每次MyNewString被調用完成後,就會有兩個引用仍然占用空間。
3、 你的本地方法不會返回任何東西。例如,一個本地方法可能會在一個事件接收循環裡面被調用,這種情況下,為了不讓局部引用累積造成內存溢出,手動釋放也是必須的。
4、 你的本地方法訪問一個大對象,因此創建了一個對這個大對象的引用。然後本地方法在返回前會有一個做大量的計算過程,而在這個過程中是不需要前面創建的對大對象的引用的。但是,計算過程,對大對象的引用會阻止GC回收大對象。
在下面的程序中,因為預先有一個明顯的DeleteLocalRef操作,在函數lengthyComputation的執行過程中,GC可能會釋放由引用lref指向的對象。

JNI回調類型對應表
一旦你有了這個頭文件,你就需要寫頭文件對應的本地方法,就像我在清單C做的那樣。注意:所有的本地方法的第一個參數都是指向JNIEnv結構的。 這個結構是用來調用JNI函數的,(我會在另一個章節中討論)。第二個參數jclass的意義,要看方法是不是靜態的(static)或者實例 (Instance)的。前者,jclass代表一個類對象的引用,而後者是被調用的方法所屬對象的引用。最後的兩個jint參數表示了Java方法的 int參數。
返回值和參數類型根據等價約定映射到本地C/C++類型,如表A所示。有些類型,如清單B裡面的兩個jint參數,在本地代碼中可直接使用,而其他類型只有通過JNI調用操作。
表A

Java類型 本地類型 描述 boolean jboolean 8位整型 byte jbyte 帶符號的8位整型 char jchar 無符號的16位整型 short jshort C/C++ 帶符號的16位整型 int jint C/C++帶符號的32位整型 long jlong C/C++帶符號的64位整型e float jfloat C/C++32位浮點型 double jdouble C/C++64位浮點型 Object jobject 任何Java對象,或者沒有對應java類型的對象 Class jclass Class對象 String jstring 字符串對象 Object[] jobjectArray 任何對象的數組 boolean[] jbooleanArray 布爾型數組 byte[] jbyteArray 比特型數組 short[] jshortArray 短整型數組 int[] jintArray 整型數組 long[] jlongArray 長整型數組 double[] jdoubleArray 雙浮點型數組

※ JNI類型映射
最後一步是把本地代碼編譯成共享庫(比如,UNIX的so文件,Windows的dll文件)。在Java中調用方法前,共享庫須通過System.loadLibrary導入。最常用的方式是在類的靜態(static)初始化器裡做這這個工作。

在本地代碼中訪問JNI
我舉的例子很簡單,並不能滿足演示怎樣寫JNI方法的目標。現在,讓我們看一些高級的,通過JNIEnv結構使用非簡單類型的例子。
JNI通過函數的形式提供了很多功能,供本地代碼通過指向JNIEnv結構的指針調用;它作為第一個參數傳遞給每個本地方法。JNI函數的調用有下面幾種格式(這裡,假設env是指向JNIEnv的指針):
//C

(*env)->( env, )++ 格式
env->(  )

這篇文章中接下來的例子我將會用C++格式。
使用數組:
JNI通過JNIEnv提供的操作Java數組的功能。它提供了兩個函數:一個是操作java的簡單型數組的,另一個是操作對象類型數組的。
因為速度的原因,簡單類型的數組作為指向本地類型的指針暴露給本地代碼。因此,它們能作為常規的數組存取。這個指針是指向實際的Java數組或者Java數組的拷貝的指針。另外,數組的布置保證匹配本地類型。
為了存取Java簡單類型的數組,你就要要使用GetXXXArrayElements函數(見表B),XXX代表了數組的類型。這個函數把Java數組看成參數,返回一個指向對應的本地類型的數組的指針。
表B

函數 Java數組類型 本地類型 GetBooleanArrayElements jbooleanArray jboolean GetByteArrayElements jbyteArray jbyte GetCharArrayElements jcharArray jchar GetShortArrayElements jshortArray jshort GetIntArrayElements jintArray jint GetLongArrayElements jlongArray jlong GetFloatArrayElements jfloatArray jfloat GetDoubleArrayElements jdoubleArray jdouble

JNI數組存取函數
當你對數組的存取完成後,要確保調用相應的Relea***XXArrayElements函數,參數是對應Java數組和 GetXXXArrayElements返回的指針。如果必要的話,這個釋放函數會復制你做的任何變化(這樣它們就反射到java數組),然後釋放所有相 關的資源。
為了使用java對象的數組,你必須使用GetObjectArrayElement函數和SetObjectArrayElement函數,分別去get,set數組的元素。GetArrayLength函數會返回數組的長度。
清單D包含了一個簡單的類,它演示了本地代碼如何使用Java數組。這個本地實現循環遍歷一個整型(int)數組,返回這些元素的總和。為簡單起見,這個清單包含了java代碼和本地實現。我已經省略了頭文件,它可以很方便地通過javah得到。

在本地代碼中訪問JNI
使用對象
JNI提供的另外一個功能是在本地代碼中使用Java對象。通過使用合適的JNI函數,你可以創建Java對象,get、set 靜態(static)和實例(instance)的域,調用靜態(static)和實例(instance)函數。JNI通過ID識別域和方法,一個域或 方法的ID是任何處理域和方法的函數的必須參數。
表C列出了用以得到靜態(static)和實例(instance)的域與方法的JNI函數。每個函數接受(作為參數)域或方法的類,它們的名稱,符號和它們對應返回的jfieldID或jmethodID。
表C

函數 描述 GetFieldID 得到一個實例的域的ID GetStaticFieldID 得到一個靜態的域的ID GetMethodID 得到一個實例的方法的ID GetStaticMethodID 得到一個靜態方法的ID

※域和方法的函數
如果你有了一個類的實例,它就可以通過方法GetObjectClass得到,或者如果你沒有這個類的實例,可以通過FindClass得到。符號是從域的類型或者方法的參數,返回值得到字符串,如表D所示。
表D

類型 符號 boolean Z byte B char C short S int I long L float F double D void V objects對象 Lfully-qualified-class-name;L類名 Arrays數組 [array-type [數組類型 methods方法 (argument-types)return-type(參數類型)返回類型

※確定域和方法的符號
一旦你有了類和方法或者域的ID,你就能把它保存下來以後使用,而沒有必要重復去獲取。
有幾個分別訪問域和方法的函數。實例的域可以使用對應域的GetXXXField的變體函數訪問。GetStaticXXXField函數用於靜態類型。設置域的值,用SetXXXField 和SetStaticXXXField函數。表E包含了所有訪問域的函數列表。
表E

Java 類型 Method方法 boolean GetBooleanField, GetStaticBooleanField, SetBooleanField,SetStaticBooleanField byte GetByteField, GetStaticByteField, SetByteField, SetStaticByteField char GetCharField, GetStaticCharField, SetCharField, SetStaticCharField short GetShortField, GetStaticShortField, SetShortField, SetStaticShortField int GetIntField, GetStaticIntField, SetIntField, SetStaticIntField long GetLongField, GetStaticLongField, SetLongField, SetStaticLongField float GetFloatField, GetStaticFloatField, SetFloatField, SetStaticFloatField double GetDoubleField, GetStaticDoubleField, SetDoubleField, SetStaticDoubleField object GetObjectField, GetStaticObjectField, SetObjectField, SetStaticObjectField

※訪問域的函數
另外,方法的訪問是由CallXXXMethod 函數和CallStaticXXXMethod函數完成的,XXX表明了方法的返回值類型。這些函數的變體允許傳遞數組參數 (CallXXXMethodA and CallStaticXXXMethodA)或者傳遞一個可變大小的列表(CallXXXMethodV and CallStaticXXXMethodV)。

一個完整的列表
表F:一個完整的列表

返回類型 函數 boolean CallBooleanMethod, CallBooleanMethodA, CallBooleanMethodV, CallStaticBooleanMethod, CallStaticBooleanMethodA, CallStaticBooleanMethodV byte CallByteMethod, CallByteMethodA, CallByteMethodV, CallStaticByteMethod, CallStaticByteMethodA, CallStaticByteMethodV char CallCharMethod, CallCharMethodA, CallCharMethodV, CallStaticCharMethod, CallStaticCharMethodA, CallStaticCharMethodV short CallShortMethod, CallShortMethodA, CallShortMethodV, CallStaticShortMethod, CallStaticShortMethodA, CallStaticShortMethodV int CallIntMethod, CallIntMethodA, CallIntMethodV, CallStaticIntMethod, CallStaticIntMethodA, CallStaticIntMethodV long CallLongMethod, CallLongMethodA, CallLongMethodV, CallStaticLongMethod, CallStaticLongMethodA, CallStaticLongMethodV float CallFloatMethod, CallFloatMethodA, CallFloatMethodV, CallStaticFloatMethod, CallStaticFloatMethodA, CallStaticFloatMethodV double CallDoubleMethod, CallDoubleMethodA, CallDoubleMethodV, CallStaticDoubleMethod, CallStaticDoubleMethodA, CallStaticDoubleMethodV void CallVoidMethod, CallVoidMethodA, CallVoidMethodV, CallStaticVoidMethod, CallStaticVoidMethodA, CallStaticVoidMethodV object CallObjectMethod, CallObjectMethodA, CallObjectMethodV, CallStaticObjectMethod, CallStaticObjectMethodA, CallStaticObjectMethodV

※方法訪問函數
清單E演示了如何在本地代碼中調用方法。本地方法printRandom得到了靜態方法Math.random的ID,並且調用它幾次,打印出結果。實例方法也一樣處理。
當你關注java的擴展時,JNI是一個強大的工具,它不會嚴重降低可移植性。我這裡只是接觸它的表面,僅僅向你演示了JNI的能力和潛力。我鼓勵你獲取

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved