Android加載大圖的優化策略

當我們使用大的Bitmap圖片時很容易出現OOM的現象,今天我們就來看下該怎麼解決這個問題。
一般有兩種方法:
1、壓縮圖片;
2、LruCache緩存;
當然這兩種方式同時使用效果更好^^
一、壓縮圖片
先介紹下圖片質量(Bitmap.Config),一共有4種:
ALPHA_8 只有透明度,沒有顏色,那麼一個像素點佔8位。
RGB_565 即R=5,G=6,B=5,沒有透明度,那麼一個像素點佔5+6+5=16位。
ARGB_4444 由4個4位組成,即A=4,R=4,G=4,B=4,那麼一個像素點佔16位。
ARGB_8888 由4個8位組成,即A=8,R=8,G=8,B=8,那麼一個像素點佔32位。
默認是ARGB_8888。

據此我們可以寫出如下函數:

//根據圖片質量確定每個像素點所佔字節數
public static int getBytesPerPixel(Bitmap.Config config) {  
    if (config == Bitmap.Config.ARGB_8888) {  
        return 4;  
    } else if (config == Bitmap.Config.RGB_565) {  
        return 2;  
    } else if (config == Bitmap.Config.ARGB_4444) {  
        return 2;  
    } else if (config == Bitmap.Config.ALPHA_8) {  
        return 1;  
    } 
    return 1;  
}
//根據Bitmap的寬高來計算其大小,知道圖片大小後,我們可以決定是否對其壓縮
public static long getBitmapSizeInMemory(int imageW, int imageH) {
    return imageH * imageW * getBytesPerPixel(Bitmap.Config.ARGB_8888);  
}
BitmapFactory.Options有個inJustDecodeBounds屬性,將inJustDecodeBounds設置爲true時,就不解碼圖片到內存,只讀取圖片的基本信息,讀取並設置之後,再把該值改爲false,然後再進行解碼獲取圖片,這就是壓縮圖片的原理。
代碼如下:
//獲取的inSampleSize必須是2的倍數 ps:本函數(getScaleInSampleSize)只是一種參考,具體實現還需要根據實際情況有所變動
public static int getScaleInSampleSize(int resW, int resH, int desW, int desH) {  
	int scaleW = resW / desW;  
	int scaleH = resH / desH;  
	int largeScale = scaleH > scaleW ? scaleH : scaleW;  
	int sampleSize = 1;  
	while (sampleSize < largeScale) {  
		sampleSize *= 2;  
	}
	return sampleSize;  
}
//獲取壓縮圖片
public static Bitmap decodeBitmapFromResource(Resources res, int resId,  
        int reqWidth, int reqHeight) {  
    // 第一次解析將inJustDecodeBounds設置爲true,來獲取圖片信息
    final BitmapFactory.Options options = new BitmapFactory.Options();  
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);  
    int height = options.outHeight;  
    int width = options.outWidth;  
    String mimeType = options.outMimeType; 
    // 調用上面定義的方法計算inSampleSize值  
    options.inSampleSize = getScaleInSampleSize(width, height, reqWidth, reqHeight);  
    //options.inPreferredConfig= Bitmap.Config.RGB_565; //如有必要,還可以修改圖片質量,進一步減小圖片大小
    // 使用獲取到的inSampleSize值再次解析圖片  
    options.inJustDecodeBounds = false;  
    return BitmapFactory.decodeResource(res, resId, options);  
}
二、使用LruCache緩存

LruCache緩存主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,並且把最近最少使用的對象在緩存值即將達到預設定值之前從內存中移除。

用法如下:

private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
	// 獲取到可用內存的最大值,使用內存超出這個值會引起OutOfMemory異常。
	// LruCache通過構造函數傳入緩存值,以KB爲單位。
	int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
	// 使用最大可用內存值的1/6作爲緩存的大小。
	int cacheSize = maxMemory / 6;
	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);
}
public void loadBitmap(int resId, ImageView imageView) {
	final String imageKey = String.valueOf(resId);
	final Bitmap bitmap = getBitmapFromMemCache(imageKey);
	if (bitmap != null) {
		imageView.setImageBitmap(bitmap);
	} else {
		//如果緩存裏面沒有就使用默認的圖片
		imageView.setImageResource(R.drawable.image_default);
		LoadWorkerTask task = new LoadWorkerTask();
		task.execute(resId);
	}
}
class LoadWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
	// 在後臺加載圖片。
	@Override
	protected Bitmap doInBackground(Integer... params) {
		final Bitmap bitmap = decodeBitmapFromResource(
				getResources(), params[0], 100, 100);
		addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
		return bitmap;
	}
}

代碼已經很清楚了,就不解釋了。

只要我們掌握了這兩種方法,那麼當我們需要使用大尺寸或是使用多張Bitmap時就不需要再擔心OOM問題了。

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