Glide设置默认图片后setImageBitmap,setImageResource失效问题

复现场景

在适配器中添加了一个条件,path为空就去加载本地图片,不为空就使用Glide去加载图片,伪代码实现如下:

 @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
         if(path==null){
               imageView.setImageResource(R.drawable.my_image)
         }else{
               Glide.with(this).load(path).apply( RequestOptions().placeholder(R.drawable.place).error(R.drawable.error)).into(imageView)
           }
    }

咋一看没啥问题,当path为一个无效地址的时候,imageView会显示error的图片,但是当path为null的时候,发现imageView没有显示my_image,还是显示的error图片,打断点发现imageView.setImageResource(R.drawable.my_image)这句代码是执行了的,但为什么会失效呢?

解决方案

先来说一下解决方案,如果对出现原因(从源码层面分析)感兴趣可以继续往下看

1.使用Glide.with(this).clear(imageView)关闭失败重试
2.使用Glide加载本地图片,不使用原生Api:

Glide.with(imageView).load(R.drawable.my_image).into(imageView)

出现原因

Glide内部有加载失败重试机制,当第一次加载失败,重试机制就会启动,这时imageView.setImageResource(R.drawable.my_image)也同步执行了,但是当重试机制执行完毕后,Glide发现图片最终还是加载失败,所以会将error中设置的图片又添加到imageView上去,所以并不是imageView.setImageResource(R.drawable.my_image)这句代码失效了,而是Glide又重新设置了一遍。

我们可以来瞅一眼源码,我们知道图片加载失败是有一个回调的,所以从回调入手:
先找到RequestListener回调:

public interface RequestListener<R> {


  boolean onLoadFailed(
      @Nullable GlideException e, Object model, Target<R> target, boolean isFirstResource);


  boolean onResourceReady(
      R resource, Object model, Target<R> target, DataSource dataSource, boolean isFirstResource);
}

随后找到onLoadFailed实现类SingleRequest:

  /**
   * A callback method that should never be invoked directly.
   */
  @Override
  public void onLoadFailed(GlideException e) {
    onLoadFailed(e, Log.WARN);
  }

  private void onLoadFailed(GlideException e, int maxLogLevel) {
    stateVerifier.throwIfRecycled();
    int logLevel = glideContext.getLogLevel();
    if (logLevel <= maxLogLevel) {
      Log.w(GLIDE_TAG, "Load failed for " + model + " with size [" + width + "x" + height + "]", e);
      if (logLevel <= Log.INFO) {
        e.logRootCauses(GLIDE_TAG);
      }
    }

    loadStatus = null;
    status = Status.FAILED;

    isCallingCallbacks = true;
    try {
      //TODO: what if this is a thumbnail request?
      if ((requestListener == null
          || !requestListener.onLoadFailed(e, model, target, isFirstReadyResource()))   //可以看到listence的接口调用是在这里,返回值决定了是否往下调用逻辑
          && (targetListener == null
          || !targetListener.onLoadFailed(e, model, target, isFirstReadyResource()))) {
        setErrorPlaceholder();
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadFailed();  //这里会重试一次
  }

可以看到这里会先有一个requestListener的接口调用,这里requestListener其实就是我们的listence监听功能:

Glide.with(imageView).load(url).listener(new RequestListener<Drawable>() {
            @Override
            public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                return false;
            }

            @Override
            public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                return false;
            }
        })

只有返回false才会去调用setErrorPlaceholder()方法,看一下:

  private void setErrorPlaceholder() {
    if (!canNotifyStatusChanged()) {
      return;
    }

    Drawable error = null;
    if (model == null) {
      error = getFallbackDrawable();
    }
    // Either the model isn't null, or there was no fallback drawable set.
    if (error == null) {
      error = getErrorDrawable();
    }
    // The model isn't null, no fallback drawable was set or no error drawable was set.
    if (error == null) {
      error = getPlaceholderDrawable();
    }
    target.onLoadFailed(error);  //target就是我们设置进去的ImageView
  }

在看一下我们的onLoadFailed实现:

  @Override
  public void onLoadFailed(@Nullable Drawable errorDrawable) {
    super.onLoadFailed(errorDrawable);
    setResourceInternal(null);
    setDrawable(errorDrawable);
  }

所以setErrorPlaceholder方法的作用就是把我们设置的error图片设置给我们放进去的ImageView上
我们回到onLoadFailed方法中,发现最后调用了notifyLoadFailed()方法,这个方法是干嘛的呢,看一下源码:

  private void notifyLoadFailed() {
    if (requestCoordinator != null) {
      requestCoordinator.onRequestFailed(this);
    }
  }

是一个接口,我们看一下它的实现,是在ErrorRequestCoordinator中:

    if (!request.equals(error)) {
      if (!error.isRunning()) {
        error.begin();
      }
      return;
    }

    if (parent != null) {
      parent.onRequestFailed(this);
    }

我们发现调用了一个熟悉的方法,begin(),研究过源码的都知道,Glide都会在这个接口实现开始异步加载的方法,所以在这里又会去开始异步加载图片

所以,走一圈下来,大致的流程就是这样子:
SingleRequest类中先调用begin开始异步加载图片->加载失败->onLoadFailed()->调用notifyLoadFailed()方法重试(内部会判断是否需要重试)->需要加载重试->begin

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 【Android 库 Glide】 引用 Android图片加载框架最全解析(一),Glide的基本用法Andro...
    Rtia阅读 11,020评论 0 22
  • 概述 刚才有说到,有些朋友觉得Glide 4相对于Glide 3改动非常大,其实不然。之所以大家会有这种错觉,是因...
    陈晓松快点跑阅读 6,682评论 0 14
  • 最近在做群聊功能,使用了环信的SDK,在环信demo中无意之中看到了glide框架,简单了解后,发现这个框架很强(...
    guggle阅读 6,584评论 1 7
  • 学习来源:郭霖大师博客地址 1、图片加载框架挺多,如Volley、Glide、Picasso、Fresco、本次是...
    子谦宝宝阅读 5,718评论 0 6
  • 一、简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这...
    天天大保建阅读 12,231评论 2 28

友情链接更多精彩内容