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>
|
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));
|
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>
|
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
;
}
}
|
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;
}
}
|
跟到这,相信大家都跟我一样了解了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