现在的位置: 首页 > 综合 > 正文

【安卓笔记】OOM解决方案

2014年08月29日 ⁄ 综合 ⁄ 共 6617字 ⁄ 字号 评论关闭

主流方案无非是以下三种:

1:对图片进行缩放;

2:内存缓存;

3:文件缓存。

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

方法1:压缩图片

package com.example.utils;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

/**
 *    防止OOM的第一种解决方案------>压缩bitmap
 */
public class BitmapUtils
{
    private static final String TAG = "BitmapUtils";
    /**
     * 计算采样比
     */
    private static int calculateInSampleSize(BitmapFactory.Options opts,int reqHeight,int reqWidth)
    {
        if(opts == null)
            return -1;
        int width = opts.outWidth;
        int height = opts.outHeight;
        
        int sampleSize = 1;
        
        if(width > reqWidth || height > reqHeight)
        {
            int heightRatio = (int) (height/(float)reqHeight);
            int widthRatio = (int) (width/(float)reqWidth);
            sampleSize = (heightRatio > widthRatio) ? widthRatio : heightRatio;
        }
        return sampleSize;
    }
    
    /**
     * 根据需要的宽高压缩一张图片
     * 
     * @param res 资源
     * @param resId 资源id
     * @param reqWidth 需求宽度
     * @param reqHeight 需求高度
     * @return
     */
    public static Bitmap decodeSampledBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight)
    {
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, opts);
        int sampleSize = calculateInSampleSize(opts, reqHeight, reqWidth);
        Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]");
        opts.inJustDecodeBounds = false;
        opts.inSampleSize = sampleSize;
        Log.i(TAG,"insamplesize="+sampleSize);
        Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts);
        Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]");
        return bitmap;
    }

    /**
     * 根据需要的宽高压缩一张图片
     * 
     * @param data 包含图片信息的byte数组
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public static Bitmap decodeSampledBitmapFromByteArray(byte[] data,int reqWidth,int reqHeight)
    {
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, opts);
        Log.i(TAG,"before[width:"+opts.outWidth+",height:"+opts.outHeight+"]");
        opts.inSampleSize = calculateInSampleSize(opts, reqHeight, reqWidth);
        Log.i(TAG,"insamplesize="+opts.inSampleSize);
        opts.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
        Log.i(TAG,"after[width:"+bitmap.getWidth()+",height:"+bitmap.getHeight()+"]");
        return bitmap;
    }
}

方法2:使用LruCache进行图片缓存(内存缓存)

package com.example.utils;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.Log;

/**
 *解决OOM的方式2---->使用LruCache进行内存缓存
 */
public class BitmapLruCacheUtils
{
    private static final String TAG = "BitmapCacheUtils";
    private static BitmapLruCacheUtils instance = new BitmapLruCacheUtils();
    private BitmapLruCache cache = null;
    private BitmapLruCacheUtils()
    {
        int maxSize = (int) Runtime.getRuntime().maxMemory();//以字节单位,注意和sizeof方法单位一致
        int cacheSize = maxSize / 8;
        cache = new BitmapLruCache(cacheSize);
    }
    
    public static BitmapLruCacheUtils getInstance()
    {
        return instance;
    } 
    /**
     * 将bitmap图片加入内存缓存
     * @param key
     * @param bitmap
     */
    public void addBitmapToMemoryCache(String key,Bitmap bitmap)
    {
        if(cache!=null && getBitmapFromMemoryCache(key)==null)
        {
            cache.put(key, bitmap);
            Log.i(TAG,"---->>>put");
        }
    }
    
    /**
     * 根据指定的键获取内存缓存中的图片
     * @param key
     * @return
     */
    public Bitmap getBitmapFromMemoryCache(String key)
    {
        if(key == null || cache == null)
        {
            return null;
        }
        Bitmap bitmap =  cache.get(key);
        if(bitmap == null)
        {
            Log.i(TAG,"---->>>get failed");
        }else
        {
            Log.i(TAG,"---->>>get success");
        }
        return bitmap;
    }
    
    private class BitmapLruCache extends LruCache<String,Bitmap>
    {
        public BitmapLruCache(int cacheSize)
        {
            super(cacheSize);
        }
        @Override
        protected int sizeOf(String key, Bitmap value)
        {
            return value.getRowBytes() * value.getHeight();//以字节为单位
        }
    }
}
方法3:文件缓存
基本原理就是将图片存到文件中,下次加载图片时就从文件缓存中获取,如果没有获取到才去网络上下载。
我们需要定义一个阀值,即最大缓存的空间,当超过阀值时,就会根据近期最少使用的淘汰算法来删除部分缓存。另外当sd卡存储空间不足时也会清除部分缓存。
package com.example.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Comparator;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;

