博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Glide preload和into的区别
阅读量:2354 次
发布时间:2019-05-10

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

一、背景

  贝壳2.6.0版本使用Glide preload方法替换了部分显示图片的方式, 在灰度期间发现控件显示了错误的图片或者崩溃问题。

Fatal Exception: java.lang.RuntimeException:Canvas: trying to use a recycled bitmap android.graphics.Bitmap@25e89bf at android.graphics.Canvas.throwIfCannotDraw(Canvas.java:1271) at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:257) at android.graphics.Canvas.drawBitmap(Canvas.java:1415) at com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable.draw(GlideBitmapDrawable.java:101)

 

二、原因分析

    Glide使用activityResources、LruResourceCache、LruBitmapPool等3级内存和文件缓存LazyDiskCacheProvider。

 

  activeResources是个Map, key值(EngineKey)根据10个参数组合生成,value是ResourceWeakReference类型; resource是EngineResource类并实现引用计数。

activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
public void recycle() {    if (acquired > 0) {        throw new IllegalStateException("Cannot recycle a resource while it is still acquired");    }    if (isRecycled) {        throw new IllegalStateException("Cannot recycle a resource that has already been recycled");    }    isRecycled = true;    resource.recycle();  //实际上将Bitmap添加到BitmapPool} /** * Increments the number of consumers using the wrapped resource. Must be called on the main thread. * * 

* This must be called with a number corresponding to the number of new consumers each time new consumers * begin using the wrapped resource. It is always safer to call acquire more often than necessary. Generally * external users should never call this method, the framework will take care of this for you. *

*/void acquire() { if (isRecycled) { throw new IllegalStateException("Cannot acquire a recycled resource"); } if (!Looper.getMainLooper().equals(Looper.myLooper())) { throw new IllegalThreadStateException("Must call acquire on the main thread"); } ++acquired;} /** * Decrements the number of consumers using the wrapped resource. Must be called on the main thread. * *

* This must only be called when a consumer that called the {@link #acquire()} method is now done with the * resource. Generally external users should never callthis method, the framework will take care of this for * you. *

*/void release() { if (acquired <= 0) { throw new IllegalStateException("Cannot release a recycled or not yet acquired resource"); } if (!Looper.getMainLooper().equals(Looper.myLooper())) { throw new IllegalThreadStateException("Must call release on the main thread"); } if (--acquired == 0) { listener.onResourceReleased(key, this); }}

LruResourceCache使用最近最少使用算法跟保存从activeResources移出的resource, 如果LruCache满了则移除记录并添加到LruBitmapPool。

 

LruBitmapPool的作用是缓存废弃的Bitmap(包括从activeResources或者LurResourcesCache移出的), 每次解码图片时先从bitmappool找是否有合适的bitmap实例复用,找到了则从BitmapPool里移出,找不到则实例化个Bitmap对象。

                                      

                                                                                 into方式bitmap转换关系

                                                               使用BitmapPool

 

三、preload和into区别

  preload会创建PreloadTarget, 在回调onResourceReady时执行了Glide.clear(this),  将当前GlideDrawable从activeResources移动到LruResourcesCache。 即执行了cache.put(cacheKey, resource);

public final class PreloadTarget
extends SimpleTarget
{ /** * Returns a PreloadTarget. * * @param width The width in pixels of the desired resource. * @param height The height in pixels of the desired resource. * @param
The type of the desired resource. */ public static
PreloadTarget
obtain(int width, int height) { return new PreloadTarget
(width, height); } private PreloadTarget(int width, int height) { super(width, height); } @Override public void onResourceReady(Z resource, GlideAnimation
glideAnimation) { Glide.clear(this); }}

 

 划重点: 如果当前界面在onResourceReady回调函数里使用drawable刷新到界面, 那么可能导致crash或者渲染错误图片的问题。 原因是LruCache里缓存的图片可能被移出到BitmapPool; 而Glide在transform图片时会从BitmapPool里取Bitmap对象(UI显示错误的图片), 也可能执行bitmap的recycle方法(程序崩溃)。  即在onResourceReady里使用的GlideDrawable内存区域可能在其它地方篡改。

 

into会创建GenericTarget, 在回调onResourceReady时

public final class GenericRequest
implements Request, SizeReadyCallback, ResourceCallback { ...public void onResourceReady(Resource
resource) { if (resource == null) { onException(new Exception("Expected to receive a Resource
with an object of " + transcodeClass + " inside, but instead got null.")); return; } Object received = resource.get(); if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) { releaseResource(resource); onException(new Exception("Expected to receive an object of " + transcodeClass + " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}" + " inside Resource{" + resource + "}." + (received != null ? "" : " " + "To indicate failure return a null Resource object, " + "rather than a Resource object containing null data.") )); return; } if (!canSetResource()) { releaseResource(resource); // We can't set the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } onResourceReady(resource, (R) received);} /** * Internal {@link #onResourceReady(Resource)} where arguments are known to be safe. * * @param resource original {@link Resource}, never
null * @param result object returned by {@link Resource#get()}, checked for type and never
null */private void onResourceReady(Resource
resource, R result) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache, isFirstResource)) { GlideAnimation
animation = animationFactory.build(loadedFromMemoryCache, isFirstResource); target.onResourceReady(result, animation); //执行GlideDrawbleImageViewTarget的setResource方法,即view.setImageDrawable } notifyLoadSuccess(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: " + (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache); }}.... }

 

四、结论

      Glide的preload回调函数onResourceReady返回的resource并不可靠, 在使用时要新创建个实例或者保存成文件后使用它。

LJImageLoader.with(getContext()).url(ConstantUtil.URL_CDN_WALLET_REWARD_SELECTED)    .dontAnimate()    .listener(new ILoadListener() {      @Override public boolean onException(Exception e, String model) {        return false;      }       @Override public boolean onResourceReady(Drawable resource, String model) {        if (resource != null) {          Bitmap bmp = Tools.drawableToBitmap(resource);          if (bmp != null) {            rootView.setBackground(new BitmapDrawable(getResources(), bmp));          }        }        return false;      }    }).preload();

 

 

转载地址:http://hzqvb.baihongyu.com/

你可能感兴趣的文章
如何安装最纯净的Windows系统,玩转重装操作系统
查看>>
RabbitMQ安装使用及数据同步
查看>>
用户中心
查看>>
授权中心
查看>>
乐优商城—购物车
查看>>
乐优商城—订单微服务
查看>>
《剑指offer》思路与实现总结--Java
查看>>
字符串数组转成矩阵
查看>>
VC++ UDP转TCP互发数据 UDP为服务端 TCP为客户端 可修改IP和端口最小化 2TCP/UDP中转
查看>>
仿养生网 帝国CMS 更新后域名栏目链接一直没变 解决方法:在后台地图--模板标签替换里直接全部替换
查看>>
微信扫码自动群发消息给所有人技术分析 之通过https请求获取微信网页版登录二维码图
查看>>
C#源码刷新网页 最小化托盘http get和post请求配置保存版权时间限制定时调用 单实例运行,如果已经运行则激活窗口到最前显示
查看>>
域名解密 商家联盟会员消费管理系统_连锁店会员积分系统 带微信(域名加密的) aqinxiaodian
查看>>
android蓝牙4.0BLE及2.0 2.1 apk 串口助手带16个自定义按键和自定义指令 字符接收 十六进制或字符发送
查看>>
爬虫采集 通用正则表达式
查看>>
织梦学习 变量的运用 添加新变量 删除新变量 添加上传视频mp4
查看>>
CocosCreator+VS2017提示“要求的 VS 版本:[2013, 2015, 2017]”解决办法 无法找到 v140_xp 的生成工具
查看>>
助学贷款系统导入预申请时问题解决办法汇总
查看>>
FTP连接阿里云不能获得列表目录等功能,能连接,21端口也打开了。原因FTP是双向的,阿里云入出方向安全组规则必须添加本地随机端口
查看>>
读书程序标准化建模--高效阅读学习,越学越有劲/趣
查看>>