Canvas尝试使用已回收的位图

6
自从我升级了SDK/ADT到最新的Android 4.4更改以来,我就一直在我的应用程序中遇到这个错误。我不确定这是否与此相关,但这是自此错误开始以来唯一的重大更改。日志没有说出这是在我的应用程序中的哪个位置发生的。尽管有关于DrawerLayout的引用。而且它似乎在引用支持库v4,但我正在使用v13。
   11-16 15:45:12.406: E/AndroidRuntime(1236): FATAL EXCEPTION: main
    11-16 15:45:12.406: E/AndroidRuntime(1236): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@41f1e4e8
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.graphics.Canvas.throwIfRecycled(Canvas.java:1058)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.graphics.Canvas.drawBitmap(Canvas.java:1097)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.View.draw(View.java:13854)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.drawChild(ViewGroup.java:3086)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.support.v4.widget.DrawerLayout.drawChild(DrawerLayout.java:804)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2923)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.View.draw(View.java:13823)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.drawChild(ViewGroup.java:3086)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2923)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.View.draw(View.java:13823)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.drawChild(ViewGroup.java:3086)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2923)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.View.draw(View.java:13823)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.drawChild(ViewGroup.java:3086)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2923)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.View.draw(View.java:13947)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.widget.FrameLayout.draw(FrameLayout.java:467)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2224)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2482)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2395)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2239)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1004)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5481)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.Choreographer.doCallbacks(Choreographer.java:562)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.Choreographer.doFrame(Choreographer.java:532)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.os.Handler.handleCallback(Handler.java:730)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.os.Handler.dispatchMessage(Handler.java:92)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.os.Looper.loop(Looper.java:137)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at android.app.ActivityThread.main(ActivityThread.java:5103)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at java.lang.reflect.Method.invokeNative(Native Method)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at java.lang.reflect.Method.invoke(Method.java:525)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    11-16 15:45:12.406: E/AndroidRuntime(1236):     at dalvik.system.NativeStart.main(Native Method)

这是我唯一使用BitMap的代码,在一个异步任务中;这与我的抽屉listView标题中的图像有关:

 static class GetProfileImageURL extends AsyncTask<String, String, Void> {
    private Bitmap b;
    private Context mContext;

    public GetProfileImageURL(Context c) {
        mContext = c;
    }

    @Override
    protected Void doInBackground(String... params) {

        URL url;
        try {
            String profImage = String.valueOf(Rateit.userId);
            url = new URL(Rateit.PROFILE_PIC_URL + profImage + ".jpg");
            b = BitmapFactory.decodeStream(url.openConnection()
                    .getInputStream());

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }

    protected void onPostExecute(Void v) {

        if (mContext != null) {
            if (b != null) {
                userIcon.setImageBitmap(ImageHelper.getRoundedCornerBitmap(
                        b, 50));
            }
        }
    }
}

只有在 Activity 创建时才会调用此 AsyncTask

根据请求,这是 ImageHelper 类:

public class ImageHelper { 
    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) { 
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap 
                .getHeight(), Config.ARGB_8888); 
        Canvas canvas = new Canvas(output); 

        final int color = 0xff424242; 
        final Paint paint = new Paint(); 
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
        final RectF rectF = new RectF(rect); 
        final float roundPx = pixels; 
        final Rect topRightRect = new Rect(bitmap.getWidth()/2, 0, bitmap.getWidth(), bitmap.getHeight()/2); 
        final Rect bottomRect = new Rect(0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()); 

        paint.setAntiAlias(true); 
        canvas.drawARGB(0, 0, 0, 0); 
        paint.setColor(color); 
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 
        // Fill in upper right corner 
        canvas.drawRect(topRightRect, paint); 
        // Fill in bottom corners 
        canvas.drawRect(bottomRect, paint); 

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
        canvas.drawBitmap(bitmap, rect, rect, paint); 

        return output; 

你能发布AsyncTask的完整代码吗? - Gopal Gopi
@GopalRao 我已经添加了它。另外,我在它下面添加了一个更正。 - TheLettuceMaster
userIcon在哪里?它是否在可能被销毁的视图中? - Rick Falck
@RickFalck userIcon 在导航抽屉的 listViewheader 中。看了我的代码,它只在 Activity 创建时设置。 - TheLettuceMaster
1
你可以使用bitmap.isRecycled()来检查位图是否已被回收。这可能有助于确定出现了什么问题。 - MungoRae
显示剩余3条评论
3个回答

3
据我正确理解,此问题仅在Android尝试呈现具有位图对象的视图时发生,并且在呈现期间,如果Android发现之前设置的位图对象已被回收,则会出现此异常。(在API升级后,位图由于某种奇怪或熟悉的原因被回收!)。
(考虑您没有在任何地方调用bitmap.recycle())
我尝试修复的方法是:
第一次尝试:
  • Let doInBackground return a Bitmap object.
  • Remove local variable(private Bitmap b;) from static AsyncTask
  • Adjust onPostExecute accordingly:

    protected void onPostExecute(Bitmap bmp) {
      if (mContext != null) {
          if (bmp != null) {
              userIcon.setImageBitmap(ImageHelper.getRoundedCornerBitmap(
                    bmp, 50));
          }
      }
    }
    
第二次尝试:
    protected void onPostExecute(Void v) {
    if (mContext != null) {
        if (b != null) {
            Bitmap roundedbmp = ImageHelper.getRoundedCornerBitmap(
                    b, 50);
        }
    }
   }

仅勾选此项以确保位图回收问题不是由 ImageHelper.getRoundedCornerBitmap(b, 50) 这段代码引起的。
    protected void onPostExecute(Void v) {

    if (mContext != null) {
        if (b != null) {
            Bitmap roundedbmp = ImageHelper.getRoundedCornerBitmap(
                    b, 50);
                    if(roundedbmp!=null){
                        userIcon.setImageBitmap(roundedbmp);
                    }
        }
    }
}

我尝试提出的解决方案完全基于假设和过去的经验,因此请不要误解。正如您所看到的,第二种解决方案根本没有意义,但它可能与视图有关!另一方面,第一种解决方案在位图对象引用方面对我来说有点意义。据我理解,在处理位图时,该对象在某个地方被回收,基于这个事实,我尝试了上述两种方法。 希望对您有所帮助。

1

检查是否在任何地方使用了bitmap_image.recycle()?然后将其注释掉并检查是否有效。我遇到过类似的问题,这样解决了。


我不认为我的代码中有这个问题。但是我已经发布了更多可能引起问题的代码。 - TheLettuceMaster

1

我猜测一下...

在OnCreate中,您以一种方式引用了抽屉的布局组件,使得布局管理器不知道您的任务中存在异步延迟,因此它会继续执行而不知道异步任务还没有完成。

问题可能是您在onCreate()中构建的视图需要在任务完成之前就能够组合好某些内容。当任务完成时,它需要更改您的View资源的状态并发出信号,表示应该刷新某个视图组件。例如,在网络上进行异步检索后即将出现的圆形位图的虚拟BMP。

我不知道为什么您在小于4.4的版本上没有同样的问题。

我假设您的问题与方向变化无关。

可能this code可以进一步澄清由于asnyc而导致的延迟问题。可能根本不相关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接