Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
生活随笔
收集整理的這篇文章主要介紹了
Android bitmap.recycle()导致trying to use a recycled bitmap报错分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在android實際項目中,有時會在Activity的onDestroy()做一些資源釋放工作,比如bitmap資源。通常的寫法是這樣
public class NextActivity extends Activity {private ImageView imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_next);}@Overrideprotected void onDestroy() {if (imageView instanceof ImageView) {Drawable d = imageView.getDrawable();if (d != null && d instanceof BitmapDrawable) {Bitmap bmp = ((BitmapDrawable) d).getBitmap();bmp.recycle();bmp = null;}imageView.setImageBitmap(null);imageView.setBackgroundDrawable(null);if (d != null) {d.setCallback(null);}}System.gc();super.onDestroy();} }對應的xml是這樣
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".NextActivity" ><ImageViewandroid:id="@+id/imageview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_launcher" /></RelativeLayout>我們這里模擬啟動Activity操作,MainActivity啟動NextActivity,第一次啟動正常,按back鍵,第二次啟動,就會拋出java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@cd46a67。報錯信息很明確,試圖使用一個已經回收的bitmap。 我們log一下NextActivity的onCreate()方法,打印bitmap的哈希碼值第一次和第二次的實例為什么是相同的呢?每次onCrate()不是應該重新繪制嗎?為什么相同呢?其實這是android的優秀設計,我們這里的bitmap使用xml的src來指定的Drawable,Android系統每次解析圖片優先于從緩存中拿,沒有才去創建,所以第一次是創建的實例,第二次是從緩存中拿到的數據。為了刨根問底,我們看一下源碼。
我們知道xml給控件設置屬性最終都是使用pull解析在用代碼創建,那么我們應該看一下ImageView的構造方法
ImageView.class
public ImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initImageView();TypedArray a = context.obtainStyledAttributes(attrs,com.android.internal.R.styleable.ImageView, defStyle, 0);Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);if (d != null) {setImageDrawable(d);}<span style="white-space:pre"> </span>......a.recycle();//need inflate syntax/reader for matrix}我們重點看第8行,getDrawalbe(com.android.internal.R.styleable.ImageView_src);點進去public Drawable getDrawable(int index) {final TypedValue value = mValue;if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {if (false) {System.out.println("******************************************************************");System.out.println("Got drawable resource: type="+ value.type+ " str=" + value.string+ " int=0x" + Integer.toHexString(value.data)+ " cookie=" + value.assetCookie);System.out.println("******************************************************************");}return mResources.loadDrawable(value, value.resourceId);}return null;}這里13行裝載Drawable,這個方法是Resources的,繼續點進去。 /*package*/ Drawable loadDrawable(TypedValue value, int id)throws NotFoundException {......Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);if (dr != null) {return dr;}Drawable.ConstantState cs;if (isColorDrawable) {cs = sPreloadedColorDrawables.get(key);} else {cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);}if (cs != null) {dr = cs.newDrawable(this);} else {if (isColorDrawable) {dr = new ColorDrawable(value.data);}if (dr == null) {if (value.string == null) {throw new NotFoundException("Resource is not a Drawable (color or path): " + value);}String file = value.string.toString();if (TRACE_FOR_MISS_PRELOAD) {// Log only framework resourcesif ((id >>> 24) == 0x1) {final String name = getResourceName(id);if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"+ Integer.toHexString(id) + ": " + name+ " at " + file);}}if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "+ value.assetCookie + ": " + file);if (file.endsWith(".xml")) {Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);try {XmlResourceParser rp = loadXmlResourceParser(file, id, value.assetCookie, "drawable");dr = Drawable.createFromXml(this, rp);rp.close();} catch (Exception e) {Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);NotFoundException rnf = new NotFoundException("File " + file + " from drawable resource ID #0x"+ Integer.toHexString(id));rnf.initCause(e);throw rnf;}Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);} else {Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);try {InputStream is = mAssets.openNonAsset(value.assetCookie, file, AssetManager.ACCESS_STREAMING);// System.out.println("Opened file " + file + ": " + is);dr = Drawable.createFromResourceStream(this, value, is,file, null);is.close();// System.out.println("Created stream: " + dr);} catch (Exception e) {Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);NotFoundException rnf = new NotFoundException("File " + file + " from drawable resource ID #0x"+ Integer.toHexString(id));rnf.initCause(e);throw rnf;}Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);}}}}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));}}}}}return dr;}
這里的代碼較多,做了一些刪節,只留下重點部分,方面閱讀,可以看到第6行是先從緩存中拿Drawable,然后第20行else才是真正創建Drawable的地方,50行是pull解析xml的地方90行就放入了緩存,其中sPreloadedDrawables和mDrawableCache是LongSparseArray<Drawable.ConstantState>[]該類就是對HashMap的優化類,可以當作HashMap來使用,這樣就不難理解,重新創建Bitmap的時候是同一個實例了,好了,明白了原因。做一個小結吧!
總結:
1.通過XML給控件設置的Drawable最好不要recycle(),除非該Drawable只使用一次。
2.android系統會將使用過的資源(R.Drawable)會放入緩存中,優化下次使用的速度。
3.出現trying to use a recycled bitmap報錯的原因,就是使用的相同的實例,并且之前recycle()過,應該從這里分析具體原因。
總結
以上是生活随笔為你收集整理的Android bitmap.recycle()导致trying to use a recycled bitmap报错分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【HiFlow】新型零代码自动化助手
- 下一篇: MTK6577+Android环境变量