曹幫琴,徐 昊
(信陽(yáng)職業(yè)技術(shù)學(xué)院數(shù)學(xué)與計(jì)算機(jī)科學(xué)學(xué)院,河南信陽(yáng)464000)
作為一款便攜式終端平臺(tái),Android是以Linux內(nèi)核為基礎(chǔ)的開(kāi)放源碼操作系統(tǒng)[1],主要應(yīng)用于便攜設(shè)備,目前Android已經(jīng)成為全球最受歡迎的智能手機(jī)平臺(tái).Android系統(tǒng)以其開(kāi)源、開(kāi)放和優(yōu)異便捷的開(kāi)發(fā)架構(gòu),吸引了眾多程序開(kāi)發(fā)者,其運(yùn)行環(huán)境由核心庫(kù)集和Dalvik虛擬機(jī)組成[2].核心庫(kù)集提供了Java編程語(yǔ)言核心庫(kù)的大多數(shù)功能,每一個(gè)應(yīng)用程序都在它自己的進(jìn)程中運(yùn)行,擁有一個(gè)獨(dú)立的Dalvik虛擬機(jī)實(shí)例.在編寫(xiě)應(yīng)用程序時(shí),可以訪問(wèn)Android提供的API[3],調(diào)用它的C/C++開(kāi)發(fā)庫(kù)及核心應(yīng)用程序包.
隨著移動(dòng)互聯(lián)網(wǎng)技術(shù)的進(jìn)步,大量的Android應(yīng)用都會(huì)使用分辨率較高的圖片,這些圖片大多以位圖(Bitmap)的形式出現(xiàn).位圖是由稱作像素的單個(gè)點(diǎn)組成,圖像數(shù)據(jù)采用非壓縮格式,加載到內(nèi)存時(shí)需要占用較大的存儲(chǔ)空間.當(dāng)Android應(yīng)用加載大量圖片到內(nèi)存時(shí),則會(huì)有很大概率導(dǎo)致應(yīng)用內(nèi)存溢出[4].如果工程師對(duì)Android的內(nèi)存管理機(jī)制不了解,很容易造成系統(tǒng)的OOM(Out Of Memory)錯(cuò)誤,即內(nèi)存溢出錯(cuò)誤.因此,優(yōu)化Bitmap的使用方法是開(kāi)發(fā)Android應(yīng)用程序中比較重要的內(nèi)容.
對(duì)于一些大量使用Bitmap的項(xiàng)目,影響性能的瓶頸主要是Android系統(tǒng)內(nèi)存管理機(jī)制默認(rèn)分配給應(yīng)用的堆內(nèi)存不足.對(duì)于Android平臺(tái),其托管層使用的Dalvik Java VM,還有很多地方可以進(jìn)行優(yōu)化處理.堆(HEAP)是VM中占用內(nèi)存最多的部分,通常是動(dòng)態(tài)分配的.堆的大小不是一成不變的,當(dāng)堆內(nèi)存的實(shí)際利用率偏離設(shè)定值的時(shí)候,虛擬機(jī)會(huì)在垃圾回收(GC)的時(shí)候調(diào)整堆內(nèi)存的大小,讓實(shí)際占用率向設(shè)定值靠攏.在開(kāi)發(fā)一些大型游戲或耗資源的應(yīng)用中可以考慮手動(dòng)干涉GC處理,使用dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強(qiáng)程序堆內(nèi)存的處理效率,可以在應(yīng)用onCreate時(shí)通過(guò)VMRuntime.getRuntime().setTargetHeapUtilization(0.75),把堆內(nèi)存的利用率設(shè)置為75%,優(yōu)化Dalvik虛擬機(jī)的堆內(nèi)存分配.除了優(yōu)化Dalvik虛擬機(jī)的堆內(nèi)存分配外,還可以使用Dalvik提供的dalvik.system.VMRuntime類重新定義堆內(nèi)存的大小,把最小堆內(nèi)存設(shè)置成16 MB.此方法的缺點(diǎn)是,重新設(shè)置堆內(nèi)存涉及內(nèi)存拷貝,反復(fù)更改堆內(nèi)存的大小勢(shì)必影響程序效率.
2.2.1 以最省內(nèi)存的方式讀取本地資源的圖片
Android應(yīng)用在加載圖片時(shí)的顏色模式有4種,分別是①ALPHA_8,每像素占用1 byte內(nèi)存;②ARGB_4444,每像素占用2 byte內(nèi)存;③ARGB_8888,每像素占用4 byte內(nèi)存;④RGB_565,每像素占用2 byte內(nèi)存.
Android默認(rèn)的顏色模式為ARGB_8888,這個(gè)顏色模式色彩最細(xì)膩、顯示質(zhì)量最高,但占用的內(nèi)存也最大.通過(guò)指定加載圖片時(shí)的顏色模式,可以達(dá)到節(jié)省內(nèi)存的目的,代碼如下:
以上代碼實(shí)現(xiàn)了圖片資源以RGB_565模式讀取.對(duì)于大多數(shù)圖片,RGB_565(或ARGB_4444)模式與ARGB_8888模式的顯示效果差別不大.此方法的缺點(diǎn)是,由于采用了顯示質(zhì)量一般的顏色模式加載圖片,所以對(duì)于漸變效果的圖片可能會(huì)出現(xiàn)顏色條.此外,使用RGB_565(或ARGB_4444)模式時(shí)會(huì)影響圖片的特效處理.
2.2.2 將圖片轉(zhuǎn)化為縮略圖加載
如果圖片的像素過(guò)大,在使用BitmapFactory類方法實(shí)例化Bitmap的過(guò)程中,需要使用的內(nèi)存空間可能超過(guò)Android系統(tǒng)分配的空間,從而導(dǎo)致內(nèi)存溢出.如果遇到這種情況,可以在實(shí)例化Bitmap時(shí)將圖片縮小,減少圖片載入過(guò)程中的內(nèi)存占用,避免異常發(fā)生.
在Android應(yīng)用中,使用BitmapFactory.Options設(shè)置inSampleSize屬性可以對(duì)圖片進(jìn)行二次采樣,屬性值inSampleSize表示縮略圖的寬度和高度為完整圖片的幾分之一,代碼如下:
以上代碼實(shí)現(xiàn)了讀取縮略圖的功能,由于inSampleSize的值為2,則縮略圖的寬度和高度都是原始圖片的1/2,圖片的大小為原始大小的1/4.這種做法的弊病是圖片質(zhì)量會(huì)變差,inSampleSize的值越大,圖片的質(zhì)量就越差.在實(shí)際項(xiàng)目中,正確的做法是先獲取圖片真實(shí)的寬度和高度,然后判斷是否需要使用縮略圖.如果不需要,設(shè)置inSampleSize的值為1;如果需要,則動(dòng)態(tài)計(jì)算并設(shè)置inSampleSize的值,對(duì)圖片進(jìn)行縮小.
2.3.1 捕獲程序異常
Android應(yīng)用中實(shí)例化Bitmap會(huì)占用大量?jī)?nèi)存,為了避免應(yīng)用在分配Bitmap內(nèi)存時(shí)出現(xiàn)內(nèi)存溢出異常導(dǎo)致的應(yīng)用異常結(jié)束,需要特別注意實(shí)例化Bitmap部分的代碼.通常在實(shí)例化Bitmap的代碼中,一定要對(duì)內(nèi)存溢出異常進(jìn)行捕獲,代碼如下:
這段代碼通過(guò)在初始化Bitmap對(duì)象過(guò)程中對(duì)可能發(fā)生的內(nèi)存溢出異常進(jìn)行了捕獲和處理,即使出現(xiàn)內(nèi)存溢出的情況,應(yīng)用也不會(huì)崩潰,而是得到一個(gè)默認(rèn)的Bitmap位圖.需要注意的是,很多開(kāi)發(fā)者會(huì)習(xí)慣性地在代碼中直接捕獲Exception,但是對(duì)于Out Of Memory Error來(lái)說(shuō),這樣做的結(jié)果是捕獲不到異常的,因?yàn)镺ut Of Memory Error是一種Error,而不是Exception.
2.3.2 及時(shí)回收內(nèi)存
由于Bitmap對(duì)象占用的內(nèi)存大,在確認(rèn)不再使用該圖片對(duì)象時(shí),可以通過(guò)調(diào)用Bitmap對(duì)象的recycle()方法及時(shí)回收內(nèi)存,有效降低圖片本地?cái)?shù)據(jù)的峰值,減少內(nèi)存溢出的概率.
recycle()方法的作用是標(biāo)記圖片對(duì)象,方便回收?qǐng)D片對(duì)象的本地?cái)?shù)據(jù),及時(shí)釋放占用內(nèi)存,并非真正降低Bitmap使用內(nèi)存.使用了recycle()方法的圖片對(duì)象處于“廢棄”狀態(tài),再次調(diào)用時(shí)會(huì)造成程序錯(cuò)誤,所以在無(wú)法保證該圖片對(duì)象絕對(duì)不會(huì)被再次調(diào)用的情況下,不建議使用該方法.特別要注意的是,已經(jīng)用setImageBitmap()方法分配給控件的圖片對(duì)象,可能會(huì)被系統(tǒng)類庫(kù)調(diào)用,造成程序錯(cuò)誤.
2.3.3 緩存通用的Bitmap對(duì)象
在Android應(yīng)用中,經(jīng)常需要在一個(gè)Activity里多次用到同一圖片.例如,在一個(gè)Activity中展示一些用戶的頭像列表,如果某用戶沒(méi)有設(shè)置頭像,則會(huì)顯示一個(gè)默認(rèn)頭像.遇到此類應(yīng)用時(shí),如果不進(jìn)行緩存,而采用BitmapFactory類的方法實(shí)例化每個(gè)Bitmap,盡管看到的是同一圖片,得到的卻是不同的Bitmap對(duì)象.正確的做法是對(duì)同一Bitmap對(duì)象(默認(rèn)頭像)進(jìn)行緩存,避免重復(fù)新建,以減少內(nèi)存占用.
有限的內(nèi)存容量和對(duì)內(nèi)存不斷提升的需求始終是矛盾的,Android應(yīng)用不可避免地會(huì)使用圖片資源,在處理大量Bitmap數(shù)據(jù)集時(shí)避免內(nèi)存的溢出有著十分重要的意義.本研究給出了相應(yīng)的解決方法,通過(guò)使用這些方法,在軟件設(shè)計(jì)時(shí)充分考慮需求并合理地利用技術(shù)有效地管理Android應(yīng)用的內(nèi)存,避免應(yīng)用運(yùn)行時(shí)內(nèi)存溢出.
[1]余志龍,陳昱勛,鄭名杰,等.Google Android SDK開(kāi)發(fā)范例大全[M].北京:人民郵電出版社,2010.
[2]姚昱曼,劉衛(wèi)國(guó).Android與J2ME平臺(tái)間即時(shí)通信的研究與實(shí)現(xiàn)[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2008(12):118-120,127.
[3]黃偉敏.基于XMPP協(xié)議的Android即時(shí)通信系統(tǒng)設(shè)計(jì)[J].電子設(shè)計(jì)工程,2011(19):57-59.
[4]董鋮.針對(duì)Android應(yīng)用中Gallery內(nèi)存溢出的解決方案[D].上海:東華大學(xué),2012.