Glide不发起请求
问题
接入第三方广告SDK时,使用对方的自渲染信息流广告,在拿到对方图片物料并使用Glide加载网络图片时,Glide设置了listener却没有回调。
广告位的布局如下:
<FrameLayout
android:id="@+id/third_ad_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/ad_panel"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ad_img_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</FrameLayout>
物料请求如下:
val img_view = findViewById<ImageView>(R.id.ad_img_view)
Glide.with(this@MainActivity)
.asBitmap()
.load("imgurl")
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Bitmap>?,
isFirstResource: Boolean
): Boolean {
Log.e("test_url", "onLoadFailed ----")
return false
}
override fun onResourceReady(
resource: Bitmap?,
model: Any?,
target: Target<Bitmap>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
Log.e("test_url", "onResourceReady ----")
return false
}
})
.into(img_view)
期望结果是图片物料展示,且listener打印回调。实际结果是:物料没有展示且任何回调都没有打印。
为什么会出现上面的结果那?
Glide源码分析
我们知道:Glide会在 into 函数中创建一个 request,然后在 onSizeReady 后进行网络请求。上面的问题是没有进行请求,那么肯定是 onSizeReady 没有调用。
// 如果设置了 overrideWidth和overrideHeight,直接执行 onSizeReady
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else { // 否则,查看 ViewTarget.getSize()
target.getSize(this);
}
public void getSize(@NonNull SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
private boolean isSizeValid(int size) {
return size > 0 || size == LayoutParams.WRAP_CONTENT;
}
private void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
// 每次绘制回来,都检测一边宽高是否有效
if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight))
return;
}
// 有效的话,则调用 onSizeReady
notifyCbs(currentWidth, currentHeight);
if (observer.isAlive()) {
observer.removeOnPreDrawListener(layoutListener);
}
layoutListener = null;
}
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
// 如果宽高都是有效的,则调用 onSizeReady
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} else {
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
// 监听view的绘制过程
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}
从分析源码可得:当我们使用 into(targetView) 且没有使用 override 设置大小, 如果view的大小无效,则不会进行网络请求。
解决方法
从上面的结论可得,我们只要满足 onSizeReady 触发的条件,就可以实现进行网络请求。
- 调用时,使用 override 重写大小。
- 将ImageView的宽高设置为 wrap_content或者固定的宽高
可是,在项目中ImageView必须设置为 match_parent,又该如何解决问题那?
其实,上面分析源码的时候,有一个问题没有解决:为什么我们设置的ImageView监听中没有大小那?它不应该和父布局的大小一致吗?如果在测量后,我们能得到想要的大小,问题不就解决了嘛。所以,最终的问题还是view测量的问题。
当我使用下面的布局方式,就解决了问题:
<FrameLayout
android:id="@+id/third_ad_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ReleativeLayout
android:id="@+id/ad_panel"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ad_img_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ReleativeLayout>
</FrameLayout>
FrameLayout | FrameLayout | ImageView |
match_parent | 同xml一致 | 同xml一致 |
wrap_content | 同父布局一样 | 同父布局一样 |
FrameLayout | ReleativeLayout | ImageView |
match_parent | 同xml一致 | 同xml一致 |
wrap_content | 同父布局一样 | 同xml一致 |
从上表可得:ReleativeLayout 在测量时,不会影响子view的测量方式。
所以,在使用ReleativeLayout作为ImageView的父容器时,ImageView测量的大小是window的大小。
上面的逻辑,我是在Android源码中看到的。具体位置:
ReleativeLayout.getChildMeasureSpec() 和 ViewGroup.getChildMeasureSpec(), 可是,Android为什么要这样做那?
ReleativeLayout会测量水平和垂直方向的view,根据相对属性,测量子View的大小。FrameLayout 总是按照左上角的坐标,一层一层的测量view的大小,而LinearLayout是在onMeasure中自己计算每个子view的左上角坐标,然后根据左上角坐标顺序测量子view的大小。所以,ReleativeLayout自己一套测量子view逻辑,而FrameLayout和LinearLayout公用一套测量子View的逻辑。