當(dāng)使用超大的Bitmap圖片或加載大量圖片時(shí)很容易出現(xiàn)OOM(Out Of Memory)的現(xiàn)象
一般有兩種解決的方法:
1、使用超大的Bitmap圖片時(shí)采取壓縮圖片的方法
2、加載大量圖片時(shí)使用LruCache緩存
使用超大圖片
BitmapFactory.Options有個(gè)inJustDecodeBounds屬性,將inJustDecodeBounds設(shè)置為true時(shí),這樣就可以在不給圖片分配內(nèi)存時(shí)讀取圖片的基本信息,讀取并設(shè)置之后,再把該值改為false,然后再進(jìn)行圖片解析,這就是壓縮圖片的原理。
計(jì)算inSampleSize
圖片壓縮時(shí)高和寬都按inSampleSize比例進(jìn)行壓縮,比如inSampleSize為4時(shí)就表示高和寬都為原來(lái)的1/4,圖片整體壓縮到原來(lái)的1/16。
// 計(jì)算inSampleSize
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源圖片的高度和寬度
final int height = options.outHeight;
final int width = options.outWidth;
// inSampleSize不能小于1
int inSampleSize = 1;
// 源圖片的高或?qū)挻笥谝蟮? if (height > reqHeight || width > reqWidth) {
// 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高
// 一定都會(huì)大于等于目標(biāo)的寬和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
從資源中解析圖片
// 解析圖片
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 第一次解析將inJustDecodeBounds設(shè)置為true,來(lái)獲取圖片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 調(diào)用上面定義的方法計(jì)算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 使用獲取到的inSampleSize值再次解析圖片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
這里從資源中解析圖片,所以需要傳入Resources和圖片的資源id;另外我們希望將圖片壓縮成多少尺寸就需要傳入相應(yīng)的寬和高。
向ImageView中加載圖片
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(
getResources(), R.drawable.image, 100, 100));
加載大量圖片
LruCache緩存主要算法原理是把最近使用的對(duì)象用強(qiáng)引用存儲(chǔ)在LinkedHashMap中,并且把最近最少使用的對(duì)象在緩存值即將達(dá)到預(yù)設(shè)定值之前從內(nèi)存中移除。
實(shí)例化緩存對(duì)象
// 每個(gè)應(yīng)用程序最高可用內(nèi)存,單位KB。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024));
// 設(shè)置緩存空間的大小為可用最大內(nèi)存的1/8
int cacheSize = maxMemory / 8;
// 傳入緩存大小實(shí)例化緩存對(duì)象
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 每張圖片的大小單位是KB
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);
}
加載圖片的異步任務(wù)
// 加載圖片的異步任務(wù)
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private ImageView imageView;
public BitmapWorkerTask(ImageView imageView) {
this.imageView = imageView;
}
// 在后臺(tái)加載圖片。
@Override
protected Bitmap doInBackground(Integer... params) {
// 從資源中解析圖片
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100);
// 將圖片加入緩存
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
// 返回解析出來(lái)的圖片
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
// 設(shè)置ImageView的圖片
imageView.setImageBitmap(bitmap);
}
}
加載圖片
// 加載圖片
public void loadBitmap(int resId, ImageView imageView) {
// 將圖片資源id作為Key
final String imageKey = String.valueOf(resId);
// 從緩存中加載圖片
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
// 如果緩存中有的話
if (bitmap != null) {
// 設(shè)置ImageView的圖片
imageView.setImageBitmap(bitmap);
} else {// 如果緩存中沒(méi)有
imageView.setImageResource(R.drawable.image);
// 傳入ImageView實(shí)例化加載圖片的異步任務(wù)
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
// 開啟異步任務(wù)從資源中加載
task.execute(resId);
}
}