关于Picasso加载图片Callback不执行问题

问题背景

代码大致如下,Target或Callback的回调有时候不执行。

https://github.com/square/picasso/issues/352

第一种:使用Target

  1. Picasso.get().load(URL).into(new Target() {
  2. @Override
  3. public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
  4. Log.i(TAG, "onBitmapLoaded: " + from);
  5. }
  6. @Override
  7. public void onBitmapFailed(Exception e, Drawable errorDrawable) {
  8. Log.e(TAG, "onBitmapFailed");
  9. }
  10. @Override
  11. public void onPrepareLoad(Drawable placeHolderDrawable) {
  12. }
  13. });

第二种:使用Callback

  1. ImageView imageView = new ImageView(this);
  2. Picasso.get().load(URL).into(imageView, new Callback() {
  3. @Override
  4. public void onSuccess() {
  5. Log.i(TAG, "onSuccess: ");
  6. }
  7. @Override
  8. public void onError(Exception e) {
  9. Log.e(TAG, "onError" );
  10. }
  11. });

以上代码,通常用在预加载图片的业务中。

问题原因

对于这样偶现的问题,第一反应应该是引用持有问题。果然,上述问题是弱引用问题: WeakReference

源码推导:

  1. // 1.先看into方法:RequestCreator.java
  2. public void into(@NonNull Target target) {
  3. // ...
  4. Action action =
  5. new TargetAction(picasso, target, request, memoryPolicy, networkPolicy, errorDrawable,
  6. requestKey, tag, errorResId);
  7. picasso.enqueueAndSubmit(action);
  8. }
  9. // 2.跟到Action:TargetAction.java
  10. final class TargetAction extends Action<Target> {
  11. TargetAction(Picasso picasso, Target target, Request data, int memoryPolicy, int networkPolicy,
  12. Drawable errorDrawable, String key, Object tag, int errorResId) {
  13. // target被传到了super构造器
  14. super(picasso, target, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key, tag,
  15. false);
  16. }
  17. @Override
  18. void complete(Bitmap result, Picasso.LoadedFrom from) {
  19. if (result == null) {
  20. throw new AssertionError(
  21. String.format("Attempted to complete action with no result!\n%s", this));
  22. }
  23. Target target = getTarget();
  24. if (target != null) { // 这一句判断很关键
  25. target.onBitmapLoaded(result, from);
  26. if (result.isRecycled()) {
  27. throw new IllegalStateException("Target callback must not recycle bitmap!");
  28. }
  29. }
  30. }
  31. @Override
  32. void error(Exception e) {
  33. Target target = getTarget();
  34. if (target != null) { // 这一句判断很关键
  35. if (errorResId != 0) {
  36. target.onBitmapFailed(e, picasso.context.getResources().getDrawable(errorResId));
  37. } else {
  38. target.onBitmapFailed(e, errorDrawable);
  39. }
  40. }
  41. }
  42. }
  43. // 3.往上跟到Action的构造方法:Action.java
  44. Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
  45. int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
  46. // ...
  47. this.target =
  48. target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue);
  49. // ...
  50. }

有此可见,如果this.target这个虚引用,要是在gc的时候被回收了,回调自然也不会有了。

场景复现

以下代码模拟系统内存紧张时候的gc过程,这样偶现问题便成了必现,或极大概率出现。

  1. public void preloadImage() {
  2. Picasso.get().load(URL).into(new Target() {
  3. @Override
  4. public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
  5. Log.i(TAG, "onBitmapLoaded: " + from);
  6. }
  7. @Override
  8. public void onBitmapFailed(Exception e, Drawable errorDrawable) {
  9. Log.e(TAG, "onBitmapFailed");
  10. }
  11. @Override
  12. public void onPrepareLoad(Drawable placeHolderDrawable) {
  13. }
  14. });
  15. for (int i = 0; i < 1000; i++) {
  16. double[] b = new double[1000000];
  17. }
  18. System.gc();
  19. }

解决方式

可以考虑通过加持引用,不让若引用销毁。方式有很多种,这里可以考虑用匿名内部类来延长变量的生命周期。

第一种:使用Target

  1. public void preloadImage() {
  2. Log.d(TAG, "preloadImage: preloadImage.");
  3. class Ref {
  4. private Target t;
  5. }
  6. final Ref ref = new Ref();
  7. ref.t = new Target() {
  8. @Override
  9. public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
  10. Log.i(TAG, "preloadImage.onBitmapLoaded: " + from + ref.hashCode());
  11. }
  12. @Override
  13. public void onBitmapFailed(Exception e, Drawable errorDrawable) {
  14. Log.e(TAG, "preloadImage.onBitmapFailed" + ref.hashCode());
  15. }
  16. @Override
  17. public void onPrepareLoad(Drawable placeHolderDrawable) {
  18. }
  19. @Override
  20. protected void finalize() throws Throwable {
  21. super.finalize();
  22. Log.e(TAG, "target.finalize!");
  23. }
  24. };
  25. Picasso.get().load(URL).into(ref.t);
  26. }

