Android的Drawable缓存机制源码分析

Android获取Drawable的方式一般是Resources.getDrawable(int),Framework会返回给你一个顶层抽象的Drawable对象。而在Framework中,系统使用了享元的方式来节省内存。为了证明这一点,我们来写一个小demo:

我们在我们的Android项目中引入一个简单的图片test.png。由于我们只是为了享元的结论,我们定义一个简单的Activity,并复写它的onCreate方法:

List<Bitmap> list = new ArrayList<Bitmap>(); Bitmap bitmap = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for (int i = 0; i < 10; i ++) { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); list.add(bitmap); } ImageView iv = new ImageView(this); iv.setImageBitmap(bitmap); this.setContentView(iv); }

可能你这里有疑惑为何要需要一个list把Bitmap存储起来,这重要是为了避免GC引起的内存释放。好了我们将我们的内存打印出来会发现我们加入了10个Bitmap占用的实际内存是:26364K。我们在转化成为Drawable的方式:

List<Drawable> list = new ArrayList<Drawable>(); Drawable bitmap = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); for (int i = 0; i < 10; i ++) { bitmap = this.getResources().getDrawable(R.drawable.test); list.add(bitmap); } ImageView iv = new ImageView(this); iv.setImageDrawable(bitmap); this.setContentView(iv); }


我们再打印内存,发现内存已经降到了:7844K,这部分数据基本就证明了我们的结论。那么有没有可能是Resources缓存了相同的drawable。当然不是,你可以写一个简单代码测试一下:

Drawable d1 = this.getResources().getDrawable(R.drawable.test); Drawable d2 = this.getResources().getDrawable(R.drawable.test); System.out.println(">>>d1 == d2 ? = "+(d1 == d2));

你会发现输出的是false。实际上,享元这点我们基本达成了共识,关键Framwork来包装Drawable的时候还引入了组合模式,Framework本身缓存的是你这个Drawable的核心元数据。

Resources.java Drawable loadDrawable(TypedValue value, int id) throws NotFoundException { ... Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key); ... }


从代码可以看出,系统对Drawable主要分成两大类,实际上还有一类熟悉预加载类的Drawable,不过不作为我们讨论的重点,由于我们load的并不属于color类型的Drawable,因此我们对应的享元池由mDrawableCache对象实现。

Resources.java private Drawable getCachedDrawable( LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { synchronized (mAccessLock) { WeakReference<Drawable.ConstantState> wr = drawableCache.get(key); if (wr != null) { // we have the key Drawable.ConstantState entry = wr.get(); if (entry != null) { //Log.i(TAG, "Returning cached drawable @ #" + // Integer.toHexString(((Integer)key).intValue()) // + " in " + this + ": " + entry); return entry.newDrawable(this); } else { // our entry has been purged drawableCache.delete(key); } } } return null; }

我们通过调用代码,会发现我们存储在数据池中的根本不是我们的Drawable对象,而是一个叫做Drawable.ConstantState类型的对象,而且用了弱引用包装起来。ConstantState是一个抽象类,有多个子类的实现

public static abstract class ConstantState { /** * Create a new drawable without supplying resources the caller * is running in. Note that using this means the density-dependent * drawables (like bitmaps) will not be able to update their target * density correctly. One should use {@link #newDrawable(Resources)} * instead to provide a resource. */ public abstract Drawable newDrawable(); /** * Create a new Drawable instance from its constant state. This * must be implemented for drawables that change based on the target * density of their caller (that is depending on whether it is * in compatibility mode). */ public Drawable newDrawable(Resources res) { return newDrawable(); } /** * Return a bit mask of configuration changes that will impact * this drawable (and thus require completely reloading it). */ public abstract int getChangingConfigurations(); /** * @hide */ public Bitmap getBitmap() { return null; } }

由于我们使用的是BitmapDrawable,而BitmapDrawable对应的ConstantState是BitmapState

final static class BitmapState extends ConstantState { Bitmap mBitmap; int mChangingConfigurations; int mGravity = Gravity.FILL; Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); Shader.TileMode mTileModeX = null; Shader.TileMode mTileModeY = null; int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; boolean mRebuildShader; boolean mAutoMirrored; BitmapState(Bitmap bitmap) { mBitmap = bitmap; } BitmapState(BitmapState bitmapState) { this(bitmapState.mBitmap); mChangingConfigurations = bitmapState.mChangingConfigurations; mGravity = bitmapState.mGravity; mTileModeX = bitmapState.mTileModeX; mTileModeY = bitmapState.mTileModeY; mTargetDensity = bitmapState.mTargetDensity; mPaint = new Paint(bitmapState.mPaint); mRebuildShader = bitmapState.mRebuildShader; mAutoMirrored = bitmapState.mAutoMirrored; } @Override public Bitmap getBitmap() { return mBitmap; } @Override public Drawable newDrawable() { return new BitmapDrawable(this, null); } @Override public Drawable newDrawable(Resources res) { return new BitmapDrawable(this, res); } @Override public int getChangingConfigurations() { return mChangingConfigurations; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzgyf.html