美文网首页Android开发Android开发经验谈Android开发
Glide设置默认图片后setImageBitmap,setIm

Glide设置默认图片后setImageBitmap,setIm

作者: 我是黄教主啊 | 来源:发表于2019-01-22 16:58 被阅读10次

    复现场景

    在适配器中添加了一个条件,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

    相关文章

      网友评论

        本文标题:Glide设置默认图片后setImageBitmap,setIm

        本文链接:https://www.haomeiwen.com/subject/keoljqtx.html