图片加载优化,拒绝OOM

最近作了个资讯类的app项目,涉及到大量的图片加载,因公司项目框架已经集成了Glide用于加载图片,理所固然就直接用了Glide来加载app中的图片。本来觉得用了Glide第三方框架就能够高枕无忧了,然并卵,一发包测试,测试反馈部分图片没法加载,各类OOM。然而本身测试却没有任何问题,此刻个人心里是崩溃的。一问细节,测试机型版本4.0,RAM1GB。开发调试时,机型的配置是市面上主流及较高的配置,因此一点问题都察觉不到。没有办法,谁让合同上签的最低兼容版本是4.0呢?只能作适配了。android

1、OOM的缘由

OOM:所谓的OOM指的就是Out-of-Memory内存不足啦。Android上加载图片OOM无非也就那么几个。git

一、Bitmap用完没有回收,致使内存泄漏。
二、手机像素愈来愈高,照片体积愈来愈大,在上传及加载时如不进行压缩处理,OOM是常有的事。
三、机型偏旧、内存偏小。近几年Android的发力生猛,机型配置虽然一路飙升,可是仍然有一部分人还在用着两年前的机器。做为app的厂商又不能放弃这一部分用户。无奈,开发时仍是得根据机型作适配。github

2、解决方案

  • 首选固然仍是得选择第三方图片加载库,主流的加载库无非是UniversalImageLoader、Fresco、Glide、Picasso,推荐Glide。
  • 按照界面图片尺寸,加载不一样尺寸的图片
  • LruCache 缓存工具类

要实现图片加载最优,单单靠以上某一种方式处理确定是不靠谱的,因此咱们要使用的是三者结合来处理。是的,你没有听错。算法

3、分析起因

1.市面上主流的图片加载开源库,在磁盘缓存,内存管理,图片加载优化方面已经作了很好的处理,犯不着本身去实现一套图片加载机制,选择第三方开源库也是理所固然。通常状况,直接用第三方库加载图片便可,几乎不用作额外处理,固然复杂的状况就须要结合第三方库进行优化处理了。七牛云存储

2.按照不一样的图片控件尺寸去加载图片,能够减小内存开销,节省资源,提升加载速度。例如微信朋友圈,在列表界面加载缩略小图,点击查看时才加载大图。咱们项目开发时,图片上传与存储用的是七牛云存储,而七牛云存储自己提供强大的图片处理API,能够根据请求的连接,获取不一样尺寸的图片,方便开发们结合自身项目需求,实现最优图片尺寸加载方案。七牛图片处理API文档地址放在文章最底下,有兴趣的能够了解下。缓存

3.今天的主角LruCache,什么是LruCache?LruCache是android提供的一个缓存工具类,其算法是最近最少使用算法。它把最近使用的对象用“强引用”存储在LinkedHashMap中,而且把最近最少使用的对象在缓存值达到预设定值以前就从内存中移除。其在API12被引进,低版本能够用support包中的类。因此咱们用LruCache来缓存加载的Bitmap,当内存低于咱们设定的值后,LruCache便会自动帮咱们回收用不到的资源。微信

4、代码

具体缘由已经分析,废话很少说,直接上代码app

一、LruCacheUtils工具类框架

import android.graphics.Bitmap;
import android.util.LruCache;

/**
 * Created by leo on 16/8/17.
 * LruCache 图片缓存优化处理类
 */
public class LruCacheUtils extends LruCache<String, Bitmap> {
    //获取手机内存大小
    private static int MAXMEMONRY = (int) (Runtime.getRuntime().maxMemory() / 1024);
    private static LruCacheUtils cacheUtils;

    private LruCacheUtils(int maxSize) {
        super(maxSize);
    }

    /**
     * 单例
     */
    public static LruCacheUtils getInstance() {
        if (cacheUtils == null) {
            //建立对象时分配缓存,咱们取内存的5分之一
            cacheUtils = new LruCacheUtils(MAXMEMONRY / 5);
        }
        return cacheUtils;
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }

