天天看點

bug修複---印象最深的OOM

因為android系統的app的每個程序或者每個虛拟機有個最大記憶體限制,如果申請的記憶體資源超過這個限制,系統就會抛出OOM錯誤。跟整個裝置的剩餘記憶體沒太大關系。比如比較早的android系統的一個虛拟機最多16M記憶體,當一個app啟動後,虛拟機不停的申請記憶體資源來裝載圖檔,當超過記憶體上限時就出現OOM。

常見避免OOM的幾個注意點:

适當調整圖像大小 。因為手機螢幕尺寸有限,配置設定給圖像的顯示區域有限,尤其對于超大圖檔,加載自網絡或者sd卡,圖檔檔案提及達到幾M或者十幾個M的。加載到記憶體前,先算出該bitmap的大小,然後通過适當調節采樣率使得加載的圖檔剛好,或稍大捷克在手機螢幕上顯示就滿意了:

BimtapFactory.Option opts = new BitampFactory.Option();

opts.inJustDecodeBounds =  true ;
    opts.inSampleSize=computeSample(opts, minSideLength, maxNumOfPixels);  // Android 提供了一種動态計算的方法 computeSampleSize
    opts.inJustDecodeBounds =  false ;
     try {
            return  BitmapFactory.decodeFile(imageFile, opts);
    }  catch (OutOfMemoryError err){           

}

圖像緩存:在listview或Gallery等控件中一次性加載大量圖檔時,隻加載螢幕顯示的資源,尚未顯示的不加載,移出螢幕的資源及時釋放,采用強引用+軟引用2級緩存,提高加載性能。緩存圖像到記憶體,采用軟引用緩存到記憶體,而不是在每次使用的時候都從新加載到記憶體。

采用低記憶體占用量的編碼方式:比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省記憶體。

及時回收圖像:如果引用了大量的Bitmap對象,而應用又不需要同時顯示所有圖檔。可以将暫時不用到的Bitmap對象及時回收掉。對于一些明确直到圖檔使用情況的場景可以主動recycle回收。App的啟動splash畫面上的圖檔資源,使用完就recycle。對于幀動畫,可以加載一張,畫一張,釋放一張。

不要在循環中建立過多的本地變量:慎用static,用static來修飾成員變量時,該變量就屬于該類,而不是該類執行個體,它的生命周期是很長的。如果用它來引用一些記憶體占用太多的執行個體,這時候就要謹慎對待了。

App使用圖檔時避免OOM的幾種方式:

對于app裡使用的大量圖檔,采用方式:使用時加載,不顯示時直接置null或recycle。

這樣處理是個好習慣,記本可以杜絕OOM,但是缺憾是代碼多了,可能會忘記某些資源recycle。

而有些情況下會出現特定圖檔反複加載,釋放,再加載等,低效率的事情。

建個SoftReference的hashmap

使用圖檔時先查詢這個hashmap是否有softreference,softreference裡的圖檔是否為空,

如果為空就加載圖檔到softreference并加入hashmap。無需再代碼裡顯式的處理圖檔的回收與釋放,gc會自動處理資源的釋放。這種方式處理起來簡單實用,能一定程度上避免前一種方法反複加載釋放的低效率。但還不夠優化。

Android示範程式ImageDownloader.java, 使用了一個二級緩存機制。就是有一個資料結構直接持有解碼成功的Bitmap對象引用,同時使用一個二級緩存資料結構保持淘汰的Bitmap的softreference對象,由于softreference對象的特殊性,系統會再需要記憶體的時候首先将softreference持有的對象釋放掉,也就是說當vm發現可用的記憶體較少需要出發gc的時候,二級緩存中的bitmap對象将被回收,而持有一級緩存的bitmap對象用于顯示。其實這個解決方案最為關鍵的一點是使用了一個比較合适的資料結構,那就是LinkedHashMap類型來進行一級緩存Bitmap的容器。由于LinkeHashMap的特殊性,我們可以控制其記憶體存儲對象的個數并且将不在使用的對象從容器中移除,放到softreference二級緩存裡,我們可以在一級緩存中一緻儲存最近被通路到的bitmap對象,而已經被通路過的圖檔在LinkedHashMap的容量超過我們預設值時将會把容器中存在的時間最長的對象移除,這個時候我麼可以将被移除的LinkedHashMap中的放到二級緩存容器,而二級緩存中的對象管理就交給系統來做了,當系統需要gc時就會首先回收二級緩存容器的Bitmap對象了。

在擷取圖檔對象時候先從一級緩存容器中查找,如果有對應對象并可用直接傳回,如果沒有的話從二級緩存中查找對應的SoftReference, 判斷SoftReference對象持有的Bitmap是否可用,可用直接傳回,否則傳回空。如果二級緩存都找不到圖檔,那就直接加載圖檔資源

繼續閱讀