好记性不如烂笔头。生活中多做笔记,不仅可以方便自己,还可以方便他人。
背景
前几天,测试同学提了个跟头像有关的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的一个坑。
总结
虽然是个简单问题,但是也告诉我一个道理,遇到问题,多看源码,多分析源码。