您好,登錄后才能下訂單哦!
當我們在做項目過程中,一遇到顯示圖片時,就要考慮圖片的大小,所占內存的大小,原因就是Android分配給Bitmap的大小只有8M,試想想我們用手機拍照,普通的一張照片不也得1M以上,所以android處理圖片時不得不考慮圖片過大造成的內存異常。
那時候只是簡單地緩存圖片到本地 然后將圖片進行壓縮,但是感覺這個問題沒有很好的解決辦法,只是減小了發生的幾率
這里,我將前輩們解決的方法重新整理一番,方便自己以后使用。
1.在內存引用上做些處理,常用的有軟引用、強化引用、弱引用(可以參考這篇博客:http://smallwoniu.blog.51cto.com/blog/3911954/1248751)
import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.reflect.Field; public class Test { public static boolean isRun = true; public static void main(String[] args) throws Exception { String abc = new String("abc"); System.out.println(abc.getClass() + "@" + abc.hashCode()); final ReferenceQueue referenceQueue = new ReferenceQueue<String>(); new Thread() { public void run() { while (isRun) { Object o = referenceQueue.poll(); if (o != null) { try { Field rereferent = Reference.class .getDeclaredField("referent"); rereferent.setAccessible(true); Object result = rereferent.get(o); System.out.println("gc will collect:" + result.getClass() + "@" + result.hashCode()); } catch (Exception e) { e.printStackTrace(); } } } } }.start(); PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc, referenceQueue); abc = null; Thread.currentThread().sleep(3000); System.gc(); Thread.currentThread().sleep(3000); isRun = false; } }
結果:
class java.lang.String@96354 gc will collect:class java.lang.String@96354
2.在內存中加載圖片時直接在內存中做處理
A.邊界壓縮
@SuppressWarnings("unused") private Bitmap copressImage(String imgPath){ File picture = new File(imgPath); Options bitmapFactoryOptions = new BitmapFactory.Options(); //下面這個設置是將圖片邊界不可調節變為可調節 bitmapFactoryOptions.inJustDecodeBounds = true; bitmapFactoryOptions.inSampleSize = 2; int outWidth = bitmapFactoryOptions.outWidth; int outHeight = bitmapFactoryOptions.outHeight; bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(), bitmapFactoryOptions); float p_w_picpathw = 150; float p_w_picpathh = 150; int yRatio = (int) Math.ceil(bitmapFactoryOptions.outHeight / p_w_picpathh); int xRatio = (int) Math .ceil(bitmapFactoryOptions.outWidth / p_w_picpathw); if (yRatio > 1 || xRatio > 1) { if (yRatio > xRatio) { bitmapFactoryOptions.inSampleSize = yRatio; } else { bitmapFactoryOptions.inSampleSize = xRatio; } } bitmapFactoryOptions.inJustDecodeBounds = false;//false --- allowing the caller to query the bitmap without having to allocate the memory for its pixels. bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(), bitmapFactoryOptions); if(bmap != null){ //ivwCouponImage.setImageBitmap(bmap); return bmap; } return null; }
B.邊界壓縮的情況下間接的使用了軟引用來避免OOM
/* 自定義Adapter中部分代碼*/ public View getView(int position, View convertView, ViewGroup parent) { File file = new File(it.get(position)); SoftReference<Bitmap> srf = p_w_picpathCache.get(file.getName()); Bitmap bit = srf.get(); ImageView i = new ImageView(mContext); i.setImageBitmap(bit); i.setScaleType(ImageView.ScaleType.FIT_XY); i.setLayoutParams( new Gallery.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT)); return i; }
但大家都知道,這些函數在完成decode后,最終都是通過java層的createBitmap來完成的,需要消耗更多內存,如果圖片多且大,這種方式還是會引用OOM異常的,因此需要進一步處理:
A.第一種方式
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() //提醒系統及時回收 }
B.第二中方式
/** * 以最省內存的方式讀取本地資源的圖片 * */ 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); }
C.在適當的時候垃圾回收
if(bitmapObject.isRecycled()==false) //如果沒有回收 bitmapObject.recycle();
D.優化Dalvik虛擬機的堆內存分配
對于Android平臺來說,其托管層使用的Dalvik JavaVM從目前的表現來看還有很多地方可以優化處理,eg我們在開發一些大型游戲或耗資源的應用中可能考慮手動干涉GC處理,使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強程序堆內存的處理效率。
private final static floatTARGET_HEAP_UTILIZATION = 0.75f; //在程序onCreate時就可以調用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可
至于上面為何是0.75,是因為堆(HEAP)是VM中占用內存最多的部分,通常是動態分配的。堆的大小不是一成不變的,通常有一個分配機制來控制它的大小。比如初始的HEAP是4M大,當4M的空間被占用超過75%的時候,重新分配堆為8M大;當8M被占用超過75%,分配堆為16M大。倒過來,當16M的堆利用不足30%的時候,縮減它的大小為8M大。重新設置堆的大小,尤其是壓縮,一般會涉及到內存的拷貝,所以變更堆的大小對效率有不良影響。
E.自定義我們的應用需要多大的內存
private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; //設置最小heap內存為6MB大小 VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
以上這些就是本人總結的一些解決OOM異常的方法,希望能幫助到大家!
參考博客:http://blog.sina.com.cn/s/blog_7501670601014dcj.html
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。