編輯:關於Android編程
Android獲取Drawable的方式一般是Resources.getDrawable(int),Framework會返回給你一個頂層抽象的Drawable對象。而在Framework中,系統使用了享元的方式來節省內存。為了證明這一點,我們來寫一個小demo:
我們在我們的Android項目中引入一個簡單的圖片test.png。由於我們只是為了享元的結論,我們定義一個簡單的Activity,並復寫它的onCreate方法:
List可能你這裡有疑惑為何要需要一個list把Bitmap存儲起來,這重要是為了避免GC引起的內存釋放。好了我們將我們的內存打印出來會發現我們加入了10個Bitmap占用的實際內存是:26364K。我們在轉化成為Drawable的方式: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); }
Listlist = 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); }
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的核心元數據。
Resources.java
Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
...
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
...
}
Resources.java
private 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;
}
我們通過調用代碼,會發現我們存儲在數據池中的根本不是我們的Drawable對象,而是一個叫做Drawable.ConstantState類型的對象,而且用了弱引用包裝起來。ConstantState是一個抽象類,有多個子類的實現
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
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是如何存入的。
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));
}
}
}
}
...
}
可以看出,當你新生成一個Drawable的時候,就會將Drawable的ConstantState從Drawable中取出,然後放入你Cache池中。
安卓動態調試七種武器之離別鉤 – Hooking(下)
0x00 序隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。另外工具是死的,人是活的,如果能搞懂工具的原理再結合上自身的經驗,
【我的Android進階之旅】 高效的設計稿標注及測量工具Markman介紹
前言 高效的設計稿標注及測量工具Markman介紹。最近有個煩惱是UI設計師可能太忙了,經常給出的UI設計稿中有很多地方都沒有標注,比如長度和顏色值等。這個時候每次都要通
android 設置全屏的兩種方法
現在android的每一個項目都會需要設置為全屏,現在介紹兩種設置為全屏的方式。一、在配置文件中設置android:theme=”@android:style/Theme
Android中實現TCP和UDP傳輸實例
TCP和UDP在網絡傳輸中非常重要,在Android開發中同樣重要。首先我們來看一下什麼是TCP和UDP。什麼是TCP?TCP:Transmission Control