解決加載圖片OOM 方法彙總

加載圖片出現OOM的情況一般有兩個,
    一、圖片過大
    二、圖片過多

首先來看下第一種情況:
    簡單的一句話就能造成
         Bitmap bitmap = BitmapFactory.decodeFile(path);//圖片一大就直接OOM
    
    解決方法:
        預加載,獲取屏幕大小,圖片大小,計算縮放比例,然後縮放原圖後加載
    
    一、獲取屏幕大小:
        1,通過DisplayMetrics 
            DisplayMetrics metric=getResources().getDisplayMetrics(); 或  DisplayMetrics metric = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metric);
            int width = metric.widthPixels;  // 屏幕寬度(像素)
            int height = metric.heightPixels;  // 屏幕高度(像素)
            float density = metric.density;  // 屏幕密度(0.75 / 1.0 / 1.5)
            int densityDpi = metric.densityDpi;  // 屏幕密度DPI(120 / 160 / 240)
        2 , 通過WindowManager
            Display display=getWindowManager().getDefaultDisplay();
            int screenWidth  =display.getWidth();       // 屏幕寬(像素,如:480px)  
            int screenHeight =display.getHeight();      // 屏幕高(像素,如:800p)  

        3, 上面兩種在小屏時會有誤差
            // 獲取屏幕密度(方法3)  
            dm = new DisplayMetrics();  
            getWindowManager().getDefaultDisplay().getMetrics(dm);  
            screenWidth  = (int)(dm.widthPixels * density + 0.5f);      // 屏幕寬(px,如:480px)  
            screenHeight = (int)(dm.heightPixels * density + 0.5f);     // 屏幕高(px,如:800px)  
    二、 預加載獲取圖片大小
            BitmapFactory.Options options = new BitmapFactory.Options();   //new 一個bitmap的屬性
            options.inJustDecodeBounds = true;   //描邊 爲true則解析方法不止爲bitmap分配內存,解析後的bitmap爲null但是options的屬性已經賦值
       BitmapFactory.decodeResource(getResources(), R.id.image, options);  
       //BitmapFactory.decodeFile(path,options);  這下面5種是根據輸入不同來初始化的方法
       //BitmapFactory.decodeByteArray(byteArray, offset, length,options); 
       //BitmapFactory.decodeFileDescriptor(filedescriptor, outPadding, options);
       //BitmapFactory.decodeStream(inputstream, outPadding, options)
       int imageHeight = options.outHeight;   
       int imageWidth = options.outWidth;   
       String imageType = options.outMimeType; 

    三、計算縮放比例   這個網上有幾種好的算法, 基本上就是把屏幕高寬與圖片大小相除 
        public static int calculateInSampleSize(BitmapFactory.Options options,   
             int reqWidth, int reqHeight) {   
           // 源圖片的高度和寬度   
           final int height = options.outHeight;   
           final int width = options.outWidth;   
           int inSampleSize = 1;   
           if (height > reqHeight || width > reqWidth) {   
               // 計算出實際寬高和目標寬高的比率   
               final int heightRatio = Math.round((float) height / (float) reqHeight);   
               final int widthRatio = Math.round((float) width / (float) reqWidth);   
               // 選擇寬和高中最小的比率作爲inSampleSize的值,這樣可以保證最終圖片的寬和高   
               // 一定都會大於等於目標的寬和高。   
               inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;   
           }   
           return inSampleSize;   
        
    四、 縮放原圖 
              // 重新new一個option 再次預加載
        final BitmapFactory.Options options = new BitmapFactory.Options();   
        options.inJustDecodeBounds = true;   
        BitmapFactory.decodeResource(path, options);   
        // 設置 縮放比例inSampleSize值到options內   
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);   
        // 取消預加載
        options.inJustDecodeBounds = false;  
        // 最後  加載圖片!!!! 
        Bitmap b=BitmapFactory.decodeResource(path, options);  

第二種情況: 圖片過多
       最通用的方法就是建立圖片緩存
       這裏自己沒去測試 ,召喚傳送門吧

private LruCache<string, bitmap> mMemoryCache;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
    // 獲取到可用內存的最大值,使用內存超出這個值會引起OutOfMemory異常。
    // LruCache通過構造函數傳入緩存值,以KB爲單位。
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    // 使用最大可用內存值的1/8作爲緩存的大小。
    int cacheSize = maxMemory / 8;
    mMemoryCache = new LruCache<string, bitmap="">(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // 重寫此方法來衡量每張圖片的大小,默認返回圖片數量。
            return bitmap.getByteCount() / 1024;
        }
    };
}
 
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}
 
public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}
}


下次繼續深入—— 看到了bavariama大神的博客  

Android 圖片 OOM問題總結

XML佈局中加載的圖片是不用GC的,

他們是和View/Activity 綁定在一起,同生同死。

UI上加載的可以使用SoftReference讓虛擬機進行快速回收。
如果迫切需要手動回收,嚴格控制內存開銷,也許就該針對場景寫Cache的管理了。

對於一般的場景,使用棧的結構就足夠了,對於期望高效流暢又有着複雜item的ListView的話
可能需要預加載和手動回收。

