十、性能优化-优化Bitmap资源

背景:Android系统分配给每个应用程序的内存有限,而图片资源非常消耗内存(即Bitmap)
原因:若对Bitmap的使用与内存管理不当,则可能引发内存溢出,从而导致程序崩溃

Bitmap占多大的内存?
bitmap.getAllocationByteCount(); 或者
bitmap.getByteCount();

加载Bitmap的时候,通常要做这几件事

一、根据分辨率适配与缩放图片

  1. 设置多套图片资源
    如果项目大小允许,至少在xh/xxh/xxxh中都放置图片,然后通过BitmapFactory.decodeResource获取

  2. 缩放图片比例进行压缩,按图片分辨率需求加载,用inSampleSize
    示例代码:

     BitmapFactory.Options opts = new BitmapFactory.Options();
     opts.inSampleSize = 4;
     Bitmap bitmap = BitmapFactory.decodeFile(imageFile, opts);  
    

二、按需选择合适的解码方式:options.inPreferredConfig

优化原因:不同的图片解码方式对应的内存占用大小相差很大
知识点:

  1. ARGB_4444: 基本不用,因为图片比较失真

  2. 示例ARGB_8888: 32位的ARGB位图,由4个8位组成(即32位),每个像素占8位,即A=8,R=8,G=8,B=8。 8+8+8+8 = 32位= 4个字节。即1个像素占4个字节
    示例:若1张480*800的图片,格式是ARGB_8888,将占1536000字节,即1500KB的内存
    3.默认使用解码方式:ARGB_8888
    特别注意:PNG图片设置无效
    示例代码:

    BitmapFactory.Options options1 = new BitmapFactory.Options();
    options1.inPreferredConfig = Bitmap.Config.RGB_565;
    Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher,options1);

四、使用完毕后释放图片资源

优化原因:使用完毕后若不释放图片资源,容易造成内存泄漏,从而导致内存溢出
优化方案:
在 Android2.3.3(API 10)前,调用 Bitmap.recycle()方法
在 Android2.3.3(API 10)后,采用软引用(SoftReference)
示例代码:

    View view = findViewById(R.id.button);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
Drawable drawable = new BitmapDrawable(bitmap);
SoftReference drawableSoftReference = 
                                            new SoftReference(drawable);
    Drawable bgdrawable = drawableSoftReference.get();
if(bgdrawable != null) {
    view.setBackground(bgdrawable);
}

需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃

三、设置图片缓存

优化原因:重复加载图片资源消耗太多资源(cpu,内存与流量)
优化方案:使用三级缓存机制或者软引用
三级缓存机制:内存缓存 – 本地缓存(硬盘、数据库或文件) – 网络缓存

为什么要使用三级缓存
假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。
特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知

什么是三级缓存:

网络缓存, 不优先加载, 速度慢,浪费流量
本地缓存, 次优先加载, 速度快
内存缓存, 优先加载, 速度最快

三级缓存原理:
首次加载App时,通过网络交互来获取图片,之后将图片保存到本地SD卡和内存中
之后运行App时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的,如果SD卡中没有,则加载网络的
内存缓存示例代码
关键代码LruCache

/**
 * 三级缓存之内存缓存
 */
public class MemoryCacheUtils {

    // private HashMap mMemoryCache=new HashMap();//1.因为强引用,容易造成内存溢出,所以考虑使用下面弱引用的方法
    // private HashMap> mMemoryCache = new HashMap();//2.因为在Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache
    private LruCache mMemoryCache;

    public MemoryCacheUtils(){
        long maxMemory = Runtime.getRuntime().maxMemory()/8;//得到手机最大允许内存的1/8,即超过指定内存,则开始回收
        //需要传入允许的内存最大值,虚拟机默认内存16M,真机不一定相同
        mMemoryCache=new LruCache((int) maxMemory){
            //用于计算每个条目的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                int byteCount = value.getByteCount();
                return byteCount;
            }
        };

    }

    /**
     * 从内存中读图片
     * @param url
     */
    public Bitmap getBitmapFromMemory(String url) {
        //Bitmap bitmap = mMemoryCache.get(url);//1.强引用方法
        /*2.弱引用方法
        SoftReference bitmapSoftReference = mMemoryCache.get(url);
        if (bitmapSoftReference != null) {
            Bitmap bitmap = bitmapSoftReference.get();
            return bitmap;
        }
        */
        Bitmap bitmap = mMemoryCache.get(url);
        return bitmap;

    }

    /**
     * 往内存中写图片
     * @param url
     * @param bitmap
     */
    public void setBitmapToMemory(String url, Bitmap bitmap) {
        //mMemoryCache.put(url, bitmap);//1.强引用方法
        /*2.弱引用方法
        mMemoryCache.put(url, new SoftReference(bitmap));
        */
        mMemoryCache.put(url,bitmap);
    }
}

图片三级缓存这个,现在基本都是用成熟的图片框架Glide或者picasso
针对Glide的skipMemoryCache(false)//启动内存缓存 diskCacheStrategy() //硬盘缓存策略

参考:https://www.jianshu.com/nb/21401973