閱讀658 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Android在處理圖片減少出現OOM的方式

在做Android圖片程序的時候,由於圖片比較多,很有很的機會出現OOM的機會,根據網上的資料做了些總結,期待能夠減少OOM出現的機會。


1.使用底層的方法來替代使用java層的方法
     盡量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設置一張大圖。 
 因為這些函數在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多內存。 因此,改用先通過BitmapFactory.decodeStream方法,創建出一個bitmap,再將其設為ImageView的 source, 
decodeStream最大的秘密在於其直接調用JNI>>nativeDecodeAsset()來完成decode, 無需再使用java層的createBitmap,從而節省了java層的空間。 
 在讀取時加上圖片的Config參數,可以跟有效減少加載的內存,
 通過使用這樣的辦法,能更有效阻止拋out of Memory異常 另外,decodeStream直接拿的圖片來讀取字節碼了, 不會根據機器的各種分辨率來自動適應, 使用了decodeStream之後,需要在hdpi和mdpi,ldpi中配置相應的圖片資源, 否則在不同分辨率機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。 另外,以下方式也大有幫助:

片段一: 

InputStream is = this.getResources().openRawResource(R.drawable.pic1); 
     BitmapFactory.Options options=new BitmapFactory.Options(); 
     options.inJustDecodeBounds = false; 
     options.inSampleSize = 10;   //width,hight設為原來的十分一 
     Bitmap btp =BitmapFactory.decodeStream(is,null,options); 

片段二:

if(!bmp.isRecycle() ){ 
         bmp.recycle()   //回收圖片所占的內存 
         system.gc()  //提醒係統及時回收 
} 
片段三:
public static Bitmap readBitMap(Context context, int resId){  
     BitmapFactory.Options opt = new BitmapFactory.Options();  
         opt.inPreferredConfig = Bitmap.Config.RGB_565;   
       opt.inPurgeable = true;  
       opt.inInputShareable = true;   
      InputStream is = context.getResources().openRawResource(resId);  
        return BitmapFactory.decodeStream(is,null,opt);  
  } 

2. 優化虛擬機的堆內存使用
對於Android平台來說,其托管層使用的Dalvik Java VM從目前的表現來看還有很多地方可以優化處理,比如我們在開發一些大型遊戲或耗資源的應用中可能考慮手動幹涉GC處理,使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強程序堆內存的處理效率。當然具體原理我們可以參考開源工程,這裏我們僅說下使用方法:  
  private final static float TARGET_HEAP_UTILIZATION = 0.75f; 
在程序onCreate時就可以調用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可。 
Android堆內存也可自己定義大小 
    對於一些Android項目,影響性能瓶頸的主要是Android自己內存管理機製問題,目前手機廠商對RAM都比較吝嗇,對於軟件的流暢性來說RAM對性能的影響十分敏感,除了 優化Dalvik虛擬機的堆內存分配外,我們還可以強製定義自己軟件的對內存大小,我們使用Dalvik提供的 dalvik.system.VMRuntime類來設置最小堆內存為例: 
private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; 
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //設置最小heap內存為6MB大小。
當然對於內存吃緊來說還可以通過手動幹涉GC去處理 


3.其他一些使用技巧
       1.不同大小的圖片需要做成同一個高度的的縮略圖(如100px),而且要保證圖片不失真,那怎麼辦?我們總不能將原始圖片加載到內存中再進行縮放處理吧,要知道在移動開發中,內存是相當寶貴的,而且一張100K的圖片,加載完所占用的內存何止100K?
   經過研究,發現,Options中有個屬性inJustDecodeBounds,研究了一下,終於明白是什麼意思了,SDK中的E文是這麼說的
      If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
  意思就是說如果該值設為true那麼將不返回實際的bitmap不給其分配內存空間而裏麵隻包括一些解碼邊界信息即圖片大小信息,那麼相應的方法也就出來了,通過設置inJustDecodeBounds為true,獲取到outHeight(圖片原始高度)和 outWidth(圖片的原始寬度),然後計算一個inSampleSize(縮放值),然後就可以取圖片了,這裏要注意的是,inSampleSize 可能小於0,必須做判斷。


       2.用BitmapFactory解碼一張圖片時,有時會遇到該錯誤。這往往是由於圖片過大造成的。要想正常使用,則需要分配更少的內存空間來存儲。
BitmapFactory.Options.inSampleSize.設置恰當的inSampleSize可以使BitmapFactory分配更少的空間以消除該錯誤。inSampleSize的具體含義請參考SDK文檔。例如:

BitmapFactory.Options opts = new BitmapFactory.Options(); 
opts.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts); 

設置恰當的inSampleSize是解決該問題的關鍵之一。

最後更新:2017-04-02 17:09:25

  上一篇:go 《iPhone與iPad開發實戰—iOS經典應用剖析》連載一
  下一篇:go Android: 縮放圖片文件引起的OOM異常