1.Android 內(nèi)存基礎(chǔ)
- 所有的內(nèi)存都是基于物理內(nèi)存的淘讥,即移動設(shè)備上的RAM甚带。當啟動一個Android程序時鸟召,會啟動一個Dalvik vm 進程,系統(tǒng)會給它分配固定的內(nèi)存空間了牛,這塊內(nèi)存會映射到RAM上某個區(qū)域颜屠,然后Android程序就運行在這塊空間上。JAVA里會將這塊空間分成Stack棧內(nèi)存和Heap堆內(nèi)存鹰祸。stack里存放對象的引用甫窟,heap里存放實際對象數(shù)據(jù)。
2.Android 問題與調(diào)節(jié)
- Memory Leak(內(nèi)存泄露):在程序中創(chuàng)建對象蛙婴,不及時回收無效空間粗井。
- OutOfMemory(內(nèi)存溢出):當應(yīng)用程序申請的java heap空間超過Dalvik vm HeapGrowthLimit時,溢出街图。(內(nèi)存溢出并不是內(nèi)存不夠浇衬,而是超過了Dalvik vm的限制)
- Memory Churn(內(nèi)存抖動):GC操作頻繁,導致內(nèi)存抖動餐济。
- 調(diào)節(jié):如果RAM申請的空間不足耘擂,那么Android的Memory Killer會殺死優(yōu)先級低的進程。
3.Android默認內(nèi)存回收機制——垃圾回收GC(Garbage Collection)
- 進程優(yōu)先級:
- Foreground:處于焦點的進程絮姆。
- Visible:可以看見的進程醉冤。
- Service:服務(wù)進程。
- Background:后臺進程篙悯。
- Empty:空進程蚁阳。
- 進行GC時刻:
- app空閑的時候。
- 內(nèi)存緊張的時候 鸽照。
- 分配大的內(nèi)存塊不夠用的時候韵吨。
- GC回收模型:
Generational Heap Memory
采用分代技術(shù),分為年輕代移宅、老年代、持久代椿疗。
最先清理的是年輕代漏峰,只要清理出足夠的空間就不去清理老年代了。依次持久帶也是這樣届榄。 - GC回收步驟:
- 從GC Root開始浅乔,開始對對象引用遍歷,找出GC Root直接引用的所有對象集1,再找出直接引用對象集1的對象集2……直到遍歷完所有對象靖苇。這些對象都是有效對象席噩。
- 刪除其他無效對象。
- 注:GC執(zhí)行的時候贤壁,當前所有線程的任何操作都會需要暫停悼枢。
4.優(yōu)化——減少額外的內(nèi)存使用
4.1 Bitmap
基本思路:縮略圖,像素脾拆,圖片回收馒索,捕獲OOM異常
- 縮略圖:使用BitmapFactory.Options方法里的inSampleSize設(shè)置縮小比。
- 像素:
Android中圖片有四種屬性名船,分別是:
ALPHA_8:每個像素占用1byte內(nèi)存
ARGB_4444:每個像素占用2byte內(nèi)存
ARGB_8888:每個像素占用4byte內(nèi)存 (默認)
RGB_565:每個像素占用2byte內(nèi)存
默認是顯示效果最好的ARGB_8888绰上,可以使用BitmapFactory.Options的inPreferredConfig方法設(shè)置。 - 圖片回收:
// 先判斷是否已經(jīng)回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置為null
bitmap.recycle();
bitmap = null;
}
System.gc();
- 捕獲OOM異常:
Bitmap bitmap = null;
try {
// 實例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
// 捕獲OutOfMemoryError渠驼,避免直接崩潰
}
if (bitmap == null) {
// 如果實例化失敗 返回默認的Bitmap對象
return defaultBitmapMap;
}
4.2 軟引用和弱引用
- 想避免OutOfMemory異常的發(fā)生蜈块,則可以使用軟引用。
- 想要提高應(yīng)用性能迷扇,盡快回收占用內(nèi)存更大的對象百揭,可以使用弱引用。
4.3 小Tips
- Context的引用谋梭,具體請看這篇Android Context詳解信峻。
- 避免創(chuàng)建不必要的對象:你要頻繁操作一個字符串時,使用StringBuffer代替String瓮床。
- 避免使用浮點數(shù):在Android設(shè)備中盹舞,浮點數(shù)會比整型慢兩倍。
- 循環(huán):盡量避免在for的條件參數(shù)(即())中訪問成員變量隘庄,調(diào)用方法踢步,例如:
for(int i=0;i<a.length;i++)//訪問成員變量,會慢很多丑掺。
for(int i=0;i<a.length();i++)//調(diào)用方法获印,也會慢很多。
正確的做法如下:
int b = a.length/int b=a.length();
for(int i=0;i<b;i++)//這樣循環(huán)體最快街州。
java還有一種訪問數(shù)組的方法,這種方法比普通循環(huán)多出4個字節(jié)兼丰,因為產(chǎn)生了一個額外的變量,如下列的a唆缴。
for(Foo a:Array){
m+=a.value;
}
-
了解并使用類庫:
當你在處理字串的時候鳍征,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊實現(xiàn)的方法面徽。這些方法都是使用C/C++實現(xiàn)的艳丛,比起Java循環(huán)快10到100倍匣掸。
android.text.format包下的Formatter類,提供了IP地址轉(zhuǎn)換氮双、文件大小轉(zhuǎn)換等方法碰酝;DateFormat類,提供了各種時間轉(zhuǎn)換戴差,都是非常高效的方法送爸。
TextUtils類,對于字符串處理Android為我們提供了一個簡單實用的TextUtils類造挽,如果處理比較簡單的內(nèi)容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類碱璃。
5.優(yōu)化—— 重復使用內(nèi)存資源
- 將已經(jīng)存在的內(nèi)存資源重新使用而避免去創(chuàng)建新的。最典型的使用就是**緩存(Cache)和池(Pool)饭入。
- 緩存:
一種是內(nèi)存緩存嵌器,一種是硬盤緩存。- 內(nèi)存緩存(LruCache):存在寶貴的內(nèi)存中谐丢。
- 硬盤緩存(DiskLruCache):存在硬盤中爽航。
- 池:
一種是對象池,一種是線程池乾忱。- 對象池:將用過的對象保存起來讥珍,等下一次使用時,再拿出來使用窄瘟, 從而在一定情況下減少創(chuàng)造對象所產(chǎn)生的開銷衷佃。但是不是任何對象都適合使用對象池,因為維護對象池也需要一定的開支蹄葱。一定要“維護對象池的開支”小于“創(chuàng)建對象的開支”氏义。
- 線程池:基本思想任然是對象池的思想,在開辟的一片空間里图云,存著很多線程惯悠,線程池的調(diào)度由池管理器來處理。當需要一個線程時竣况,從池中取一個克婶,用完后重新歸池,減少了創(chuàng)建線程的開支丹泉。
java提供了ExecutorService和Executors類情萤,我們可以用它們?nèi)?chuàng)建線程池。
6.優(yōu)化——回收不需要的內(nèi)存
回收主要是Dalvik的GC機制摹恨。下面來講一些具體的:
線程回收:
線程中涉及的任何東西GC都不能回收筋岛,所以線程很容易造成內(nèi)存泄露。
Thread t = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
System.out.println("thread is running...");
} catch (InterruptedException e) {
}
}
}
};
t.start();
t = null;
System.gc();
這個時候GC并不會回收這個線程睬塌,因為正在運行的線程屬于GCRoot的一種,并不會被GC進行回收。
- 游標回收:
Cursor是Android查詢數(shù)據(jù)后得到的一個管理數(shù)據(jù)集合的類揩晴,應(yīng)該在使用完之后立即手動進行關(guān)閉勋陪。一般使用如下:
Cursor cursor = null;
try {
cursor = mContext.getContentResolver().query(uri,null, null,null,null);
if(cursor != null) {
cursor.moveToFirst();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
注:在CursorAdapter中應(yīng)用時,我們不能直接關(guān)掉Cursor硫兰,而且CursorAdapter在Acivity結(jié)束時并沒有自動的將Cursor關(guān)閉掉诅愚,因此,需要在onDestroy函數(shù)中劫映,手動關(guān)閉违孝。
@Override
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
}
super.onDestroy();
}
-
Receiver(接收器)回收:
當在Activity進行了Receiver注冊時,需要注意最后要注銷泳赋。
即調(diào)用registerReceiver()雌桑,使用完畢后調(diào)用unregisterReceiver()。
一般在onDestroy()進行回收祖今。
@Override
protected void onDestroy() {
this.unregisterReceiver(receiver);
super.onDestroy();
}
-
Stream/File(流/文件)回收:
各種流/文件如:
InputStream/OutputStream校坑,SQListeOpenHelper,SQLiteDatabase千诬,Cursor耍目,F(xiàn)ile,I/O徐绑,Bitmap都需要關(guān)閉邪驮。
7.優(yōu)化——視圖布局優(yōu)化
- 使用HierarchyViewer查看并減少OverDraw。
- ViewStub標簽:可以使布局在可見與不可見之間切換傲茄,默認不可見的情況下不占用內(nèi)存毅访。
- include標簽:復用UI資源。
- merge標簽:解決include標簽的問題烫幕,減少一個層級俺抽,并且方便使用。
- 重用系統(tǒng)資源:使用系統(tǒng)自帶的資源(sdk\platforms\android-x\data\res)较曼,例如:
- id
android:id="@android:id/list" - 字符串
@android:string/yes - Style
android:textAppearance="?android:attr/textAppearanceMedium" - 顏色
android:background ="@android:color/transparent"
- id