Android 內(nèi)存優(yōu)化 (二)

Bitmap內(nèi)存優(yōu)化:
Bitmap是內(nèi)存消耗大戶屑柔,絕大多數(shù)的OOM崩潰都是在操作Bitmap時(shí)產(chǎn)生的,下面來看看如何幾個(gè)處理圖片的方法:

圖片顯示優(yōu)化:
圖片服務(wù)器配合加載對應(yīng)尺寸的圖片您单。
根據(jù)屏幕尺寸和用戶體驗(yàn)地最低值選擇圖片壓縮尺寸和質(zhì)量踢代。

圖片大小優(yōu)化:
直接使用ImageView顯示bitmap會(huì)占用較多資源,特別是圖片較大的時(shí)候,可能導(dǎo)致崩潰岳守。
使用BitmapFactory.Options設(shè)置inSampleSize, 這樣做可以減少對系統(tǒng)資源的要求。
屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一碌冶,即如果這個(gè)值為2湿痢,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4扑庞。

代碼:
BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
bitmapFactoryOptions.inJustDecodeBounds = true;
bitmapFactoryOptions.inSampleSize = 2;
// 這里一定要將其設(shè)置回false譬重,因?yàn)橹拔覀儗⑵湓O(shè)置成了true
// 設(shè)置inJustDecodeBounds為true后,decodeFile并不分配空間罐氨,即臀规,BitmapFactory解碼出來的Bitmap為Null,但可計(jì)算出原始圖片的長度和寬度
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);

圖片像素優(yōu)化:
Android中圖片有四種屬性,分別是:
ALPHA_8:每個(gè)像素占用1byte內(nèi)存
ARGB_4444:每個(gè)像素占用2byte內(nèi)存
ARGB_8888:每個(gè)像素占用4byte內(nèi)存 (默認(rèn))
RGB_565:每個(gè)像素占用2byte內(nèi)存

Android默認(rèn)的顏色模式為ARGB_8888栅隐,這個(gè)顏色模式色彩最細(xì)膩塔嬉,顯示質(zhì)量最高。但同樣的租悄,占用的內(nèi)存也最大邑遏。 所以在對圖片效果不是特別高的情況下使用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);  
}  

圖片回收優(yōu)化:
使用Bitmap過后恰矩,就需要及時(shí)的調(diào)用Bitmap.recycle()方法來釋放Bitmap占用的內(nèi)存空間记盒,而不要等Android系統(tǒng)來進(jìn)行釋放。
下面是釋放Bitmap的示例代碼片段外傅。


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

另外也可以使用lru算法來控制bitmap對內(nèi)存占用纪吮。

捕獲異常優(yōu)化:
經(jīng)過上面這些優(yōu)化后還會(huì)存在報(bào)OOM的風(fēng)險(xiǎn),所以下面需要一道最后的關(guān)卡——捕獲OOM異常:


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

Java四種引用類型:

強(qiáng)引用(strong reference)
如:Object object=new Object()碾盟,object就是一個(gè)強(qiáng)引用了。當(dāng)內(nèi)存空間不足技竟,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤冰肴,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對象來解

決內(nèi)存不足問題榔组。
軟引用(SoftReference)
只有內(nèi)存不夠時(shí)才回收,常用于緩存熙尉;當(dāng)內(nèi)存達(dá)到一個(gè)閥值,GC就會(huì)去回收它搓扯;
弱引用(WeakReference)
弱引用的對象擁有更短暫的生命周期检痰。在垃圾回收器線程掃描它 所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象锨推,不管當(dāng)前內(nèi)存空間足夠與否铅歼,都會(huì)回收它的內(nèi)存公壤。
虛引用(PhantomReference)
"虛引用"顧名思義,就是形同虛設(shè)椎椰,與其他幾種引用都不同厦幅,虛引用并不會(huì)決定對象的生命周期。如果一個(gè)對象僅持有虛引用慨飘,那么它就和沒有任何引用一樣慨削,在任何時(shí)候都可能被垃圾回收

Android 2.3 引用回收變化:

注意:對于SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案套媚,現(xiàn)在已經(jīng)不推薦使用了。自Android2.3版本(API Level 9)開始磁椒,垃圾回收器更著重于對軟/弱引用的回收

堤瘤,那么依靠軟、弱引用來優(yōu)化內(nèi)存回收已經(jīng)不可靠了浆熔。

到底什么時(shí)候使用軟引用遏考,什么時(shí)候使用弱引用呢漱抓?