第二种:使用Callback

  1. public void preloadImage() {
  2. Log.d(TAG, "preloadImage: start.");
  3. final ImageView imageView = new ImageView(this);
  4. Picasso.get().load(URL).into(imageView, new Callback() {
  5. @Override
  6. public void onSuccess() {
  7. Log.i(TAG, "preloadImage.onSuccess: " + imageView.hashCode());
  8. }
  9. @Override
  10. public void onError(Exception e) {
  11. Log.e(TAG, "preloadImage.onError" + imageView.hashCode());
  12. }
  13. @Override
  14. protected void finalize() throws Throwable {
  15. super.finalize();
  16. Log.e(TAG, "callback.finalize!");
  17. }
  18. });
  19. }

以上。

关于Picasso加载图片Callback不执行问题的更多相关文章

  1. Picasso 加载图片到RelativeLayout之解决方案

    Picasso 加载图片到ImageView 或者自己的自定义View都是可以直接调用对应API的,但是用into(0直接也加载到RelatieLayout就不好使了,可以这样来: Picasso.w ...

  2. (Android图片内存优化)Picasso加载图片 教程。。详细版

    Picasso 是 Android 上一个强大的图片下载和缓存库. 示例代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Picasso.with( ...

  3. 实例演示Android异步加载图片

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  4. 实例演示Android异步加载图片(转)

    本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...

  5. jquery的promise实践--连续加载图片

    在javascript设计模式实践之代理模式--图片预加载中用代理模式实现了图片预加载功能. 现在就更进一步,完成一个能够一张一张的连续图片加载的功能. 功能: 1.一张一张加载图片. 2.加载错误, ...

  6. android 加载图片框架--Glide使用详解

    一.简介 Glide,一个被google所推荐的图片加载库,作者是bumptech.这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方app.(PS:众所 ...

  7. JQuery实现无刷新下拉加载图片

          最近做的一个项目需要做页面无刷新下拉加载图片,调研了一番,大多都采用检测滚动条达到底部,然后利用ajax加载下一页数据对页面数据进行添加,根据这一逻辑,自己写了一个,具体代码如下: JQu ...

  8. android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存

    经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目  Android-Universal-Image-Loader  或者 ignition 都是个很好的选择. 在这里把原来 ...

  9. ListView异步加载图片,完美实现图文混排

    昨天参加一个面试,面试官让当场写一个类似于新闻列表的页面,文本数据和图片都从网络上获取,想起我还没写过ListView异步加载图片并实现图文混排效果的文章,so,今天就来写一下,介绍一下经验. Lis ...

随机推荐

  1. 求1到n的质数个数和O(n)

    也许更好的阅读体验 \(\mathcal{AIM}\) 我们知道: 对于一个合数\(x\) 有\(x=p^{a_1}_1*p^{a_2}_2*...*p^{a_n}_n\) 现在给出一个\(n\) 求 ...

  2. Flume框架的学习使用

    Flume框架的学习使用 Flume简介 Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集.聚集.移动的服务. Flume基于流失架构,容错性强,也很灵活简单 Flume,kafka用 ...

  3. iframe插入视频自动播放代码

    <iframe marginwidth=0 marginheight=0 src='http://www.wsview.com/yzplayerAction!play2.action?autoP ...

  4. C语言编程入门之--第二章编译环境搭建

    第二章 编译环境搭建 导读:C语言程序如何工作,首先需要编译链接成可执行文件,然后就可以运行在不同的环境中,这个“环境”的意思就是比如说,电脑,手机,路由器,蓝牙音箱等等智能设备中,其中编译器启到了关 ...

  5. WebGL中深度碰撞方法总结

    z-fighting问题是三维渲染中常见的问题,本文根据实际工作中遇到的一些场景,进行了系统的总结 一个实际工作中的问题 当两个面离得太近就会发生深度碰撞问题,比如: 遇到深度检测问题,最重要的是先搞 ...

  6. linux初学者-网络管理篇

    linux初学者-网络管理篇 linux学习中,网络管理是非常重要的一个内容,本篇将会介绍一些ip.网关.DNS配置的一些基本内容. 1.ip配置 1.1.ip查询  在linux系统中一般可以使用& ...

  7. Linux基本操作及安装(部分)

    1.分别用cat \tac\nl三个命令查看文件/etc/ssh/sshd_config文件中的内容,   并用自己的话总计出这三个文档操作命令的不同之处? [root@localhost ~]# c ...

  8. hdoj 4706 Children's Day

    题目意思就是用a-z组成一个N,然后到z后又跳回a,输出宽从3到10的N. #include <stdio.h> #include <string.h> char s[14][ ...

  9. jenkins未授权访问漏洞

    jenkins未授权访问漏洞 一.漏洞描述 未授权访问管理控制台,可以通过脚本命令行执行系统命令.通过该漏洞,可以后台管理服务,通过脚本命令行功能执行系统命令,如反弹shell,wget写webshe ...

  10. SpringMVC学习笔记之---简单入门

    SpringMVC简单入门 (一)什么是MVC设计模式 (1)model:模型数据,业务逻辑 (3)view:呈现模型,与用户进行交互 (3)controller:负责接收并处理请求,响应客户端 (二 ...