记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进行修改
  • 不修改源码的方式暂未找到,等待你的支持,哈哈

你可能感兴趣的:(记Fresco#placeholder的alpha值错乱修复)