    @Override
    protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
    }

    /**
     * 清理缓存
     */
    public void clearCache() {
        if (cacheUtils.size() > 0) {
            cacheUtils.evictAll();
        }
    }


    /**
     * 添加缓存图片
     */
    public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (cacheUtils.get(key) != null) {
            return;
        }
        if (!isEmpty(key) && bitmap != null) {
            cacheUtils.put(key, bitmap);
        }
    }


    /**
     * 获取缓存图片
     */
    public synchronized Bitmap getBitmapFromMemCache(String key) {
        if (isEmpty(key)) {
            return null;
        }
        Bitmap bm = cacheUtils.get(key);
        if (bm != null && !bm.isRecycled()) {
            return bm;
        }
        return null;
    }

    /**
     * 移除缓存
     *
     * @param key
     */
    public synchronized void removeImageCache(String key) {
        if (isEmpty(key)) {
            return;
        }
        Bitmap bm = cacheUtils.remove(key);
        if (bm != null && !bm.isRecycled()) {
            bm.recycle();
        }
    }


    /**
     * 判断字符串是否为空
     *
     * @param str
     * @return
     */
    public boolean isEmpty(String... str) {
        if (str == null) {
            return true;
        }
        for (String s : str) {
            if (s == null || s.isEmpty() || s.trim().isEmpty()) {
                return true;
            }
        }
        return false;
    }


}

二、LruCacheUtils使用ide

String url = http://i2.buimg.com/567571/d208d52913b997bb.jpg?imageView2/2/w/
     200;
     ImageView photoView = new ImageView();
     //判断缓存中是否已经缓存过该图片,有则直接拿Bitmap,没有则直接调用Glide加载并缓存Bitmap
     Bitmap bitmap = LruCacheUtils.getInstance().getBitmapFromMemCache(url);
     if (bitmap != null) {
            photoView.setImageBitmap(bitmap);
     } else {
            PhotoLoader.displayImageTarget(photoView, url, getTarget(photoView, 
            url, position));
        }

三、图片加载方法

/**
     * 加载图片 Target
     *
     * @param imageView
     * @param target
     * @param url
     */
    public void displayImageTarget(final ImageView imageView, final String 
    url, BitmapImageViewTarget target) {
        Glide.get(imageView.getContext()).with(imageView.getContext())
                .load(url)
                .asBitmap()//强制转换Bitmap
                .diskCacheStrategy(DiskCacheStrategy.NONE)
                .into(target);
    }


    /**
     * 获取BitmapImageViewTarget
     */
    private BitmapImageViewTarget getTarget(ImageView imageView, final String url, 
    final int position) {
        return new BitmapImageViewTarget(imageView) {
            @Override
            protected void setResource(Bitmap resource) {
                super.setResource(resource);
                //缓存Bitmap,以便于在没有用到时,自动回收
                LruCacheUtils.getInstance().addBitmapToMemoryCache(url, 
                            resource);
            }
        };
    }

5、调试查看。

优化完成后,运行程序,在Android studio中找到Monitors一栏,进行图片查看测试,就能清楚的看到内存变化以及释放的过程啦。优化以前是直接使用Glide进行加载图片,内存曾一路飙升到200M,而且很难释放。加了缩略图以及LruCache优化后,内存一直保持在40M-80M之间。测试结果,基本上没有重现过OOM的状况。

PS:建议app中全部加载过的bitmap都直接扔到LruCacheUtils中进行缓存,在bitmap没有使用时,方便系统对齐回收。调用上面代码前,请记得集成Glide开源库哦。若是你使用以上方法进行图片加载优化,仍是会出现OOM的话,那就说明......你可能要换手机了……


实例地址:https://github.com/wangzhiyuan888/GlideAndLruCacheDemo

相关文章
相关标签/搜索