decodeResource

getResources().getDrawable(id)

会执行

 public Drawable getDrawable(int id) throws NotFoundException {
        synchronized (mTmpValue) {
            TypedValue value = mTmpValue;
            getValue(id, value, true);
            return loadDrawable(value, id);
        }
    }

 

 

  1     /*package*/ Drawable loadDrawable(TypedValue value, int id)
  2             throws NotFoundException {
  3 
  4         if (TRACE_FOR_PRELOAD) {
  5             // Log only framework resources
  6             if ((id >>> 24) == 0x1) {
  7                 final String name = getResourceName(id);
  8                 if (name != null) android.util.Log.d("PreloadDrawable", name);
  9             }
 10         }
 11 
 12         final long key = (((long) value.assetCookie) << 32) | value.data;
 13         boolean isColorDrawable = false;
 14         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
 15                 value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
 16             isColorDrawable = true;
 17         }
 18         Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
 19 
 20         if (dr != null) {
 21             return dr;
 22         }
 23 
 24         Drawable.ConstantState cs = isColorDrawable ?
 25                 sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
 26         if (cs != null) {
 27             dr = cs.newDrawable(this);
 28         } else {
 29             if (isColorDrawable) {
 30                 dr = new ColorDrawable(value.data);
 31             }
 32 
 33             if (dr == null) {
 34                 if (value.string == null) {
 35                     throw new NotFoundException(
 36                             "Resource is not a Drawable (color or path): " + value);
 37                 }
 38 
 39                 String file = value.string.toString();
 40 
 41                 if (TRACE_FOR_MISS_PRELOAD) {
 42                     // Log only framework resources
 43                     if ((id >>> 24) == 0x1) {
 44                         final String name = getResourceName(id);
 45                         if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
 46                                 + Integer.toHexString(id) + ": " + name
 47                                 + " at " + file);
 48                     }
 49                 }
 50 
 51                 if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
 52                         + value.assetCookie + ": " + file);
 53 
 54                 if (file.endsWith(".xml")) {
 55                     try {
 56                         XmlResourceParser rp = loadXmlResourceParser(
 57                                 file, id, value.assetCookie, "drawable");
 58                         dr = Drawable.createFromXml(this, rp);
 59                         rp.close();
 60                     } catch (Exception e) {
 61                         NotFoundException rnf = new NotFoundException(
 62                             "File " + file + " from drawable resource ID #0x"
 63                             + Integer.toHexString(id));
 64                         rnf.initCause(e);
 65                         throw rnf;
 66                     }
 67 
 68                 } else {
 69                     try {
 70                         InputStream is = mAssets.openNonAsset(
 71                                 value.assetCookie, file, AssetManager.ACCESS_STREAMING);
 72         //                System.out.println("Opened file " + file + ": " + is);
 73                         dr = Drawable.createFromResourceStream(this, value, is,
 74                                 file, null);
 75                         is.close();
 76         //                System.out.println("Created stream: " + dr);
 77                     } catch (Exception e) {
 78                         NotFoundException rnf = new NotFoundException(
 79                             "File " + file + " from drawable resource ID #0x"
 80                             + Integer.toHexString(id));
 81                         rnf.initCause(e);
 82                         throw rnf;
 83                     }
 84                 }
 85             }
 86         }
 87 
 88         if (dr != null) {
 89             dr.setChangingConfigurations(value.changingConfigurations);
 90             cs = dr.getConstantState();
 91             if (cs != null) {
 92                 if (mPreloading) {
 93                     if (isColorDrawable) {
 94                         sPreloadedColorDrawables.put(key, cs);
 95                     } else {
 96                         sPreloadedDrawables.put(key, cs);
 97                     }
 98                 } else {
 99                     synchronized (mTmpValue) {
100                         //Log.i(TAG, "Saving cached drawable @ #" +
101                         //        Integer.toHexString(key.intValue())
102                         //        + " in " + this + ": " + cs);
103                         if (isColorDrawable) {
104                             mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
105                         } else {
106                             mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
107                         }
108                     }
109                 }
110             }
111         }
112 
113         return dr;
114     }

 