/**
 * @author Rowand jj
 *
 *文件缓存
 */
public class BitmapFileCacheUtils
{
    /**
     *图片缓存路径 
     */
    private static final String IMG_CACH_DIR = "/imgCache";
    
    /**
     *缓存的扩展名 
     */
    private static final String CACHE_TAIL = ".cach";
    
    /**
     * 最大缓存空间,单位是mb
     */
    private static final int CACHE_SIZE = 10;
    
    /**
     * sd卡内存低于此值时将会清理缓存,单位是mb
     */
    private static final int NEED_TO_CLEAN = 10;

    private static final String TAG = "BitmapFileCacheUtils";
    /**
     * 从缓存中获取一张图片
     */
    public static Bitmap getBitmapFromFile(String key)
    {
        if(key==null || !isSdcardAvailable())
        {
            return null;
        }
        String path = Environment.getExternalStorageDirectory().getPath()+IMG_CACH_DIR+"/"+convertKeyToFilename(key);
        File file = new File(path);
        if(file.exists())
        {
            Bitmap bitmap = BitmapFactory.decodeFile(path);
            if(bitmap == null)
            {
                file.delete();
            }
            else
            {
                updateFileModifiedTime(path);
                Log.i(TAG,"get file success...");
                return bitmap;
            }
        }
        return null;
    }
    /**
     * 将图片存入文件缓存
     */
    public static void addBitmapToFile(String key,Bitmap bm)
    {
        if(bm == null || key == null|| !isSdcardAvailable())
        {
            return;
        }
        //视情况清除部分缓存
        removeCache(Environment.getExternalStorageDirectory().getPath()+IMG_CACH_DIR);
        
        String filename = convertKeyToFilename(key);
        File dir = new File(Environment.getExternalStorageDirectory().getPath()+IMG_CACH_DIR);
        if(!dir.exists())
        {
            dir.mkdirs();
        }
        File file = new File(dir, filename);
        try
        {
            OutputStream out = new FileOutputStream(file);//这里需要注意,如果指定目录不存在,应该先调用mkdirs生成目录,否则可能创建文件失败
            bm.compress(CompressFormat.JPEG,100, out);
            out.close();
            Log.i(TAG,"add to fils success...");
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    /**
     * 
     * 清除40%的缓存,这些缓存被删除的优先级根据近期使用时间排列,越久没被使用,越容易被删除
     */
    private static void removeCache(String dirPath)
    {
        File dir = new File(dirPath);
        File[] files = dir.listFiles();
        if(files == null)
        {
            return;
        }
        double total_size = 0;
        for(File file : files)
        {
            total_size+=file.length();
        }
        total_size = total_size/1024/1024;
        Log.i(TAG,"total"+total_size);
        if(total_size > CACHE_SIZE || getSdCardFreeSpace() <= NEED_TO_CLEAN)
        {
            Log.i(TAG,"remove cache...");
            int removeFactor = (int) (files.length*0.4);
            Arrays.sort(files, new FileLastModifiedComparator());
            for(int i = 0; i < removeFactor; i++)
            {
                files[i].delete();
            }
        }
    }
    
    /**
     *获取sd卡可用空间
     */
    private static int getSdCardFreeSpace()
    {
        StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
        double freespace = stat.getAvailableBlocks()*stat.getBlockSize();
        return (int) (freespace/1024/1024);
    }
    /**
     *判断sd卡是否可用
     * @return
     */
    private static boolean isSdcardAvailable()
    {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }
    /**
     * 将关键字转化为文件名
     */
    private static String convertKeyToFilename(String key)
    {
        if(key == null)
        {
            return "";
        }
        return key.hashCode()+CACHE_TAIL;
    }
    /**
     * 更新文件最后修改时间
     */
    private static void updateFileModifiedTime(String path)
    {
        File file = new File(path);
        file.setLastModified(System.currentTimeMillis());
    }

    private static class FileLastModifiedComparator implements Comparator<File>
    {
        @Override
        public int compare(File lhs, File rhs)
        {
            if(lhs.lastModified() > rhs.lastModified())
            {
                return 1;
            }else if(lhs.lastModified() == rhs.lastModified())
            {
                return 0;
            }else
            {
                return -1;
            }
        }
    }
}

抱歉!评论已关闭.