一、前言
在上个版本开发结束时,测试同学发现了一个偶现的 bug,页面是 RecyclerView 瀑布流显示 GIF 动画。
问题表现是页面标题栏白色背景变透明,进一步排查发现 App 中使用到该色值做背景的地方全部都被改了,而且改动无规律,即以色值 #ffffff 做背景的透明度都被改了。
比较容易复现的路径是,在该页面快速来回滑动列表。
这是一个 ColorDrawable 缓存导致的问题,老生常谈,今天就从源码的角度分析一下原因。
二、初步分析
通过问题表现可以知道肯定是有隐藏的操作改到背景的 alpha 值,进而导致全局改动。
2.1 在该页面中使用到 #ffffff 的地方主要有两个:
a. xml 设置控件背景 android:background=”@color/white_an”
b. 设置加载图片的占位图 ImageLoadParams.defaultholder = R.color.white_a
这两个色值都是定义在 values 中 #ffffff,都是很常规的操作。
查看系统方法 android.graphics.drawable.ColorDrawable#setAlpha
public void setAlpha(int alpha) {
alpha += alpha >> 7; // make it 0..256
final int baseAlpha = mColorState.mBaseColor >>> 24;
final int useAlpha = baseAlpha * alpha >> 8;
final int useColor = (mColorState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
if (mColorState.mUseColor != useColor) {
mColorState.mUseColor = useColor;
invalidateSelf();
}
}
代码很简单,计算 alpha,颜色不一致时把最终计算得到的 useColor 赋值给 mColorState,并重绘自身。这个 mColorState 是什么呢?查看源码发现它是 ColorState 的实例,而 ColorState 又是继承自抽象类 ConstantState。
ConstatntState 的源码描述:
This abstract class is used by {@link Drawable}s to store shared constant state and data between Drawables.
{@link BitmapDrawable}s created from the same resource will for instance share a unique bitmap stored in their ConstantState.
也就是说,每个 Drawable 都共享一个唯一的 ConstantState 对象,这是为了共享 Drawable 的状态和数据,从同一个 res 中创建的 Drawable,它们会共享同一个 ConstantState 对象。
三、具体分析
我们看一下从 xml 加载 backgroud 的过程。
3.1 在 View 中解析 attr
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
...
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
...
}
}
...
}
3.2 继续跟到 android.content.