溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》
  • 首頁 > 
  • 教程 > 
  • 開發(fā)技術 > 
  • 【移動開發(fā)】Android中圖片過大造成內存溢出,OOM(OutOfMemory)異常解決方法

【移動開發(fā)】Android中圖片過大造成內存溢出,OOM(OutOfMemory)異常解決方法

發(fā)布時間:2020-08-07 17:53:02 來源:網絡 閱讀:5523 作者:zhf651555765 欄目:開發(fā)技術

   當我們在做項目過程中,一遇到顯示圖片時,就要考慮圖片的大小,所占內存的大小,原因就是Android分配給Bitmap的大小只有8M,試想想我們用手機拍照,普通的一張照片不也得1M以上,所以android處理圖片時不得不考慮圖片過大造成的內存異常。

   那時候只是簡單地緩存圖片到本地 然后將圖片進行壓縮,但是感覺這個問題沒有很好的解決辦法,只是減小了發(fā)生的幾率


    這里,我將前輩們解決的方法重新整理一番,方便自己以后使用。

   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();
    //下面這個設置是將圖片邊界不可調節(jié)變?yōu)榭烧{節(jié)
    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;
        }

 

    但大家都知道,這些函數(shù)在完成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()  //提醒系統(tǒng)及時回收
}

 

    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.在適當?shù)臅r候垃圾回收

if(bitmapObject.isRecycled()==false) //如果沒有回收  
         bitmapObject.recycle();


   D.優(yōu)化Dalvik虛擬機的堆內存分配

    對于Android平臺來說,其托管層使用的Dalvik JavaVM從目前的表現(xiàn)來看還有很多地方可以優(yōu)化處理,eg我們在開發(fā)一些大型游戲或耗資源的應用中可能考慮手動干涉GC處理,使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強程序堆內存的處理效率。

private final static floatTARGET_HEAP_UTILIZATION = 0.75f; 
//在程序onCreate時就可以調用
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
即可

  至于上面為何是0.75,是因為堆(HEAP)是VM中占用內存最多的部分,通常是動態(tài)分配的。堆的大小不是一成不變的,通常有一個分配機制來控制它的大小。比如初始的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


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI