記Fresco#placeholder的alpha值錯亂修復

問題背景

出問題的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進行修改
  • 不修改源碼的方式暫未找到,等待你的支持,哈哈
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容