Android的Drawable缓存机制源码分析

来源:互联网 发布:宾得wifi软件 编辑:程序博客网 时间:2024/04/30 09:05

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

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

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<bitmap> list =new ArrayList<bitmap>();
 
    Bitmap bitmap =null;
    @Override
    protectedvoid 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);
    }</bitmap></bitmap>
可能你这里有疑惑为何要需要一个list把Bitmap存储起来,这重要是为了避免GC引起的内存释放。好了我们将我们的内存打印出来会发现我们加入了10个Bitmap占用的实际内存是:26364K。我们在转化成为Drawable的方式:

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<drawable> list =new ArrayList<drawable>();
 
   Drawable bitmap =null;
   @Override
   protectedvoid 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);
   }</drawable></drawable>

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

 

 

?
1
2
3
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的核心元数据。

 

 

?
1
2
3
4
5
6
7
Resources.java
 Drawable loadDrawable(TypedValue value,int id)
            throwsNotFoundException {
...
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
...
}

从代码可以看出,系统对Drawable主要分成两大类,实际上还有一类熟悉预加载类的Drawable,不过不作为我们讨论的重点,由于我们load的并不属于color类型的Drawable,因此我们对应的享元池由mDrawableCache对象实现。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Resources.java
private Drawable getCachedDrawable(
            LongSparseArray<weakreference<constantstate>> drawableCache,
            longkey) {
        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);
                    returnentry.newDrawable(this);
                }
                else// our entry has been purged
                    drawableCache.delete(key);
                }
            }
        }
        returnnull;
    }</drawable.constantstate></weakreference<constantstate>
我们通过调用代码,会发现我们存储在数据池中的根本不是我们的Drawable对象,而是一个叫做Drawable.ConstantState类型的对象,而且用了弱引用包装起来。ConstantState是一个抽象类,有多个子类的实现

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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.
         */
        publicabstract 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).
         */
        publicDrawable newDrawable(Resources res) {
            returnnewDrawable();
        }
        /**
         * Return a bit mask of configuration changes that will impact
         * this drawable (and thus require completely reloading it).
         */
        publicabstract int getChangingConfigurations();
 
        /**
         * @hide
         */
        publicBitmap getBitmap() {
            returnnull;
        }
    }
由于我们使用的是BitmapDrawable,而BitmapDrawable对应的ConstantState是BitmapState

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
final static class BitmapStateextends ConstantState {
        Bitmap mBitmap;
        intmChangingConfigurations;
        intmGravity = Gravity.FILL;
        Paint mPaint =new Paint(DEFAULT_PAINT_FLAGS);
        Shader.TileMode mTileModeX =null;
        Shader.TileMode mTileModeY =null;
        intmTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
        booleanmRebuildShader;
        booleanmAutoMirrored;
 
        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
        publicBitmap getBitmap() {
            returnmBitmap;
        }
 
        @Override
        publicDrawable newDrawable() {
            returnnew BitmapDrawable(this,null);
        }
 
        @Override
        publicDrawable newDrawable(Resources res) {
            returnnew BitmapDrawable(this, res);
        }
 
        @Override
        publicint getChangingConfigurations() {
            returnmChangingConfigurations;
        }
    }
我们可以看到BitmapState对应的newDrawable方法,它将自己作为参数传递给BitmapDrawable对象,也就是说BitmapDrawble组合了同一个的BitmapState。这样就实现了同一个Bitmap资源的复用。

 

跟到这,相信大家都跟我一样了解了Bitmap是如何从cache中取出,我们接下来看一下ConstantState是如何存入的。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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) {
                    finalint 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.
                                finalLongSparseArray<drawable.constantstate> 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<drawable.constantstate>(cs));
                        }else {
                            mDrawableCache.put(key,new WeakReference<drawable.constantstate>(cs));
                        }
                    }
                }
            }
...
 
}</drawable.constantstate></drawable.constantstate></drawable.constantstate>

可以看出,当你新生成一个Drawable的时候,就会将Drawable的ConstantState从Drawable中取出,然后放入你Cache池中。


原文地址: http://www.2cto.com/kf/201501/373804.html

0 0
原创粉丝点击