個(gè)人認(rèn)為,如果只是想避免OutOfMemory異常的發(fā)生,則可以使用軟引用办铡。如果對于應(yīng)用的性能更在意,想盡快回收一些占用內(nèi)存比較大的對象搬素,則可以使用弱引用绷耍。
還有就是可以根據(jù)對象是否經(jīng)常使用來判斷。如果該對象可能會(huì)經(jīng)常使用的忽刽,就盡量用軟引用天揖。如果該對象不被使用的可能性更大些,就可以用弱引用跪帝。
另外今膊,和弱引用功能類似的是WeakHashMap。WeakHashMap對于一個(gè)給定的鍵伞剑,其映射的存在并不阻止垃圾回收器對該鍵的回收斑唬,回收以后,其條目從映射中有效地移除黎泣。WeakHashMap使用

ReferenceQueue實(shí)現(xiàn)的這種機(jī)制恕刘。

static final修飾符使用的好處

讓我們來看看這兩段在類前面的聲明:
static int intVal = 42;
static String strVal = "Hello, world!";
編譯器會(huì)生成一個(gè)叫做clinit的初始化類的方法,當(dāng)類第一次被使用的時(shí)候這個(gè)方法會(huì)被執(zhí)行抒倚。方法會(huì)將42賦給intVal雪营,然后把一個(gè)指向類中常量表 的引用賦給strVal。當(dāng)以后要用到這些值

的時(shí)候衡便,會(huì)在成員變量表中查找到他們献起。 下面我們做些改進(jìn)洋访,使用“final”關(guān)鍵字:
static final int intVal = 42;
static final String strVal = "Hello, world!";
現(xiàn)在,類不再需要clinit方法谴餐,因?yàn)樵诔蓡T變量初始化的時(shí)候姻政,會(huì)將常量直接保存到類文件中。用到intVal的代碼被直接替換成42岂嗓,而使用strVal的會(huì)指向一個(gè)字符串常量汁展,而不是使用成員

變量。
將一個(gè)方法或類聲明為final不會(huì)帶來性能的提升厌殉,但是會(huì)幫助編譯器優(yōu)化代碼食绿。舉例說,如果編譯器知道一個(gè)getter方法不會(huì)被重載公罕,那么編譯器會(huì)對其采用內(nèi)聯(lián)調(diào)用器紧。
你也可以將本地變量聲明為final,同樣楼眷,這也不會(huì)帶來性能的提升铲汪。使用“final”只能使本地變量看起來更清晰些(但是也有些時(shí)候這是必須的,比如在使用匿名內(nèi)部類的時(shí)候)罐柳。

靜態(tài)方法代替虛擬方法

如果不需要訪問某對象的字段掌腰,將方法設(shè)置為靜態(tài),調(diào)用會(huì)加速15%到20%张吉。這也是一種好的做法齿梁,因?yàn)槟憧梢詮姆椒暶髦锌闯稣{(diào)用該方法不需要更新此對象的狀態(tài)。

減少不必要的全局變量

盡量避免static成員變量引用資源耗費(fèi)過多的實(shí)例,比如Context因?yàn)镃ontext的引用超過它本身的生命周期肮蛹,會(huì)導(dǎo)致Context泄漏士飒。所以盡量使用Application這種Context類型。 你可以通過調(diào)

用Context.getApplicationContext()或 Activity.getApplication()輕松得到Application對象蔗崎。

避免創(chuàng)建不必要的對象

最常見的例子就是當(dāng)你要頻繁操作一個(gè)字符串時(shí)酵幕,使用StringBuffer代替String。
對于所有所有基本類型的組合:int數(shù)組比Integer數(shù)組好缓苛,這也概括了一個(gè)基本事實(shí)芳撒,兩個(gè)平行的int數(shù)組比 (int,int)對象數(shù)組性能要好很多。
總體來說未桥,就是避免創(chuàng)建短命的臨時(shí)對象笔刹。減少對象的創(chuàng)建就能減少垃圾收集,進(jìn)而減少對用戶體驗(yàn)的影響冬耿。

避免內(nèi)部Getters/Setters

在Android中舌菜,虛方法調(diào)用的代價(jià)比直接字段訪問高昂許多。通常根據(jù)面向?qū)ο笳Z言的實(shí)踐亦镶,在公共接口中使用Getters和Setters是有道理的日月,但在一個(gè)字段經(jīng)常被訪問的類中宜采用直接訪問

袱瓮。

避免使用浮點(diǎn)數(shù)

通常的經(jīng)驗(yàn)是,在Android設(shè)備中爱咬,浮點(diǎn)數(shù)會(huì)比整型慢兩倍尺借。

使用實(shí)體類比接口好

