Android的Drawable緩衝機制源碼分析

來源:互聯網
上載者:User

Android的Drawable緩衝機制源碼分析

Android擷取Drawable的方式一般是Resources.getDrawable(int),Framework會返回給你一個頂層抽象的Drawable對象。而在Framework中,系統使用了享元的方式來節省記憶體。為了證明這一點,我們來寫一個小demo:

我們在我們的Android項目中引入一個簡單的圖片test.png。由於我們只是為了享元的結論,我們定義一個簡單的Activity,並複寫它的onCreate方法:

 

List list = new ArrayList();    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 list = new ArrayList();    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.javaprivate Drawable getCachedDrawable(            LongSparseArray> drawableCache,            long key) {        synchronized (mAccessLock) {            WeakReference 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;        }    }
我們可以看到BitmapState對應的newDrawable方法,它將自己作為參數傳遞給BitmapDrawable對象,也就是說BitmapDrawble組合了同一個的BitmapState。這樣就實現了同一個Bitmap資源的複用。

 

跟到這,相信大家都跟我一樣瞭解了Bitmap是如何從cache中取出,我們接下來看一下ConstantState是如何存入的。

 

Resources.loadDrawable(){...                        InputStream is = mAssets.openNonAsset(                                value.assetCookie, file, AssetManager.ACCESS_STREAMING);        //                System.out.println("Opened file " + file + ": " + is);                        // MIUI MOD:                        // dr = Drawable.createFromResourceStream(this, value, is, file, null);                        dr = createFromResourceStream(this, value, is, file, id);                        is.close();... if (dr != null) {            dr.setChangingConfigurations(value.changingConfigurations);            cs = dr.getConstantState();            if (cs != null) {                if (mPreloading) {                    final int changingConfigs = cs.getChangingConfigurations();                    if (isColorDrawable) {                        if (verifyPreloadConfig(changingConfigs, 0, value.resourceId,                                "drawable")) {                            sPreloadedColorDrawables.put(key, cs);                        }                    } else {                        if (verifyPreloadConfig(changingConfigs,                                LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {                            if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) {                                // If this resource does not vary based on layout direction,                                // we can put it in all of the preload maps.                                sPreloadedDrawables[0].put(key, cs);                                sPreloadedDrawables[1].put(key, cs);                            } else {                                // Otherwise, only in the layout dir we loaded it for.                                final LongSparseArray preloads                                        = sPreloadedDrawables[mConfiguration.getLayoutDirection()];                                preloads.put(key, cs);                            }                        }                    }                } else {                    synchronized (mAccessLock) {                        //Log.i(TAG, "Saving cached drawable @ #" +                        //        Integer.toHexString(key.intValue())                        //        + " in " + this + ": " + cs);                        if (isColorDrawable) {                            mColorDrawableCache.put(key, new WeakReference(cs));                        } else {                            mDrawableCache.put(key, new WeakReference(cs));                        }                    }                }            }...}
可以看出,當你新產生一個Drawable的時候,就會將Drawable的ConstantState從Drawable中取出,然後放入你Cache池中。


 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.