美文网首页工作生活
final修饰的Boolean(布尔值)可以被修改值?

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

作者: 疯震震 | 来源:发表于2019-06-30 19:22 被阅读0次

好记性不如烂笔头。生活中多做笔记,不仅可以方便自己,还可以方便他人。

背景

前几天,测试同学提了个跟头像有关的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的一个坑。

总结

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

相关文章

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

    好记性不如烂笔头。生活中多做笔记,不仅可以方便自己,还可以方便他人。 背景 前几天,测试同学提了个跟头像有关的bu...

  • final finally finalize

    final可以修饰变量,方法,类。修饰变量,变量的值不能被修改;修饰方法,方法不能被重写;修饰类,类不能被继承。 ...

  • 《Oracle Java SE编程指南》13-06:final修

    内容导航: 前言 1、final修饰符 final修饰属性、局部变量,值不能被修改final修饰类,不能被继承fi...

  • final修饰符 ---- 抽象类

    final修饰符: 被final修饰的成员变量和局部变量,编译后变为常量,不能修改值 被final修饰的成员函数,...

  • final关键字

    final修饰基本类型和引用类型的区别 final修饰基本数据类型,基本数据类型属性的值(数值or布尔值or字符值...

  • final

    final 修饰 的变量 不能改变值 修饰的方法 不能被重写 但是可以重载 修饰的类不能被继承

  • final修饰的变量

    final的作用 被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可...

  • final、finally、finalize区别

    final:修饰符,修饰类则表示类不能被继承,修饰方法和变量则表示方法或者变量不可以被修改,只能使用。 final...

  • final关键字

    final关键字 final可以修饰变量 变量必须被初始化,初始化以后不能被修改(即变成了常量) final可以修...

  • final,static,抽象类,接口,多态

    final关键字 final可以修饰变量 变量必须被初始化,初始化以后不能被修改(即变成了常量) final可以修...

网友评论

    本文标题:final修饰的Boolean(布尔值)可以被修改值?

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