Android Drawable缓存
Android获取Drawable对象方式为getResources().getDrawable(@DrawableResId int res), 只可能返回***Drawable、BitmapDrawable对象, 当然id不存在会抛异常。
public abstract class Drawable { ... }
在/framework/base/graphics/java/android/graphics目录下有很多Drawable派生类。
下面讲解下返回对象是如何产生的, 核心逻辑在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的时候, 另一个实例也会受影响。
如果想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 Http RequestCache缓存策略
- Android根据上下文对象Context找到对应的Activity
- Android解析json数组对象
- Android中的Parcel机制 实现Bundle传递对象
- Android 将从网络获取的数据缓存到私有文件
- Android 把从网络获取的图片缓存到内存中
- Android 多线程之synchronized锁住的是代码还是对象(二)
- Android Handler机制5之Message简介与消息对象对象池
- Android 对象序列化之你不知道的 Serializable