假設(shè)你有一個(gè)HashMap對象,你可以將它聲明為HashMap或者M(jìn)ap:
Map map1 = new HashMap();
HashMap map2 = new HashMap();
哪個(gè)更好呢精拟?
按照傳統(tǒng)的觀點(diǎn)Map會(huì)更好些燎斩,因?yàn)檫@樣你可以改變他的具體實(shí)現(xiàn)類,只要這個(gè)類繼承自Map接口蜂绎。傳統(tǒng)的觀點(diǎn)對于傳統(tǒng)的程序是正確的栅表,但是它并不適合嵌入式系統(tǒng)。調(diào)用一個(gè)接口的引用會(huì)

比調(diào)用實(shí)體類的引用多花費(fèi)一倍的時(shí)間师枣。如果HashMap完全適合你的程序怪瓶,那么使用Map就沒有什么價(jià)值。如果有些地方你不能確定坛吁,先避免使用Map,剩下的交給IDE提供的重構(gòu)功能好了铐尚。(當(dāng)

然公共API是一個(gè)例外:一個(gè)好的API常常會(huì)犧牲一些性能)

避免使用枚舉

枚舉變量非常方便拨脉,但不幸的是它會(huì)犧牲執(zhí)行的速度和并大幅增加文件體積。
使用枚舉變量可以讓你的API更出色宣增,并能提供編譯時(shí)的檢查玫膀。所以在通常的時(shí)候你毫無疑問應(yīng)該為公共API選擇枚舉變量。但是當(dāng)性能方面有所限制的時(shí)候爹脾,你就應(yīng)該避免這種做法了帖旨。

for循環(huán)已經(jīng)foreach 循環(huán)

在java1.5中引入的for-each語法。編譯器會(huì)將對數(shù)組的引用和數(shù)組的長度保存到本地變量中灵妨,這對訪問數(shù)組元素非常好解阅。 但是編譯器還會(huì)在每次循環(huán)中產(chǎn)生一個(gè)額外的對本地變量的存儲(chǔ)操

作(如下面例子中的變量a),這樣會(huì)比普通循環(huán)多出4個(gè)字節(jié)泌霍,速度要稍微慢一些:
使用for循環(huán)時(shí)货抄,判斷條件盡量使用局部變量和常量。

了解并使用類庫

選擇Library中的代碼而非自己重寫朱转,除了通常的那些原因外蟹地,考慮到系統(tǒng)空閑時(shí)會(huì)用匯編代碼調(diào)用來替代library方法,這可能比JIT中生成的等價(jià)的最好的Java代碼還要好藤为。
當(dāng)你在處理字串的時(shí)候怪与,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊實(shí)現(xiàn)的方法缅疟。這些方法都是使用C/C++實(shí)現(xiàn)的分别,比起Java循環(huán)快10到100倍遍愿。
System.arraycopy方法在有JIT的Nexus One上,自行編碼的循環(huán)快9倍茎杂。
android.text.format包下的Formatter類错览,提供了IP地址轉(zhuǎn)換、文件大小轉(zhuǎn)換等方法煌往;DateFormat類倾哺,提供了各種時(shí)間轉(zhuǎn)換,都是非常高效的方法刽脖。
TextUtils類羞海,對于字符串處理Android為我們提供了一個(gè)簡單實(shí)用的TextUtils類,如果處理比較簡單的內(nèi)容不用去思考正則表達(dá)式不妨試試這個(gè)在android.text.TextUtils的類
高性能MemoryFile類曲管,很多人抱怨Android處理底層I/O性能不是很理想却邓,如果不想使用NDK則可以通過MemoryFile類實(shí)現(xiàn)高性能的文件讀寫操作。MemoryFile適用于哪些地方呢院水?對于I/O需要

頻繁操作的腊徙,主要是和外部存儲(chǔ)相關(guān)的I/O操作,MemoryFile通過將 NAND或SD卡上的文件檬某,分段映射到內(nèi)存中進(jìn)行修改處理撬腾,這樣就用高速的RAM代替了ROM或SD卡,性能自然提高不少恢恼,對于

Android手機(jī)而言同時(shí)還減少了電量消耗民傻。該類實(shí)現(xiàn)的功能不是很多,直接從Object上繼承场斑,通過JNI的方式直接在C底層執(zhí)行漓踢。

內(nèi)存緩存(LruCache):

以犧牲寶貴的應(yīng)用內(nèi)存為代價(jià),內(nèi)存緩存提供了快速的Bitmap訪問方式漏隐。系統(tǒng)提供的LruCache類是非常適合用作緩存Bitmap任務(wù)的喧半,它將最近被引用到的對象存儲(chǔ)在一個(gè)強(qiáng)引用的

LinkedHashMap中,并且在緩存超過了指定大小之后將最近不常使用的對象釋放掉青责。
注意:以前有一個(gè)非常流行的內(nèi)存緩存實(shí)現(xiàn)是SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案薯酝,然而現(xiàn)在已經(jīng)不推薦使用了。自Android2.3版本(API Level 9)開始爽柒,垃

