Canvas: trying to use a recycled bitmap android.graphics 经验

Quibbler 2020-6-9 2632

Canvas: trying to use a recycled bitmap android.graphics


        又又遇到祖传问题,网上一搜6年前都有这个问题,看来从历史吸取经验教训是最快的成长!



1、异常捕获

        复现很容易:写一个Bitmap,调用Bitmaprecycle()方法将其回收掉,再设置给ImageView

    mBitmap.recycle();
    mImageView.setImageBitmap(mBitmap);

        捕获到下面的异常:

Process: com.easyicon.learnglide, PID: 8337

    java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@4337e91

        at android.graphics.Canvas.throwIfCannotDraw(Canvas.java:1270)

        at android.graphics.Canvas.drawBitmap(Canvas.java:1404)

        at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:544)

        at android.widget.ImageView.onDraw(ImageView.java:1246)

        at android.view.View.draw(View.java:16214)

        at android.view.View.updateDisplayListIfDirty(View.java:15211)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3605)

        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3585)

        at android.view.View.updateDisplayListIfDirty(View.java:15171)

        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:296)

        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:302)

        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:337)

        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2883)

        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2699)

        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2328)

        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1340)

        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6616)

        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)

        at android.view.Choreographer.doCallbacks(Choreographer.java:670)

        at android.view.Choreographer.doFrame(Choreographer.java:606)

        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)

        at android.os.Handler.handleCallback(Handler.java:739)

        at android.os.Handler.dispatchMessage(Handler.java:95)

        at android.os.Looper.loop(Looper.java:148)

        at android.app.ActivityThread.main(ActivityThread.java:5554)

        at java.lang.reflect.Method.invoke(Native Method)

        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:935)

        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:726)



2、分析

        异常堆栈一大长串,前面的是ActivityThread主线程Looper处理事件,通过Handler分发。接着是Choreographer图像绘制。直接看重点ImageView的onDraw()方法:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
            ...
            mDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        }
    }

        接着调用Drawabledraw(Canvas )方法,Drawable是个抽象类,BitmapDrawable继承自Drawable(还有很多其它类也继承自Drawable),实现draw(Canvas canvas)方法:

    @Override
    public void draw(Canvas canvas) {
        final Bitmap bitmap = mBitmapState.mBitmap;
        if (bitmap == null) {
            return;
        }
        ...
            canvas.drawBitmap(bitmap, null, mDstRect, paint);
        ...
    }

        接着调用Canvas类的drawBitmap()方法,Canvas继承自BaseCanvas类,该方法定义在BaseCanvas类中:

public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
            @Nullable Paint paint) {
        if (dst == null) {
            throw new NullPointerException();
        }
        throwIfCannotDraw(bitmap);
        throwIfHasHwBitmapInSwMode(paint);
        final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
        ...
    }

        可以看见在绘制Bitmap方法中,调用了throwIfCannotDraw(Bitmap bitmap)方法:

    protected void throwIfCannotDraw(Bitmap bitmap) {
        if (bitmap.isRecycled()) {
            throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap);
        }
        if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 &&
                bitmap.hasAlpha()) {
            throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap "
                    + bitmap);
        }
        throwIfHwBitmapInSwMode(bitmap);
    }

        如果Bitmap已经被回收isRecycled()就抛出异常:RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap)



3、处理

        无非就是异常捕获,另外一个就是严谨的时序逻辑(安卓中很难!)


3.1、继承ImageView捕获

        既然异常抛出在ImageViewonDraw()阶段,那么就在ImageViewonDraw()方法中捕获它:

    private class SafeImageView extends ImageView {
        public SafeImageView(Context context) {
            super(context);
        }
        public SafeImageView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
        public SafeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
        public SafeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
        
        //捕获可能的异常
        @Override
        protected void onDraw(Canvas canvas) {
            try {
                super.onDraw(canvas);
            } catch (Exception e) {
                Log.e(TAG, e.toString());
            }
        }
    }


3.2、使用时严谨的判断

        好在Bitmap提供了isRecycled()方法,使用Bitmap的时候判断是否回收了。

    if (null != mBitmap && !mBitmap.isRecycled()) {
        mImageView.setImageBitmap(mBitmap);
    }



参考资料:

        Canvas: trying to use a recycled bitmap android.graphics.Bitmap@XXX

        Android手动回收bitmap,引发Canvas: trying to use a recycled bitmap处理

        trying to use a recycled bitmap android.graphics.Bitmap@af903b

        Bitmap回收—Canvas: trying to use a recycled bitmap android.graphics

        关于Glide出现trying to use a recycled bitmap错误的问题分析


不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com