問題背景
出問題的placeholder是一個ColorDrawable,對應色值的透明為100
- 實際展示時發現色值的透明度會有概率的發生變化,如:可能從FFE7E7E7 => 2FE7E7E7 或者 FFE7E7E7 => 57E7E7E7
- 同時一旦發生變化后,后續通過getResource#getDrawabke(resId)獲取的資源的透明度也是錯誤的
正常的展示
|
不正常的展示
|
---|
問題探究
1. 抓取問題特征
- ColorDrawable
- 透明度發生變化且取值隨機
- 后續調用系統api獲取的透明度也是錯誤的
2. 是否僅影響ColorDrawable?
一般使用中,占位圖所使用的資源大都是ColorDrawable或者BitmapDrawable
線上發生問題的placeholder,改版前使用的是.9圖,改版后使用的是colorDrawable
3. 對比發現
對比ColorDrawable,BitmapDrawable和NinePatchDrawable對于setAlpha方法的實現方式異同
-
ColorDrawable設置不同的alpha值會影響底層的ColorState,會導致后續通過getResource#getDrawabke(resId)獲取的資源的透明度也是錯誤的
@Override 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(); } }
-
BitmapDrawable設置不同的alpha值會影響底層的BitmapState,理論上也會導致后續通過getResource#getDrawabke(resId)獲取的資源的透明度也是錯誤的
@Override public void setAlpha(int alpha) { final int oldAlpha = mBitmapState.mPaint.getAlpha(); if (alpha != oldAlpha) { mBitmapState.mPaint.setAlpha(alpha); invalidateSelf(); } }
-
NinePatchDrawable設置不同的alpha值僅影響當前的drawable,并不會影響NinePatchState
@Override public void setAlpha(int alpha) { if (mPaint == null && alpha == 0xFF) { // Fast common case -- leave at normal alpha. return; } getPaint().setAlpha(alpha); invalidateSelf(); }
而線上改版前正是用的.9圖,所以之前線上問題并不是很明顯,更難發現
4. 排查問題來源
- 正常展示的網絡圖片時候,會發起一個從placeholder過渡至actualDrawable的漸變過程。
fresco的實現原理是包裝一個FadeDrawable,不斷修改其drawable的alpha值,修改的時候會默認先mutate一下,避免影響所有的drawable - 但如果在漸變過程中被中斷并且下次這個hierarchy又被復用的時候,fresco會執行一個DrawableProperties的包括過程,相當于默認繼承之前的drawable屬性
而在這個過程中設置alpha時并沒有對drawable進行mutate!!
//DrawableProperties
public void applyTo(Drawable drawable) {
if (drawable != null) {
if (this.mAlpha != -1) {
drawable.mutate().setAlpha(this.mAlpha);
}
... ...
}
}
問題解決
在知道問題根源后解決問題就比較簡單了,在DrawableProperties設置alpha時進行下mutate。
具體實現方式:
- 整體重新編譯源碼
- 單獨重新編譯drawee
- 用字節碼工具對aar進行修改
- 不修改源碼的方式暫未找到,等待你的支持,哈哈