圾回收器更著重于對軟/弱引用的回收吴菠,這使得上述的方案相當(dāng)無效。

硬盤緩存(DiskLruCache):

一個(gè)內(nèi)存緩存對加速訪問最近瀏覽過的Bitmap非常有幫助浩村,但是你不能局限于內(nèi)存中的可用圖片做葵。GridView這樣有著更大的數(shù)據(jù)集的組件可以很輕易消耗掉內(nèi)存緩存。你的應(yīng)用有可能在執(zhí)行

其他任務(wù)(如打電話)的時(shí)候被打斷心墅,并且在后臺(tái)的任務(wù)有可能被殺死或者緩存被釋放酿矢。一旦用戶重新聚焦(resume)到你的應(yīng)用榨乎,你得再次處理每一張圖片。
在這種情況下瘫筐,硬盤緩存可以用來存儲(chǔ)Bitmap并在圖片被內(nèi)存緩存釋放后減小圖片加載的時(shí)間(次數(shù))蜜暑。當(dāng)然,從硬盤加載圖片比內(nèi)存要慢策肝,并且應(yīng)該在后臺(tái)線程進(jìn)行肛捍,因?yàn)橛脖P讀取的時(shí)間是

不可預(yù)知的。

對象池:
對象池使用的基本思路是:將用過的對象保存起來之众,等下一次需要這種對象的時(shí)候拙毫,再拿出來重復(fù)使用,從而在一定程度上減少頻繁創(chuàng)建對象所造成的開銷棺禾。 并非所有對象都適合拿來池化―

―因?yàn)榫S護(hù)對象池也要造成一定開銷缀蹄。對生成時(shí)開銷不大的對象進(jìn)行池化,反而可能會(huì)出現(xiàn)“維護(hù)對象池的開銷”大于“生成新對象的開銷”膘婶,從而使性能降低的情況缺前。但是對于生成時(shí)開銷

可觀的對象,池化技術(shù)就是提高性能的有效策略了悬襟。

線程池:
線程池的基本思想還是一種對象池的思想衅码,開辟一塊內(nèi)存空間,里面存放了眾多(未死亡)的線程古胆,池中線程執(zhí)行調(diào)度由池管理器來處理肆良。當(dāng)有線程任務(wù)時(shí)筛璧,從池中取一個(gè)逸绎,執(zhí)行完成后線程對

象歸池,這樣可以避免反復(fù)創(chuàng)建線程對象所帶來的性能開銷夭谤,節(jié)省了系統(tǒng)的資源棺牧。
比如:一個(gè)應(yīng)用要和網(wǎng)絡(luò)打交道,有很多步驟需要訪問網(wǎng)絡(luò)朗儒,為了不阻塞主線程颊乘,每個(gè)步驟都創(chuàng)建個(gè)線程,在線程中和網(wǎng)絡(luò)交互醉锄,用線程池就變的簡單乏悄,線程池是對線程的一種封裝,讓線程

用起來更加簡便恳不,只需要?jiǎng)?chuàng)一個(gè)線程池檩小,把這些步驟像任務(wù)一樣放進(jìn)線程池,在程序銷毀時(shí)只要調(diào)用線程池的銷毀函數(shù)即可烟勋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末规求,一起剝皮案震驚了整個(gè)濱河市筐付,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阻肿,老刑警劉巖瓦戚,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丛塌,居然都是意外死亡较解,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門姨伤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哨坪,“玉大人,你說我怎么就攤上這事乍楚〉北啵” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵徒溪,是天一觀的道長忿偷。 經(jīng)常有香客問我,道長臊泌,這世上最難降的妖魔是什么鲤桥? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮渠概,結(jié)果婚禮上茶凳,老公的妹妹穿的比我還像新娘。我一直安慰自己播揪,他們只是感情好贮喧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猪狈,像睡著了一般箱沦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雇庙,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天谓形,我揣著相機(jī)與錄音,去河邊找鬼疆前。 笑死寒跳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竹椒。 我是一名探鬼主播童太,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了康愤?” 一聲冷哼從身側(cè)響起儡循,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎征冷,沒想到半個(gè)月后择膝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡检激,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年肴捉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叔收。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡齿穗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饺律,到底是詐尸還是另有隱情窃页,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布复濒,位于F島的核電站脖卖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏巧颈。R本人自食惡果不足惜畦木,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砸泛。 院中可真熱鬧十籍,春花似錦、人聲如沸唇礁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垒迂。三九已至械姻,卻和暖如春妒蛇,著一層夾襖步出監(jiān)牢的瞬間机断,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工绣夺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吏奸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓陶耍,卻偏偏與公主長得像奋蔚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容