【问题标题】:Canvas trying to use a Recycled Bitmap试图使用回收位图的画布
【发布时间】:2013-11-16 20:57:51
【问题描述】:

自从我将我的 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)

这是我在 AsyncTask 中使用BitMap 的唯一代码;这与我的 Drawer 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));
            }
        }
    }
}

AsyncTask在创建 Activity 时调用。

应要求,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的完整代码吗?
  • @GopalRao 我已经添加了它。此外,在其下方添加了一项更正。
  • userIcon 在哪里?它是否在一个可能被破坏的视图中?
  • @RickFalck userIcon 在导航抽屉中listViewheader 中。查看我的代码,只有在创建 Activity 时才会设置它。
  • 可以使用bitmap.isRecycled() 来检查位图是否被回收。这可能会帮助您确定出了什么问题。

标签: java android bitmap


【解决方案1】:

据我正确理解,此问题仅在 android 尝试渲染设置了位图对象的视图时发生,并且在渲染期间如果 android 发现之前设置的位图对象已被回收,则此异常发生.(API 升级后,Bitmap 因某种奇怪或熟悉的原因被回收!!!)

(考虑到您没有在任何地方调用 bitmap.recycle())
我会尝试解决的方法是:

第一次尝试:

  • 让 doInBackground 返回一个 Bitmap 对象。
  • 从静态 AsyncTask 中移除局部变量(私有位图 b;)
  • 相应地调整 onPostExecute:

    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);
                    }
        }
    }
}

我试图提出的解决方案纯粹是基于假设和我过去的经验,所以不要误会我的意思。如您所见,第二个解决方案根本没有意义,但它可能与视图有关,也可能无关!!!。另一方面,第一个解决方案在位图对象引用方面对我来说确实有点意义。据我所知,在位图处理过程中沿路的某个地方,对象正在被回收,基于这个事实,我尝试了上述两种方法。 希望这对您有所帮助。

【讨论】:

    【解决方案2】:

    我会大胆猜测......

    在 OnCreate 中,您引用 Drawer 的布局组件,使布局管理器。 inflater 不知道您的任务中固有的异步滞后,因此它会在不知道异步任务尚未完成的情况下继续进行。

    问题可能是您在 onCreate() 中构建的视图需要能够在任务完成之前将某些东西放在一起。任务完成后,它需要更改您的视图资源的状态,并表示应该刷新某些视图组件。例如,资源中的一个虚拟 bmp 代表圆形位图,很快就会从网络上的异步检索中出现。

    我不知道为什么您在

    我认为您的问题与方向变化无关。

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我不相信我的代码中有这个。但我确实发布了更多可能的违规代码。
      猜你喜欢
      • 2013-07-12
      • 1970-01-01
      • 2017-10-01
      • 1970-01-01
      • 2015-09-21
      • 2015-12-04
      • 2012-07-25
      • 1970-01-01
      相关资源
      最近更新 更多