final修飾的Boolean(布爾值)能夠被修改值?

好記性不如爛筆頭。生活中多作筆記,不只能夠方便本身,還能夠方便他人。緩存

背景

前幾天,測試同窗提了個跟頭像有關的bug,我去檢查代碼,梳理邏輯,打log,調試代碼。頭像的顯示採用的是Glide庫的組件。大概的代碼邏輯以下:bash

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,其中出現這樣的一組狀況:ide

... setImage, test = true
    ... onResourceReady, test = false
複製代碼

test變量爲final類型,理論上打出來的log,要麼都是true,要麼都是false,不該該出現一個true一個false的啊。難道final修飾的boolean值也能夠被修改?測試

緣由

不用懷疑,被final修飾的boolean值是不能夠被修改的。問題出如今glide。查看了glide源碼的into方法(glide版本是4.5):ui

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;
  }
複製代碼

仔細看源碼,就會發現這個地方:this

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就會回收了。問題就出在這裏url

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的一個坑。spa

總結

雖然是個簡單問題,可是也告訴我一個道理,遇到問題,多看源碼,多分析源碼。調試

相關文章
相關標籤/搜索