編輯:關於Android編程
1,搭建本地NDK環境
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為例
[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
※ 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
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
※域和方法的函數
如果你有了一個類的實例,它就可以通過方法GetObjectClass得到,或者如果你沒有這個類的實例,可以通過FindClass得到。符號是從域的類型或者方法的參數,返回值得到字符串,如表D所示。
表D
※確定域和方法的符號
一旦你有了類和方法或者域的ID,你就能把它保存下來以後使用,而沒有必要重復去獲取。
有幾個分別訪問域和方法的函數。實例的域可以使用對應域的GetXXXField的變體函數訪問。GetStaticXXXField函數用於靜態類型。設置域的值,用SetXXXField 和SetStaticXXXField函數。表E包含了所有訪問域的函數列表。
表E
※訪問域的函數
另外,方法的訪問是由CallXXXMethod 函數和CallStaticXXXMethod函數完成的,XXX表明了方法的返回值類型。這些函數的變體允許傳遞數組參數 (CallXXXMethodA and CallStaticXXXMethodA)或者傳遞一個可變大小的列表(CallXXXMethodV and CallStaticXXXMethodV)。
一個完整的列表
表F:一個完整的列表
※方法訪問函數
清單E演示了如何在本地代碼中調用方法。本地方法printRandom得到了靜態方法Math.random的ID,並且調用它幾次,打印出結果。實例方法也一樣處理。
當你關注java的擴展時,JNI是一個強大的工具,它不會嚴重降低可移植性。我這裡只是接觸它的表面,僅僅向你演示了JNI的能力和潛力。我鼓勵你獲取
今天在android項目中使用AES對數據進行加解密,遇到了很多問題,網上也找了很多資料,也不行。不過最後還是讓我給搞出來了,這裡把這個記錄下來,不要讓別人走我的彎路,因
SharedPreferences是Android提供用來存儲一些簡單的配置信息的一種機制,例如,一些默認歡迎語、登錄的用戶名和密碼等。其以鍵值對的方式存儲
快速自動更新Android Studio版本在開發過程中,有些時候總是會報一些Android Studio vesion 版本低,要求升級之類的錯誤,然而大家又會嫌麻煩去
寫在前面從裝飾者模式到Context類族當觀察者模式和回調機制遇上Android源碼Android源碼中的靜態工廠方法Android中的工廠方法模式前面跟大家分享了裝飾者