--------------------------------------------------------------------------------------------------------

1.對圖片進行預處理
主要是根據實際大小對對質量進行壓縮

2.參考下android developer中的demo,裏面有兩點值得借鑑:一個是內存緩存,一個是disk緩存。可以很好的幫助你處理oom.

 

樓上所說的是LruCache,你好好研究下這個官方DEMO,研究明白了,你以後所有的項目都可以使用這個DEMO中的方式,絕對不會出現OOM。另外,在Gallery3D的源代碼中,好像有另外一種cache方式。

 

--------------------------------------------------------------------------------------------------------

https://github.com/dodola/android_waterfall/tree/master/src/net   可以加載上萬張圖片的瀑布流



還有一個有關OOM 出險原因的  應該不是簡單地圖片過大
分類: android開發基礎2014-06-06 12:40 660人閱讀 評論(1) 收藏 舉報

ImageView加載圖片時,有時會出現OOM
 

imageView.setImageResource(imageId);

解決方法
 

/**
* 以最省內存的方式讀取本地資源的圖片

* @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 bitmap=readBitMap(LoginActivity.this,imageId);
imageView.setImageBitmap(bitmap);

那是爲什麼,會導致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中配置相應的圖片資源,否則在不同分辨率機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。



Android異步加載圖片,解決圖片過大OOM問題

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.widget.ImageView;

/**
 * 
 * 功能說明:異步加載圖片
 * 
 * @author Jack.wang
 * 
 */
public class AsyncImageLoaderCore
{

  public Context context; // 做本地緩存時會用到
    public HashMap<String, SoftReference<Bitmap>> imageCache;// 軟引用集合

    public AsyncImageLoaderCore(Context context)
    {
        this.context = context;
        this.imageCache = new HashMap<String, SoftReference<Bitmap>>();
    }

    public Bitmap loadBitmap(final String imageUrl, final ImageView imageView, final ImageCallback imageCallback)
    {
        if (imageCache.containsKey(imageUrl))
        {
            SoftReference<Bitmap> softReference = imageCache.get(imageUrl);
            if (softReference.get() != null)
                return softReference.get();
        }

        final Handler handler = new Handler(new Callback()
        {

            @Override
            public boolean handleMessage(Message msg)
            {
                imageCallback.imageLoaded((Bitmap) msg.obj, imageView, imageUrl);
                return false;
            }
        });

        new Thread()
        {
            @Override
            public void run()
            {
                Bitmap bitmap = null;
                try
                {
                    bitmap = getHttpBitmap(imageUrl);
                } 
                catch (Exception e)
                {
                    e.printStackTrace();
                	return;
                }
                
                if (null != bitmap)
                {
                    imageCache.put(imageUrl, new SoftReference<Bitmap>(bitmap));
                    handler.sendMessage(handler.obtainMessage(0, bitmap));
                }
            }
        }.start();

        return null;
    }
    
    private final int MAX_PIC_LENGTH = 200000;// 最大字節長度限制[可調,最好不要超過200000]
    private final int SAMPLE_SIZE = 14;// 裁剪圖片比列(1/14)[可調]
    
    /**
     * 獲取網絡圖片
     */
    private Bitmap getHttpBitmap(String imgUrl) throws Exception
    {
        URL htmlUrl = new URL(imgUrl);
        URLConnection connection = htmlUrl.openConnection();
        HttpURLConnection conn = (HttpURLConnection) connection;
        
        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK)
        {
        	InputStream inputStream = conn.getInputStream();
        	
        	byte[] bytes = changeToBytes(inputStream);
        	
        	if(bytes.length < MAX_PIC_LENGTH)
    		{
        		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    		}
        	else if(bytes.length < MAX_PIC_LENGTH * SAMPLE_SIZE)
        	{
        		BitmapFactory.Options options = new BitmapFactory.Options();
        		options.inJustDecodeBounds = false;
        		options.inSampleSize = SAMPLE_SIZE;
        		
        		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        	}
        }
        
        return null;
    }

    /**
     * 將流轉換成字節數組
     */
  public byte[] changeToBytes(InputStream inputStream) throws Exception
  {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];// 每次讀取的字節長度
    int len = 0;
    while((len = inputStream.read(buffer)) != -1)
    {
      outputStream.write(buffer, 0, len);
    }
    inputStream.close();
    return outputStream.toByteArray();
  }

  /**
   * 異步加載資源回調接口
   */
  public interface ImageCallback
    {
        public void imageLoaded(Bitmap bitmap, ImageView imageView, String imageUrl);
    }

}









Android 獲取屏幕尺寸與密度

http://www.cnblogs.com/renyuan/archive/2012/07/25/2607936.html




Android加載大圖片OOM異常解決




Android 加載大圖片時報OOM的解決方案(源碼)

http://www.linuxidc.com/Linux/2011-12/50202.htm

Android高效加載大圖、多圖解決方案,有效避免程序OOM

http://blog.csdn.net/luohai859/article/details/38660257

Android 圖片加載圖片_OOM異常解決







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章