final修饰的Boolean(布尔值)可以被修改值?

栏目: Android · 发布时间: 5年前

内容简介:前几天,测试同学提了个跟头像有关的bug,我去检查代码,梳理逻辑,打log,调试代码。头像的显示采用的是Glide库的组件。大概的代码逻辑如下:在不同的地方,调用了上面的方法(setImage)多次,打log,其中出现这样的一组情况:test变量为final类型,理论上打出来的log,要么都是true,要么都是false,不应该出现一个true一个false的啊。

前几天,测试同学提了个跟头像有关的bug,我去检查代码,梳理逻辑,打log,调试代码。头像的显示采用的是Glide库的组件。大概的代码逻辑如下:

private void setImage(ImageView imageView, String url, final boolean test) {
        Log.d(TAG, "setImage, test = " + test);
        Glide.with(this)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    protected void setResource(@Nullable Bitmap resource) {

                    }

                    @Override
                    public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                        super.onResourceReady(resource, transition);
                        if (test) {
                            Log.d(TAG, "onResourceReady, test = " + test);
                        }
                    }
                });
    }
复制代码

问题

在不同的地方,调用了上面的方法(setImage)多次,打log,其中出现这样的一组情况:

... setImage, test = true
    ... onResourceReady, test = false
复制代码

test变量为final类型,理论上打出来的log,要么都是true,要么都是false,不应该出现一个true一个false的啊。 难道final修饰的boolean值也可以被修改?

原因

不用怀疑,被final修饰的boolean值是不可以被修改的。问题出现在glide。查看了glide源码的into方法(glide版本是4.5):

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }
复制代码

仔细看源码,就会发现这个地方:

if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        previous.begin();
      }
      return target;
    }
复制代码
@Override
  public boolean isEquivalentTo(Request o) {
    if (o instanceof SingleRequest) {
      SingleRequest<?> that = (SingleRequest<?>) o;
      return overrideWidth == that.overrideWidth
          && overrideHeight == that.overrideHeight
          && Util.bothModelsNullEquivalentOrEquals(model, that.model)
          && transcodeClass.equals(that.transcodeClass)
          && requestOptions.equals(that.requestOptions)
          && priority == that.priority
          // We do not want to require that RequestListeners implement equals/hashcode, so we don't
          // compare them using equals(). We can however, at least assert that the request listener
          // is either present or not present in both requests.
          && (requestListener != null
          ? that.requestListener != null : that.requestListener == null);
    }
    return false;
  }
复制代码

如果当前request的配置跟之前的previous配置是一样的,而且没有设置跳过缓存(isSkipMemoryCacheWithCompletePreviousRequest),就会直接拿之前的previous去请求,当前构造的request就会回收了。问题就出在这里

private void setImage(ImageView imageView, String url, final boolean test) {
        Log.d(TAG, "setImage, test = " + test);
        Glide.with(this)
                .asBitmap()
                .load(url)
                .into(new ImageViewTarget<Bitmap>(imageView) {
                    @Override
                    protected void setResource(@Nullable Bitmap resource) {

                    }

                    @Override
                    public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
                        super.onResourceReady(resource, transition);
                        if (test) {
                            Log.d(TAG, "onResourceReady, test = " + test);
                        }
                    }
                });
    }
复制代码

所以,上面的ImageViewTarget并不是新构建的target,而是以前旧的那个,所以上面的test变量的值并不是新传入的,而是之前传入的。OK,问题解决了,这是glide的一个坑。

总结

虽然是个简单问题,但是也告诉我一个道理,遇到问题,多看源码,多分析源码。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

自己动手做iOS App

自己动手做iOS App

张子怡 / 电子工业出版社 / 2017-8 / 69.00

《自己动手做iOS App:从设计开发到上架App Store》为想要接触iOS 应用设计、开发的读者提供了由浅入深的详细指导。从iOS 应用制作的步骤是什么,应该使用什么软件,如何发布应用到App Store,到iOS 的设计理念是什么,如何正确书写Swift 语言,再到后端和客户端是如何交互运作的等,本书配合图示,精辟、直观地阐明了iOS 应用制作中的种种疑问。 如果你是一位第一次接触i......一起来看看 《自己动手做iOS App》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具