3

I have a problem with a drawing application. I have the error below when I leave the drawing activity (with the return button for example). I've looked at the posts but nothing seems to help me. Please, it would mean a lot if you could have a look at my drawingsurface activity, because I've been trying to make it work for a while! Thanks a lot :)

Error :

03-25 13:41:36.760: E/AndroidRuntime(6642): FATAL EXCEPTION: Thread-423
03-25 13:41:36.760: E/AndroidRuntime(6642): java.lang.IllegalArgumentException
03-25 13:41:36.760: E/AndroidRuntime(6642):     at android.view.Surface.nativeUnlockCanvasAndPost(Native Method)
03-25 13:41:36.760: E/AndroidRuntime(6642):     at android.view.Surface.unlockCanvasAndPost(Surface.java:457)
03-25 13:41:36.760: E/AndroidRuntime(6642):     at android.view.SurfaceView$4.unlockCanvasAndPost(SurfaceView.java:812)
03-25 13:41:36.760: E/AndroidRuntime(6642):     at com.ecp.drawing.DrawingSurface$DrawThread.run(DrawingSurface.java:80)

Main code of DrawingSurface :

    public void run() {
        Canvas canvas = null;
        while (_run){
            if(isDrawing == true){
                try{
                    canvas = mSurfaceHolder.lockCanvas(null);
                    if(mBitmap == null){
                        mBitmap =  Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);
                    }
                    final Canvas c = new Canvas (mBitmap);

                    c.drawColor(0, PorterDuff.Mode.CLEAR);
                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);


                    commandManager.executeAll(c,previewDoneHandler);
                    previewPath.draw(c);

                    canvas.drawBitmap (mBitmap, 0,  0,null);
                } finally {
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                }


            }

        }

    }

There is already a surface destroyed function :

public void surfaceDestroyed(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    boolean retry = true;
    thread.setRunning(false);
    while (retry) {
        try {
            thread.join();
            retry = false;
        } catch (InterruptedException e) {
            // we will try it again and again...
        }
    }
}
jww
  • 97,681
  • 90
  • 411
  • 885
user2097069
  • 31
  • 1
  • 3

2 Answers2

2

I would guess that lockCanvas threw an exception, which left canvas set to null. The exception sent execution to the finally clause, which called unlockCanvasAndPost, which threw a new exception. The new exception obscured the old.

(This looks a bit like a bug in 4.3, but this was posted before 4.3 came out.)

fadden
  • 51,356
  • 5
  • 116
  • 166
  • 1
    This might be an app bug. It'd be necessary to see the initial exception to be sure. In any event, if it's pre-4.3, then it's not related to https://code.google.com/p/android/issues/detail?id=58385 . – fadden Sep 16 '13 at 16:54
  • For anyone who cares @fadden is right, and that bug still isn't fixed in the latest update to Android. – Cory Trese Aug 03 '15 at 16:05
2

I had similar issues in 4.3 and after reading a while how other developers bypassed the bug I came to this.

Steps to keep in mind: lockCanvas, draw and then unlockCanvas.

/**
 * Note: The drawing thread doesn't loop, it just runs once and exits
 */
@Override
public void run() {
    /* This should never happen but just to be sure... */
    if (mSurfaceHolder == null || mSurfaceView == null) {
        return;
    }

    /**
     * In order to work reliable on Nexus 7, we place ~500ms delay at the start of drawing thread
     * (AOSP - Issue 58385)
     */
    if (android.os.Build.BRAND.equalsIgnoreCase("google") &&
            android.os.Build.MANUFACTURER.equalsIgnoreCase("asus") &&
            android.os.Build.MODEL.equalsIgnoreCase("Nexus 7")) {

        Log.w(this, "Sleep 500ms (Device: Asus Nexus 7)");

        try {
            Thread.sleep(500);
        } catch (InterruptedException ignored) {
        }
    }

    Canvas canvas = null;

    while (mRunning) {
        try {
            Surface surface = mSurfaceHolder.getSurface();

            /* Check availability of surface */
            if (surface != null && surface.isValid()) {

                canvas = mSurfaceHolder.lockCanvas();

                synchronized (mSurfaceHolder) {
                    if (canvas != null) {

                        //TODO call drawing code
                    }
                }
            }

        } catch (Exception e) {
            Log.e(TAG, "[Drawing Thread]", e);

        } finally {
            /**
             * Do this in a finally so that if an exception is thrown during the above,
             * we don't leave the Surface in an inconsistent state
             */
            if (canvas != null && mSurfaceHolder != null) {
                mSurfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
}

I hope this can help someone. ;)

Ryan Amaral
  • 4,059
  • 1
  • 41
  • 39