图片加载优化,拒绝OOM
发布日期:2021-08-22 19:57:34 浏览次数:59 分类:技术文章

本文共 5330 字,大约阅读时间需要 17 分钟。

hot3.png

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

一、OOM的原因

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

1、Bitmap用完没有回收,导致内存泄漏。

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

二、解决方案

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

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

三、分析原由

1.市面上主流的图片加载开源库,在磁盘缓存,内存管理,图片加载优化方面已经做了很好的处理,犯不着自己去实现一套图片加载机制,选择第三方开源库也是理所当然。一般情况,直接用第三方库加载图片即可,几乎不用做额外处理,当然复杂的情况就需要结合第三方库进行优化处理了。

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

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

四、代码

具体原因已经分析,废话不多说,直接上代码

1、LruCacheUtils工具类

import android.graphics.Bitmap;import android.util.LruCache;/** * Created by leo on 16/8/17. * LruCache 图片缓存优化处理类 */public class LruCacheUtils extends LruCache
{ //获取手机内存大小 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; }}

2、LruCacheUtils使用

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

3、图片加载方法

/**     * 加载图片 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);            }        };    }

五、调试查看。

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

10232456_lWXB.png

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

实例地址:

转载于:https://my.oschina.net/u/2933456/blog/785895

转载地址:https://blog.csdn.net/weixin_33755554/article/details/92346994 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:android Theme使用总结
下一篇:我的友情链接

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月09日 00时59分16秒