Android Drawable cache source code analysis

Source: Internet
Author: User

Android Drawable cache source code analysis

Android generally obtains Drawable through Resources. getDrawable (int), and Framework returns a top-level abstract Drawable object. In the Framework, the system uses the metadata-sharing method to save memory. To prove this, let's write a small demo:

We have introduced a simple image test.png in our androidproject. Since we only want to share the meta conclusion, we define a simple Activity and rewrite its onCreate method:

 

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);    }
  
 
You may wonder why you need a list to store Bitmap. This is important to avoid memory release caused by GC. After printing out our memory, we will find that the actual memory occupied by 10 bitmaps is 26364 kb. We are converting to 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);    }
  
 

We printed the memory and found that the memory has been reduced to 7844 kb. This part of data basically proves our conclusion. Is it possible that Resources caches the same drawable. Of course not. You can write a simple code to test it:

 

 

Drawable d1 = this.getResources().getDrawable(R.drawable.test);        Drawable d2 = this.getResources().getDrawable(R.drawable.test);        System.out.println(">>>d1 == d2 ? = "+(d1 == d2));
You will find that the output is false. In fact, we have basically reached a consensus on the metadata. The combination mode is also introduced when Framwork is used to wrap Drawable. The Framework caches the Core Metadata of your Drawable.

 

 

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

The Code shows that the system mainly divides Drawable into two categories. In fact, there is also a type of Drawable that is familiar with the pre-loading class, but it is not the focus of our discussion, because our load does not belong to the color type Drawable, our corresponding metadata pool is implemented by the mDrawableCache object.
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;    }
  
 
By calling the code, we can find that our stored in the data pool is not our Drawable object, but an object called Drawable. ConstantState, and is encapsulated with weak references. ConstantState is an abstract class with multiple sub-classes.

 

 

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;        }    }
Because BitmapDrawable is used, the ConstantState corresponding to BitmapDrawable is 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;        }    }
We can see the newDrawable method corresponding to BitmapState. It passes itself as a parameter to the BitmapDrawable object, that is, BitmapDrawble combines the same BitmapState. In this way, the same Bitmap resource is reused.

 

So far, I believe everyone understands how Bitmap is retrieved from the cache just like me. Let's take a look at how ConstantState is saved.

 

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));                        }                    }                }            }...}
   
  
 
It can be seen that when you generate a Drawable, The Drawable ConstantState will be removed from the Drawable and then put into your Cache pool.


 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.