getDrawable()缓存带来的问题

我们经常通过getContext().getResources().getDrawable()来获取一个Drawable,当因为Android机制使用了缓存机制,直接setColorFilter()修改颜色,可能同时也会改变其他getDrawable()的颜色。在我们项目中经常遇到,自己的图片莫名其妙就被改了其他颜色。

我们先来看一下getDrawable()的源码吧。

  public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
       ......
       加载drawable
        final Drawable res = loadDrawable(value, id, theme);
        synchronized (mAccessLock) {
            if (mTmpValue == null) {
                mTmpValue = value;
            }
        }
        return res;
    }

    /*package*/ Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
        if (TRACE_FOR_PRELOAD) {
            // Log only framework resources
            if ((id >>> 24) == 0x1) {
                final String name = getResourceName(id);
                if (name != null) {
                    Log.d("PreloadDrawable", name);
                }
            }
        }

        final boolean isColorDrawable;
        final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
        final long key;
        //Drawable分为2种类型,colorDrawable(如R.color.xxx)和其他Drawable,先计算缓存的索引key
        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            isColorDrawable = true;
            caches = mColorDrawableCache;
            key = value.data;
        } else {
            isColorDrawable = false;
            caches = mDrawableCache;
            key = (((long) value.assetCookie) << 32) | value.data;
        }

        // 先从要设置主题的缓存获取
        if (!mPreloading) {
            final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
            if (cachedDrawable != null) {
                return cachedDrawable;
            }
        }

        // 从缓存获取ConstantState,子类BitmapState主要缓存了Bitmap和Paint,所以使用同个ConstantState才会互相影响
        final ConstantState cs;
        if (isColorDrawable) {
            cs = sPreloadedColorDrawables.get(key);
        } else {
            cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
        }

        //new出drawable
        final Drawable dr;
        if (cs != null) {
            final Drawable clonedDr = cs.newDrawable(this);
            if (theme != null) {
                dr = clonedDr.mutate();//这里注意了,这个会重新创建一个ConstantState
                dr.applyTheme(theme);
                dr.clearMutated();
            } else {
                dr = clonedDr;
            }
        } else if (isColorDrawable) {
            //无缓存的color,直接创建
            dr = new ColorDrawable(value.data);
        } else {
            //回去创建相应的drawable类型
            dr = loadDrawableForCookie(value, id, theme);
        }

        //缓存ConstantState
        if (dr != null) {
            dr.setChangingConfigurations(value.changingConfigurations);
            cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
        }

        return dr;
    }

接口我们来看一下setColorFilter方法

在BitmapDrawable中

    @Override
    public void setColorFilter(ColorFilter cf) {
        //修改了State的Oaint属性,而State在通过缓存获取时自然会有影响
        mBitmapState.mPaint.setColorFilter(cf);
        invalidateSelf();
    }

解决方法,上面我们提到了mutate方法,

   @Override
    public Drawable mutate() {
        if (!mMutated && super.mutate() == this) {
            //创建BitmapState
            mBitmapState = new BitmapState(mBitmapState);
            mMutated = true;
        }
        return this;
    }

    //
     BitmapState(BitmapState bitmapState) {
            mBitmap = bitmapState.mBitmap;
            mTint = bitmapState.mTint;
            mTintMode = bitmapState.mTintMode;
            mThemeAttrs = bitmapState.mThemeAttrs;
            mChangingConfigurations = bitmapState.mChangingConfigurations;
            mGravity = bitmapState.mGravity;
            mTileModeX = bitmapState.mTileModeX;
            mTileModeY = bitmapState.mTileModeY;
            mTargetDensity = bitmapState.mTargetDensity;
            mBaseAlpha = bitmapState.mBaseAlpha;
            //Paint会新建,从而解决Paint带来的问题
            mPaint = new Paint(bitmapState.mPaint);
            mRebuildShader = bitmapState.mRebuildShader;
            mAutoMirrored = bitmapState.mAutoMirrored;
        }

结论

通过getDrawable()获取BitmapDrawable是,会埋下一些坑,如修改颜色,最好再调一些mutate。当然也可以直接new BitmapDrawable,但bitmap就得不到复用,内存消耗增大。

展开阅读全文

没有更多推荐了,返回首页