在工作中碰到这样一个问题,描述为:设置计时器,等待计时器时间到按Home退出,重启手机后,解锁时SystemUI报错。(实际和计时器毫无关系,插入USB重启后就会报错)
查看LOG如下:
AndroidRuntime: Shutting down VM
AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.android.systemui, PID: 1224
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@299c9ae7
at android.graphics.Canvas.throwIfCannotDraw(Canvas.java:1282)
at android.view.GLES20Canvas.drawBitmap(GLES20Canvas.java:599)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:538)
at android.view.View.getDrawableRenderNode(View.java:15766)
at android.view.View.drawBackground(View.java:15712)
at android.view.View.draw(View.java:15479)
at android.widget.FrameLayout.draw(FrameLayout.java:658)
at android.view.View.updateDisplayListIfDirty(View.java:14384)
at android.view.View.getDisplayList(View.java:14413)
at android.view.View.draw(View.java:15204)
at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
at android.view.View.draw(View.java:15507)
at android.widget.FrameLayout.draw(FrameLayout.java:658)
at android.view.View.updateDisplayListIfDirty(View.java:14384)
at android.view.View.getDisplayList(View.java:14413)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2939)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2753)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1292)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6598)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:572)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5666)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
以上LOG中仅仅能够看到是使用了已经回收的图片,但是图片在哪使用,在哪回收无法从LOG中获取,而且现象是偶现。即使再次复现也是同样的LOG,无法追究到根本原因。网上也有很多关于类似问题的帖子,大多数都是自己开发的应用,清楚的知道手动调用recycle()的位置,或者去除或者采用其他方式。针对这个问题,问题就在于不知到系统哪里回收了图片。
1. 经过不断的尝试排除,最终基本找到了问题的必现路径。
2. 咨询其他员工,添加打印栈的信息。
在frameworks/base/graphics/java/android/graphics/Bitmap.java中
public void recycle() {
/**增加以下LOG,打印出被回收图片的hash值*/
Log.d("BitmapTAG", "Object tried to call recycle,this="+this));
/**增加以下方法,打印栈信息*/
Thread.dumpStack();
if (!mRecycled && mFinalizer.mNativeBitmap != 0) {
if (nativeRecycle(mFinalizer.mNativeBitmap)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
}
}
因为问题必现,所以很容易抓取加入信息后的报错LOG。
通过过滤发现,被回收的LOG有很多,如下:
BitmapTAG: Object tried to call recycle,this=android.graphics.Bitmap@30b0ce56
BitmapTAG: Object tried to call recycle,this=android.graphics.Bitmap@299c9ae7
BitmapTAG: Object tried to call recycle,this=android.graphics.Bitmap@2b8b32ad
BitmapTAG: Object tried to call recycle,this=android.graphics.Bitmap@2e708e18
.......
还有很多,不过只需要@299c9ae7这一个就好。
过滤tag为BitmapTAG|System.err的LOG发现了 299c9ae7
D/BitmapTAG(1224): Object tried to call recycle,this=android.graphics.Bitmap@299c9ae7
W/System.err(1224): java.lang.Throwable: stack dump
W/System.err(1224): at java.lang.Thread.dumpStack(Thread.java:490)
W/System.err(1224): at android.graphics.Bitmap.recycle(Bitmap.java:309)
W/System.err(1224): at com.android.systemui.ImageWallpaper$DrawableEngine.trimMemory(ImageWallpaper.java:165)
W/System.err(1224): at com.android.systemui.ImageWallpaper.onTrimMemory(ImageWallpaper.java:94)
W/System.err(1224): at android.app.ActivityThread.handleTrimMemory(ActivityThread.java:4573)
W/System.err(1224): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1666)
W/System.err(1224): at android.os.Handler.dispatchMessage(Handler.java:111)
W/System.err(1224): at android.os.Looper.loop(Looper.java:194)
W/System.err(1224): at android.app.ActivityThread.main(ActivityThread.java:5666)
W/System.err(1224): at java.lang.reflect.Method.invoke(Native Method)
W/System.err(1224): at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err(1224): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
W/System.err(1224): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
可以看出是ImageWallpaper.java:的94行调用了recycle()
public void trimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
mBackground != null) {
if (DEBUG) {
Log.d(TAG, "trimMemory");
}
mBackground.recycle();
mBackground = null;
mBackgroundWidth = -1;
mBackgroundHeight = -1;
mWallpaperManager.forgetLoadedWallpaper();
}
}
找到对应的代码注释掉即可。