Android的Drawable缓存机制源码分析

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
     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);
     }</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
    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);
    }</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)
             throws NotFoundException {
...
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,
             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.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.
          */
         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

 

 

?
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 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是如何存入的。

 

?
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) {
                     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<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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值