定位Canvas: trying to use a recycled bitmap android.graphics.Bitmap@299c9ae7

在工作中碰到这样一个问题,描述为:设置计时器,等待计时器时间到按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();
     }
 }

找到对应的代码注释掉即可。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值