请注意18行,这里我们会去尝试读取CachedDrawable,看看这里是否有我们需要读取的Drawable。显然,第一次读取的时候,是不会存在缓存。所以我们继续往下看。

 

 1  private Drawable getCachedDrawable(long key) {
 2         synchronized (mTmpValue) {
 3             WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
 4             if (wr != null) {   // we have the key
 5                 Drawable.ConstantState entry = wr.get();
 6                 if (entry != null) {
 7                     //Log.i(TAG, "Returning cached drawable @ #" +
 8                     //        Integer.toHexString(((Integer)key).intValue())
 9                     //        + " in " + this + ": " + entry);
10                     return entry.newDrawable(this);
11                 }
12                 else {  // our entry has been purged
13                     mDrawableCache.delete(key);
14                 }
15             }
16         }
17         return null;
18     }

 

关键是73行,这里会从资源中创建一个Drawable对象。这个函数最终会调用到drawableFromBitmap(),其中会new BitmapDrawable(res, bm):

 

1     public BitmapDrawable(Resources res, Bitmap bitmap) {
2         this(new BitmapState(bitmap), res);
3         mBitmapState.mTargetDensity = mTargetDensity;
4     }

请注意,我们创建了一个BitmapState对象。

 

继续看loadDrawable()函数,来到90行,这里,我们会把新的Drawable的ConstantState加入到缓存中。这个ConstantState对于BitmapDrawable来说,实际就是一个BitmapState对象

 

 

final static class BitmapState extends ConstantState {
}

 

然后,我们就完成第一次读取BitmapDrawable对象。

 如果我们再次读取这个BitmapDrawable对象,回头看loadDrawable()的第18行,我们会从CachedDrawable中拿:

 

 1     private Drawable getCachedDrawable(
 2             LongSparseArray<WeakReference<ConstantState>> drawableCache,
 3             long key) {
 4         synchronized (mTmpValue) {
 5             WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
 6             if (wr != null) {   // we have the key
 7                 Drawable.ConstantState entry = wr.get();
 8                 if (entry != null) {
 9                     //Log.i(TAG, "Returning cached drawable @ #" +
10                     //        Integer.toHexString(((Integer)key).intValue())
11                     //        + " in " + this + ": " + entry);
12                     return entry.newDrawable(this);
13                 }
14                 else {  // our entry has been purged
15                     drawableCache.delete(key);
16                 }
17             }
18         }
19         return null;
20     }

请看12行,对于BitmapDrawable的BitmapState来说,newDrawable()就是:

1         @Override
2         public Drawable newDrawable(Resources res) {
3             return new BitmapDrawable(this, res);
4         }

看到什么了?对了,两次读取到的Drawable共享了一个BitmapState。其实,所有的ConstantState都是共享的。NinePatchDrawable中的ConstantState,即NinePatchState,也是共享的。

 

那问题出在哪儿?请注意这个名字ConstantState。实际上,Drawable的设计者希望,能够共享给所有Drawable使用的state都必须是Constant的。但是,不幸的是BitmapDrawable的设计者却不打算遵循这个规范。请回头看看本文开始的地方写的BitmapDrawable的alpha是通过BitmapState的Paint来实现的。而BitmapState是共享的。也就是说同一个BitmapDrawable的alpha值是共享的!!而NinePatchDrawable就没有这个问题,其alpha设置并不共享。

 

会造成什么问题?

假设你有两个一样的Button,使用了BitmapDrawable作为背景,当你对某一个Button做alpha动画的时候,实际上另外一个Button的alpha值也在变化!!!但是因为另外一个Button没有重绘,所以你看不到,如果这个时候某一个事件导致界面重绘,比如锁屏再解锁,就会发现另外一个Button的alpha值也变化了。

 

所以,请慎用BitmapDrawable,最好不要用他来做alpha动画。当然,也不仅是alpha值才有这个问题,所有BitmapState中共享的值都会存在这个问题。请看:

 

1         Bitmap mBitmap;
2         int mChangingConfigurations;
3         int mGravity = Gravity.FILL;
4         Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS);
5         Shader.TileMode mTileModeX = null;
6         Shader.TileMode mTileModeY = null;
7         int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
8         boolean mRebuildShader;

 

 

 

posted on 2013-04-25 15:54  mingfeng002  阅读(667)  评论(0编辑  收藏  举报