Search code examples
androidsurfaceview

When should I start drawing on SurfaceView?


I use a SurfaceView to create a marquee feature, but sometimes after the drawing thread in SurfaceView starts running, the UI thread is blocked, my touch on the BACK or MENU button is not dispatched, and an ANR is produced. This happens now and then.

I guess it is because the drawing in SurfaceView starts too early(of course I ensure the drawing happens between surfaceCreated() and surfaceDestroyed()), I guess the drawing thread should starts after something fully initialized, maybe something related to Activity?

When I add Thread.sleep(100) before the code that actually uses Canvas returned by SurfaceHolder.lockCanvas() to start drawing, the problem almost disappears, it still happens, but the frequency is low. If I make the drawing thread sleep longer enough before actually drawing something on the canvas, the problem never occurs again.

It looks like I should start drawing after something is fully initialized, but I have no idea about what that something is.

This SurfaceView is used as a normal View that is put in the layout file, the following is the code used to draw on the surface.

public void run() {
    try {
        // this is extremely crucial, without this line, surfaceView.lockCanvas() may
        // produce ANR from now and then. Looks like the reason is that we can not start
        // drawing on the surface too early
        Thread.sleep(100);
    } catch (Exception e) {}

    while (running) {
        Canvas canvas = null;
        try{

            long ts = System.currentTimeMillis();

            canvas = surfaceHolder.lockCanvas();
            if (canvas != null) {
                synchronized (surfaceHolder) {
                    doDraw(canvas);
                }

                ts = System.currentTimeMillis() - ts;
                if (ts < delayInterval) {
                    Thread.sleep(delayInterval - ts);
                }
            }

        } catch (InterruptedException e) {
            // do nothing
        } finally {
            if (canvas != null)
                surfaceHolder.unlockCanvasAndPost(canvas);
        }

    }
}

Solution

  • You shouldn't call Thread.sleep between SurfaceHolder.lockCanvas and SurfaceHolder.unlockCanvasAndPost, it should be called only after canvas is unlocked.

    In your code example canvas remains locked almost all the time and cause starvation. There is only a little window for SurfaceFlinger to take a canvas a process it. So sometimes this code could fail and that's why ANR errors were sporadic.