Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas trying to use a Recycled Bitmap

Ever since I upgraded my SDK/ADT to the newest changes since Android 4.4, which I am not sure if that is related with this, but it is the only major change since this bug started, I have gotten this error in my app.

The Log doesn't say where in my app this is happening. Though there are references to the DrawerLayout. Also it seems like it was referencing support library v4 but I am using 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)

Here is the only code I have that is using a BitMap, in an AsyncTask; this pertains to an image that is in my Drawer listView header:

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

This AsyncTask is only called when Activity is created.

As Requested, the ImageHelper Class:

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; 
like image 572
TheLettuceMaster Avatar asked Nov 16 '13 20:11

TheLettuceMaster


2 Answers

As far as i understand correctly this problem only occurs when android tries to render a view that has a bitmap object set to it and during rendering if android finds that the bitmap object that was set before has been recycled only then this exception occurs.(Bitmap got recycled for some strange or familiar reason after api upgrade!!!.)

(Considering that you haven't called bitmap.recycle() anywhere)
The way i would try to fix is:

First attempt:

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

Second attempt:

    protected void onPostExecute(Void v) {
    if (mContext != null) {
        if (b != null) {
            Bitmap roundedbmp = ImageHelper.getRoundedCornerBitmap(
                    b, 50);
        }
    }
   }

Check only this to make sure bitmap recycle problem was not originating from ImageHelper.getRoundedCornerBitmap(b, 50) this code.

    protected void onPostExecute(Void v) {

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

The solution i tried to came up with is purely based on assumption and my past experience so don't get me wrong. As you can see the second solution doesn't make sense at all but it might or might not have something to do with the view!!!. On the other hand first solution does make sense a bit to me in terms of Bitmap object referencing.As i can understand somewhere along the road during bitmap handling the object is getting recycled, based on this fact i tried above two approach. Hope this helps you someway.

like image 147
saiful103a Avatar answered Oct 22 '22 13:10

saiful103a


I'll make a wild guess...

In the OnCreate, you reference the layout components of the Drawer in such a way that the layout mgr. inflater is not aware of the async lag inherent in your task so it goes ahead without knowing that the async task is not yet finish.

Issue may be that your view being built in onCreate() needs to be able to put something together BEFORE the task is done. When the task is done, it needs to change state on your View resources and signal that some view component should be refreshed. For example , a dummy bmp in resources that stands in for the rounded bitmap, soon to appear from async retrieval on a network.

I dont know why you would not have the same issue on < 4.4.

I assume your problem is NOT related to orientation changes.

Its possible that this code may further clarify some issues with lag due to asnyc. May not be relevant at all .

like image 33
Robert Rowntree Avatar answered Oct 22 '22 14:10

Robert Rowntree