多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 互聯網 > ANDROID內存優化(大匯總――中)

ANDROID內存優化(大匯總――中)

來源:程序員人生   發布時間:2014-10-11 08:00:01 閱讀次數:2640次

轉載請注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!


寫在最前:

本文的思路主要借鑒了2014年AnDevCon開發者大會的一個演講PPT,加上把網上搜集的各種內存零散知識點進行匯總、挑選、簡化后整理而成。

所以我將本文定義為一個工具類的文章,如果你在ANDROID開發中遇到關于內存問題,或者馬上要參加面試,或者就是單純的學習或復習一下內存相關知識,都歡迎閱讀。(本文最后我會盡量列出所參考的文章)。


OOM:


內存泄露可以引發很多的問題:

1.程序卡頓,響應速度慢(內存占用高時JVM虛擬機會頻繁觸發GC)

2.莫名消失(當你的程序所占內存越大,它在后臺的時候就越可能被干掉。反之內存占用越小,在后臺存在的時間就越長)

3.直接崩潰(OutOfMemoryError)


ANDROID內存面臨的問題:

1.有限的堆內存,原始只有16M

2.內存大小消耗等根據設備,操作系統等級,屏幕尺寸的不同而不同

3.程序不能直接控制

4.支持后臺多任務處理(multitasking)

5.運行在虛擬機之上


5R:

本文主要通過如下的5R方法來對ANDROID內存進行優化:


1.Reckon(計算)

首先需要知道你的app所消耗內存的情況,知己知彼才能百戰不殆

2.Reduce(減少)

消耗更少的資源

3.Reuse(重用)

當第一次使用完以后,盡量給其他的使用

5.Recycle(回收)

返回資源給生產流

4.Review(檢查)

回顧檢查你的程序,看看設計或代碼有什么不合理的地方。



Reckon:


關于內存簡介,和Reckon(內存計算)的內容請看上一篇文章:ANDROID內存優化(大匯總――上)



Reduce :


Reduce的意思就是減少,直接減少內存的使用是最有效的優化方式。

下面來看看有哪些方法可以減少內存使用:


Bitmap
Bitmap是內存消耗大戶,絕大多數的OOM崩潰都是在操作Bitmap時產生的,下面來看看如何幾個處理圖片的方法:


圖片顯示:

我們需要根據需求去加載圖片的大小。

例如在列表中僅用于預覽時加載縮略圖(thumbnails )。

只有當用戶點擊具體條目想看詳細信息的時候,這時另啟動一個fragment/activity/對話框等等,去顯示整個圖片


圖片大小:

直接使用ImageView顯示bitmap會占用較多資源,特別是圖片較大的時候,可能導致崩潰。 
使用BitmapFactory.Options設置inSampleSize, 這樣做可以減少對系統資源的要求。 
屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一,即如果這個值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4。 

BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options(); bitmapFactoryOptions.inJustDecodeBounds = true; bitmapFactoryOptions.inSampleSize = 2; // 這里一定要將其設置回false,因為之前我們將其設置成了true // 設置inJustDecodeBounds為true后,decodeFile并不分配空間,即,BitmapFactory解碼出來的Bitmap為Null,但可計算出原始圖片的長度和寬度 options.inJustDecodeBounds = false; Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);


圖片像素:

Android中圖片有四種屬性,分別是:
ALPHA_8:每個像素占用1byte內存 
ARGB_4444:每個像素占用2byte內存 
ARGB_8888:每個像素占用4byte內存 (默認)
RGB_565:每個像素占用2byte內存 

