Home » Android » java – lockCanvas() returns different canvases

java – lockCanvas() returns different canvases

Posted by: admin May 14, 2020 Leave a comment

Questions:

Trying to draw something every ms on single canvas. I mean only adding details to canvas, not redrawing it all every frame. So this code gives me three different canvases. Third, then first again. Why?

public void run() {
    this.run = true;
    Canvas canvas = null;
    while (run) {
        try {
            canvas = this.surfaceHolder.lockCanvas();
            synchronized (this.surfaceHolder) {
                Thread.sleep(delay);
                draw(new Img(canvas, size));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (canvas != null) {
            this.surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
        synchronized (this) {
            if (wait) {
                try {
                    wait();
                } catch (Exception e) {}
            }
        }
    }
}

If it is android triple buffering – how to turn it off, or do something with it?
Android 4.2.1

How to&Answers:
  1. I would recommend against naming a boolean field, “run” in a Runnable implementation (in which the method returns void). Even if problems don’t surface from the conflict, it’s confusing. Maybe “running”, or something (anything), would make more sense – easier to debug.

  2. Don’t use Object.wait when you are multi-threading. It won’t always (generally, will not) act as you might expect.

  3. You are most likely getting multiple instances of your Canvas member because somewhere (maybe in the Android framework, or maybe in your code… hard to tell), “new Canvas(args)” is being called while what you believe to be your only Canvas instance is out on another thread. While you have only one reference, more than one instance can be created.

  4. I wouldn’t recommend using synchronized(whatever) unless you are sure you need to do so.

  5. Hang in there. This problem is very confusing – I worked through it last Spring and it wasn’t easy or fun.

Hope any of the above helps in some way.

-Brandon

Answer:

If you like to preserve your previous draw you should draw them in an off-screen canvas and draw them to the canvas you got from lock canvas.

The puesd-code to illustrate the idea:

Bitmap offScreenBitmap = Bitmap.createBitmap(100,200,Bitmap.ARGB_8888);
Canvas offScreenCanvas = new Canvas(offScreenBitmap);

onScreenCanvas = this.surfaceHolder.lockCanvas();

//always draw to te offScreenCanvas
offScreenCanvas.drawXxxx

//copy the data to on-screen canvas you got from the lock
onScreenCanvas.drawBitmap(offScreenBitmap);

unlockAndPost(onScreenCanvas)

That should get your task done. Right?

Then, a little bit under the hood stuff:

Yes, android view (surface IS A view) has multiply buffers: one is used by apps for drawing and one is used by system for rendering and sometimes there is a third one when if the app can not finish drawing timely. No way to turn it off and you won’t want to. And it is the reason you get different canvas when lock as you have already observed.