Android Drawable缓存

Android获取Drawable对象方式为getResources().getDrawable(@DrawableResId int res), 只可能返回***Drawable、BitmapDrawable对象, 当然id不存在会抛异常。

public abstract class Drawable {
 ...
 }

Android Drawable缓存_第1张图片
在/framework/base/graphics/java/android/graphics目录下有很多Drawable派生类。
Android Drawable缓存_第2张图片

下面讲解下返回对象是如何产生的, 核心逻辑在ResourcesImpl.java

   private final DrawableCache mDrawableCache = new DrawableCache();  //缓存的是ConstantState抽象类的对象,而不是Drawable对象;  弱引用,不影响GC
    private final DrawableCache mColorDrawableCache = new DrawableCache(); //弱引用,不影响GC
    
    Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
            int density, @Nullable Resources.Theme theme)
            throws NotFoundException {
                 final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
                 //getDrawableForDensity(id, 0, theme);density为0, 所以useCache默认为true,  
                 //app取自己的drawable时mPreloading为false, 即下面的if条件为真
                if (!mPreloading && useCache) {
                     final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
                     if (cachedDrawable != null) {
                        cachedDrawable.setChangingConfigurations(value.changingConfigurations);
                    return cachedDrawable;
                }
            }

不要被上面的caches.getInstance误导, 它创建了新的实例(newDrawble方法). 但是复用了ConstantState, 如果是BitmapDrawable的话就复用了Bitmap对象。

  public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
        final Drawable.ConstantState entry = get(key, theme);
        if (entry != null) {
            return entry.newDrawable(resources, theme);
        }
        return null;
    }
    
   public Drawable newDrawable() {
            return new BitmapDrawable(this, null);
        }

做个练习:

BitmapDrawable firstDrawable = (BitmapDrawable) getResources()
                .getDrawable(R.drawable.school_picture);
BitmapDrawable secondDrawable = (BitmapDrawable) getResources()
                .getDrawable(R.drawable.school_picture);

2个drawable实例指向的是同一个Bitmap对象(其实是Bitmap
State), 在操作其中一个drawble的时候, 另一个实例也会受影响。
Android Drawable缓存_第3张图片

如果想2个drawable互不影响, 即使用不同的ConstantState实例。 可以调用BitmapDrawable.java的mutate方法:

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

即使用新的ConstantState实例替换mBitmapState参数值。

ResouresesImpl.java是通过文件后缀判断Drawable类型的, 如果后缀是xml则返回ColorDrawable对象, 否则返回BitmapDrawable对象。

private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density) {
            ...
            if (file.endsWith(".xml")) {
                    final XmlResourceParser rp = loadXmlResourceParser(
                            file, id, value.assetCookie, "drawable");
                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                    rp.close();
                } else {
                    final InputStream is = mAssets.openNonAsset(
                            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                    AssetInputStream ais = (AssetInputStream) is;
                    dr = decodeImageDrawable(ais, wrapper, value);   //返回BitmapDrawable
                }

    private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
            @NonNull Resources wrapper, @NonNull TypedValue value) {
        ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
                            wrapper, value);
        try {
            return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
            });
        } catch (IOException ioe) {
            // This is okay. This may be something that ImageDecoder does not
            // support, like SVG.
            return null;
        }
    }
    public static Drawable decodeDrawable(@NonNull Source src,
            @Nullable OnHeaderDecodedListener listener) throws IOException {
        Bitmap bitmap = decodeBitmap(src, listener);
        **return new BitmapDrawable(src.getResources(), bitmap);**
    }

后缀是xml时,执行了Drawable.createFromXmlForDensity方法, 最终会执行到DrawableInflator.java的inflateFromTag方法, 返回上面graphics文件夹下面的类。。

    private Drawable inflateFromTag(@NonNull String name) {
        switch (name) {
            case "selector":
                return new StateListDrawable();
            case "animated-selector":
                return new AnimatedStateListDrawable();
            case "level-list":
                return new LevelListDrawable();
            case "layer-list":
                return new LayerDrawable();
            case "transition":
                return new TransitionDrawable();
            case "ripple":
                return new RippleDrawable();
            case "adaptive-icon":
                return new AdaptiveIconDrawable();
            case "color":
                return new ColorDrawable();
            case "shape":
                return new GradientDrawable();
            case "vector":
                return new VectorDrawable();
            case "animated-vector":
                return new AnimatedVectorDrawable();
            case "scale":
                return new ScaleDrawable();
            case "clip":
                return new ClipDrawable();
            case "rotate":
                return new RotateDrawable();
            case "animated-rotate":
                return new AnimatedRotateDrawable();
            case "animation-list":
                return new AnimationDrawable();
            case "inset":
                return new InsetDrawable();
            case "bitmap":
                return new BitmapDrawable();
            case "nine-patch":
                return new NinePatchDrawable();
            case "animated-image":
                return new AnimatedImageDrawable();
            default:
                return null;
        }
    }

小结:
1、 getResources().getDrawable只可能返回Drawable(根据xml里的tag生成不同实例)、BitmapDrawable或者抛异常;
2、 在ResourcesImpl.java里缓存了ConstantState弱引用, 对应BitmapDrawable对象来说缓存了Bitmap引用。
3、通过调用drawable的mutate方法可以创建新的ConstantState对象。
4、ResourcesImpl.java通过文件名后缀返回不同的drawable对象, 后缀是xml时根据文件里的tag名称返回
Drawable对象,其它情况返回BitmapDrawable对象;
5、 在Activity、View退出时不需要解绑Drawable, 即ImageView的setImageDrawable(null)。 因为ResourcesImpl缓存的是Bitmap弱引用,可以被GC回收;

你可能感兴趣的:(Android)