Android默認的顏色模式為ARGB_8888,這個顏色模式色彩最細膩,顯示質量最高。但同樣的,占用的內存也最大。 所以在對圖片效果不是特別高的情況下使用RGB_565(565沒有透明度屬性),如下:
publicstaticBitmapreadBitMap(Contextcontext, intresId) { BitmapFactory.Optionsopt = newBitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; //獲取資源圖片 InputStreamis = context.getResources().openRawResource(resId); returnBitmapFactory.decodeStream(is, null, opt); }

圖片回收:

使用Bitmap過后,就需要及時的調用Bitmap.recycle()方法來釋放Bitmap占用的內存空間,而不要等Android系統來進行釋放。

下面是釋放Bitmap的示例代碼片段。

// 先判斷是否已經回收 if(bitmap != null && !bitmap.isRecycled()){ // 回收并且置為null bitmap.recycle(); bitmap = null; } System.gc();

捕獲異常:

經過上面這些優化后還會存在報OOM的風險,所以下面需要一道最后的關卡――捕獲OOM異常:

Bitmap bitmap = null; try { // 實例化Bitmap bitmap = BitmapFactory.decodeFile(path); } catch (OutOfMemoryError e) { // 捕獲OutOfMemoryError,避免直接崩潰 } if (bitmap == null) { // 如果實例化失敗 返回默認的Bitmap對象 return defaultBitmapMap; }



修改對象引用類型:


引用類型:

引用分為四種級別,這四種級別由高到低依次為:強引用>軟引用>弱引用>虛引用。

強引用(strong reference)
如:Object object=new Object(),object就是一個強引用了。當內存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足問題。

軟引用(SoftReference)
只有內存不夠時才回收,常用于緩存;當內存達到一個閥值,GC就會去回收它;

弱引用(WeakReference)   

弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它 所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。 


虛引用(PhantomReference)   

"虛引用"顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。  


軟引用和弱引用的應用實例:

注意:對于SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案,現在已經不推薦使用了。自Android2.3版本(API Level 9)開始,垃圾回收器更著重于對軟/弱引用的回收,所以下面的內容可以選擇忽略。

在Android應用的開發中,為了防止內存溢出,在處理一些占用內存大而且聲明周期較長的對象時候,可以盡量應用軟引用和弱引用技術。

下面以使用軟引用為例來詳細說明(弱引用的使用方式與軟引用是類似的):

假設我們的應用會用到大量的默認圖片,而且這些圖片很多地方會用到。如果每次都去讀取圖片,由于讀取文件需要硬件操作,速度較慢,會導致性能較低。所以我們考慮將圖片緩存起來,需要的時候直接從內存中讀取。但是,由于圖片占用內存空間比較大,緩存很多圖片需要很多的內存,就可能比較容易發生OutOfMemory異常。這時,我們可以考慮使用軟引用技術來避免這個問題發生。

首先定義一個HashMap,保存軟引用對象。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
再來定義一個方法,保存Bitmap的軟引用到HashMap。

public void addBitmapToCache(String path) { // 強引用的Bitmap對象 Bitmap bitmap = BitmapFactory.decodeFile(path); // 軟引用的Bitmap對象 SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap); // 添加該對象到Map中使其緩存 imageCache.put(path, softBitmap); }
獲取的時候,可以通過SoftReference的get()方法得到Bitmap對象。
public Bitmap getBitmapByPath(String path) { // 從緩存中取軟引用的Bitmap對象 SoftReference<Bitmap> softBitmap = imageCache.get(path); // 判斷是否存在軟引用 if (softBitmap == null) { return null; } // 取出Bitmap對象,如果由于內存不足Bitmap被回收,將取得空 Bitmap bitmap = softBitmap.get(); return bitmap; }
使用軟引用以后,在OutOfMemory異常發生之前,這些緩存的圖片資源的內存空間可以被釋放掉的,從而避免內存達到上限,避免Crash發生。

需要注意的是,在垃圾回收器對這個Java對象回收前,SoftReference類所提供的get方法會返回Java對象的強引用,一旦垃圾線程回收該Java對象之后,get方法將返回null。所以在獲取軟引用對象的代碼中,一定要判斷是否為null,以免出現NullPointerException異常導致應用崩潰。


到底什么時候使用軟引用,什么時候使用弱引用呢?

個人認為,如果只是想避免OutOfMemory異常的發生,則可以使用軟引用。如果對于應用的性能更在意,想盡快回收一些占用內存比較大的對象,則可以使用弱引用。

還有就是可以根據對象是否經常使用來判斷。如果該對象可能會經常使用的,就盡量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱引用。

另外,和弱引用功能類似的是WeakHashMap。WeakHashMap對于一個給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的回收,回收以后,其條目從映射中有效地移除。WeakHashMap使用ReferenceQueue實現的這種機制。


其他小tips:

對常量使用static final修飾符

讓我們來看看這兩段在類前面的聲明:

static int intVal = 42;
static String strVal = "Hello, world!";
編譯器會生成一個叫做clinit的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將42賦給intVal,然后把一個指向類中常量表 的引用賦給strVal。當以后要用到這些值的時候,會在成員變量表中查找到他們。 下面我們做些改進,使用“final”關鍵字:

static final int intVal = 42;
static final String strVal = "Hello, world!";

現在,類不再需要clinit方法,因為在成員變量初始化的時候,會將常量直接保存到類文件中。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字符串常量,而不是使用成員變量。

將一個方法或類聲明為final不會帶來性能的提升,但是會幫助編譯器優化代碼。舉例說,如果編譯器知道一個getter方法不會被重載,那么編譯器會對其采用內聯調用。

你也可以將本地變量聲明為final,同樣,這也不會帶來性能的提升。使用“final”只能使本地變量看起來更清晰些(但是也有些時候這是必須的,比如在使用匿名內部類的時候)。

靜態方法代替虛擬方法

如果不需要訪問某對象的字段,將方法設置為靜態,調用會加速15%到20%。這也是一種好的做法,因為你可以從方法聲明中看出調用該方法不需要更新此對象的狀態。


減少不必要的全局變量

盡量避免static成員變量引用資源耗費過多的實例,比如Context

因為Context的引用超過它本身的生命周期,會導致Context泄漏。所以盡量使用Application這種Context類型。 你可以通過調用Context.getApplicationContext()或 Activity.getApplication()輕松得到Application對象。 

避免創建不必要的對象

最常見的例子就是當你要頻繁操作一個字符串時,使用StringBuffer代替String。

對于所有所有基本類型的組合:int數組比Integer數組好,這也概括了一個基本事實,兩個平行的int數組比 (int,int)對象數組性能要好很多。

總體來說,就是避免創建短命的臨時對象。減少對象的創建就能減少垃圾收集,進而減少對用戶體驗的影響。


避免內部Getters/Setters
在Android中,虛方法調用的代價比直接字段訪問高昂許多。通常根據面向對象語言的實踐,在公共接口中使用Getters和Setters是有道理的,但在一個字段經常被訪問的類中宜采用直接訪問。

避免使用浮點數

通常的經驗是,在Android設備中,浮點數會比整型慢兩倍。


使用實體類比接口好

假設你有一個HashMap對象,你可以將它聲明為HashMap或者Map:

Map map1 = new HashMap(); HashMap map2 = new HashMap();

哪個更好呢?

按照傳統的觀點Map會更好些,因為這樣你可以改變他的具體實現類,只要這個類繼承自Map接口。傳統的觀點對于傳統的程序是正確的,但是它并不適合嵌入式系統。調用一個接口的引用會比調用實體類的引用多花費一倍的時間。如果HashMap完全適合你的程序,那么使用Map就沒有什么價值。如果有些地方你不能確定,先避免使用Map,剩下的交給IDE提供的重構功能好了。(當然公共API是一個例外:一個好的API常常會犧牲一些性能)


避免使用枚舉

枚舉變量非常方便,但不幸的是它會犧牲執行的速度和并大幅增加文件體積。

使用枚舉變量可以讓你的API更出色,并能提供編譯時的檢查。所以在通常的時候你毫無疑問應該為公共API選擇枚舉變量。但是當性能方面有所限制的時候,你就應該避免這種做法了。


for循環

訪問成員變量比訪問本地變量慢得多,如下面一段代碼:

for(int i =0; i < this.mCount; i++) {}

永遠不要在for的第二個條件中調用任何方法,如下面一段代碼:

for(int i =0; i < this.getCount(); i++) {}

對上面兩個例子最好改為:

int count = this.mCount; / int count = this.getCount(); for(int i =0; i < count; i++) {}
在java1.5中引入的for-each語法。編譯器會將對數組的引用和數組的長度保存到本地變量中,這對訪問數組元素非常好。 但是編譯器還會在每次循環中產生一個額外的對本地變量的存儲操作(如下面例子中的變量a),這樣會比普通循環多出4個字節,速度要稍微慢一些:

for (Foo a : mArray) { sum += a.mSplat; }

了解并使用類庫

選擇Library中的代碼而非自己重寫,除了通常的那些原因外,考慮到系統空閑時會用匯編代碼調用來替代library方法,這可能比JIT中生成的等價的最好的Java代碼還要好。

當你在處理字串的時候,不要吝惜使用String.indexOf()String.lastIndexOf()等特殊實現的方法。這些方法都是使用C/C++實現的,比起Java循環快10到100倍。

System.arraycopy方法在有JIT的Nexus One上,自行編碼的循環快9倍。

android.text.format包下的Formatter類,提供了IP地址轉換、文件大小轉換等方法;DateFormat類,提供了各種時間轉換,都是非常高效的方法。

TextUtils類,對于字符串處理Android為我們提供了一個簡單實用的TextUtils類,如果處理比較簡單的內容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類

高性能MemoryFile類,很多人抱怨Android處理底層I/O性能不是很理想,如果不想使用NDK則可以通過MemoryFile類實現高性能的文件讀寫操作。MemoryFile適用于哪些地方呢?對于I/O需要頻繁操作的,主要是和外部存儲相關的I/O操作,MemoryFile通過將 NAND或SD卡上的文件,分段映射到內存中進行修改處理,這樣就用高速的RAM代替了ROM或SD卡,性能自然提高不少,對于Android手機而言同時還減少了電量消耗。該類實現的功能不是很多,直接從Object上繼承,通過JNI的方式直接在C底層執行。



Reuse:


Reuse重用,減少內存消耗的重要手段之一。
核心思路就是將已經存在的內存資源重新使用而避免去創建新的,最典型的使用就是緩存(Cache池(Pool)

Bitmap緩存:

Bitmap緩存分為兩種:

一種是內存緩存,一種是硬盤緩存。


內存緩存(LruCache):

以犧牲寶貴的應用內存為代價,內存緩存提供了快速的Bitmap訪問方式。系統提供的LruCache類是非常適合用作緩存Bitmap任務的,它將最近被引用到的對象存儲在一個強引用的LinkedHashMap中,并且在緩存超過了指定大小之后將最近不常使用的對象釋放掉。

注意以前有一個非常流行的內存緩存實現是SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案,然而現在已經不推薦使用了。自Android2.3版本(API Level 9)開始,垃圾回收器更著重于對軟/弱引用的回收,這使得上述的方案相當無效。


硬盤緩存(DiskLruCache):

一個內存緩存對加速訪問最近瀏覽過的Bitmap非常有幫助,但是你不能局限于內存中的可用圖片。GridView這樣有著更大的數據集的組件可以很輕易消耗掉內存緩存。你的應用有可能在執行其他任務(如打電話)的時候被打斷,并且在后臺的任務有可能被殺死或者緩存被釋放。一旦用戶重新聚焦(resume)到你的應用,你得再次處理每一張圖片。

在這種情況下,硬盤緩存可以用來存儲Bitmap并在圖片被內存緩存釋放后減小圖片加載的時間(次數)。當然,從硬盤加載圖片比內存要慢,并且應該在后臺線程進行,因為硬盤讀取的時間是不可預知的。

注意:如果訪問圖片的次數非常頻繁,那么ContentProvider可能更適合用來存儲緩存圖片,例如Image Gallery這樣的應用程序。

更多關于內存緩存和硬盤緩存的內容請看Google官方教程https://developer.android.com/develop/index.html

圖片緩存的開源項目:
對于圖片的緩存現在都傾向于使用開源項目,這里我列出幾個我搜到的:

1. Android-Universal-Image-Loader 圖片緩存

目前使用最廣泛的圖片緩存,支持主流圖片緩存的絕大多數特性。
項目地址:https://github.com/nostra13/Android-Universal-Image-Loader

 

2. picasso square開源的圖片緩存
項目地址:https://github.com/square/picasso
特點:(1)可以自動檢測adapter的重用并取消之前的下載
(2)圖片變換
(3)可以加載本地資源
(4)可以設置占位資源
(5)支持debug模式

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生

------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久www免费人成看片入口 | 欧美黑粗特黄午夜大片 | 国产区精品福利在线观看精品 | 久草在线观看首页 | 国产dvd毛片在线视频 | 亚洲精品在线视频观看 | 激情区小说区偷拍区图片区 | 手机在线看片国产日韩生活片 | 欧美另类丰满69xxxxx | 免费国产福利 | 亚洲性影院 | 久久久高清日本道免费观看 | 图片区 日韩 欧美 亚洲 | 国产欧美精品一区二区三区 | 欧美精品xxxxbbbb | 国产精品久久久久久久久免费 | 在线观看视频在线观看 | 国产精品久久免费 | 国产亚洲久久 | 九九精品视频一区二区三区 | 视频免费视频观看网站 | 精品久久久中文字幕一区 | 国产激情久久久久久影院 | 毛片网此 | 成人国产一区二区三区精品 | 亚洲成人黄色网 | 国内精品视频在线观看 | 欧美色视频日本 | 人阁色第四影院在线观看 | 五月天在线播放视频在线 | 欧美a色 | 欧美日韩亚洲精品一区 | 亚洲欧美激情另类 | 日韩欧美精品有码在线观看 | 大色虫成人午夜在线观看 | 中文字幕无线精品乱码一区 | 免费中日高清无专码有限公司 | 亚洲精品国产第一区第二区国 | 国产亚洲精品日韩已满十八 | 久久久久国产精品免费免费 | 中文版在线乱码在线看 |