【安卓笔记】OOM解决方案

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

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;
            }
        }
    }
}




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Android OOM(Out of Memory)是一种常见的运行时异常,指的是应用程序内存不足的错误。当应用程序试图使用超过系统分配给它的内存时,就会出现这种异常。这可能是由于应用程序在后台加载大量数据、存储过多的对象或图像,或者由于系统资源管理器分配的内存不足所致。 为了解决Android OOM问题,您可以采取以下几种策略: 1. 优化您的代码以减少内存使用量:使用正确的数据类型,避免创建不必要的对象,限制图像和资源的数量,以及优化后台加载过程等。 2. 回收不再使用的内存:当您的应用程序不再需要使用某些内存时,应该及时回收它们。这可以通过调用垃圾回收器(Garbage Collector)来完成。 3. 避免在主线程上执行耗时操作:如果您的应用程序在主线程上执行耗时操作(如大量数据处理),这可能导致系统资源管理器超载,从而引发OOM异常。应该将这些操作移至后台线程。 4. 使用内存分析工具:内存分析工具可以帮助您识别内存泄漏和无效内存引用等问题,从而避免OOM异常的发生。 5. 配置您的应用程序以适应不同的内存配置:如果您正在开发一个需要大量内存的应用程序,您应该考虑在AndroidManifest.xml文件中配置您的应用程序以适应不同的内存配置。例如,您可以设置您的应用程序需要的最低和最高内存限制。 请注意,解决Android OOM问题是一个复杂的过程,需要您仔细分析和优化您的代码。如果您遇到了OOM问题,建议寻求专业的帮助或与开发社区进行讨论。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值