在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" >    <ImageView        android: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 resources                    if ((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()过,应该从这里分析具体原因。

更多相关文章

  1. android PowerManager wakelock
  2. Android(安卓)解决方案-6.0不提供org.apache.http.*
  3. Android标题栏上添加多个Menu按钮的实例
  4. SQLite抛出错误“未关闭游标及数据库对象”特殊原因一例
  5. Android内容提供器-读取通讯录
  6. [Android实例] android中进行https连接的方式的详解 (转发)
  7. Android桌面小组件:最简例子
  8. 时间和日期选择器DatePicker和TimePicker的使用
  9. Android应用开发SharedPreferences存储数据的使用方法

随机推荐

  1. 安卓基础知识
  2. android休眠与唤醒驱动流程分析
  3. Android中各种onTouch事件
  4. Android中Activity多页滑动切换效果(使用V
  5. 相对布局
  6. android 设置缓存工具类
  7. android 代码混淆之后 微信分享不起作用
  8. compileSdkVersion、buildToolsVersion、
  9. Android使用了ProGuard时注意的事项
  10. Android(安卓)解析IMEI