今天做項目,發現需要顯示一張超大圖檔,處理過後,還有561kb
加載的時候,就crash --- oom
shortmsg:java.lang.outofmemoryerror
longmsg:java.lang.outofmemoryerror: bitmap size exceeds vm budget
stacktrace:java.lang.outofmemoryerror: bitmap size exceeds vm budget
at android.graphics.bitmap.nativecreate(native method)
at android.graphics.bitmap.createbitmap(bitmap.java:477)
at android.graphics.bitmap.createbitmap(bitmap.java:444)
at android.graphics.bitmap.createscaledbitmap(bitmap.java:349)
at android.graphics.bitmapfactory.finishdecode(bitmapfactory.java:512)
at android.graphics.bitmapfactory.decodestream(bitmapfactory.java:487)
at android.graphics.bitmapfactory.decoderesourcestream(bitmapfactory.java:336)
代碼如下:
detailview=(imageview)findviewbyid(r.id.detailview);
detailview.setbackgroundresource(r.drawable.more_info);//this line will lead to oom
換成這種:
detailview.setimageresource(r.drawable.more_info); //也同樣會oom
後來找到了solution:
/**
* 以最省記憶體的方式讀取本地資源的圖檔
* @param context
*@param resid
* @return
*/
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);
}
取得bitmap之後,再 detailview.setimagebitmap(pdfimage); 就ok了!
那是為什麼,會導緻oom呢:
原來當使用像 imageview.setbackgroundresource,imageview.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中配置相應的圖檔資源,否則在不同分辨率機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。