Synchronizing SurfaceHolder lockCanvas and unlockCanvasAndPost

I know that there are many questions on this topic, however, I am still not completely satisfied with the answers provided.

Situation: I applied SurfaceView with drawing in another thread using SurfaceHolder as suggested in the developer's guide: http://developer.android.com/guide/topics/graphics/2d-graphics.html

Problem: Sometimes I get java.lang.IllegalStateException: Surface is already thrown:

  • when calling SurfaceHolder.lockCanvas ()
  • or when calling SufraceHolder.unlockCanvasAndPost ()

This means that my surface is sometimes freed before I lock the canvas, and sometimes between locking and unlocking.

My solution: I am performing a surface check and locking / unlocking the canvas in synchronized blocks, so I know for sure that the surface cannot be destroyed between these actions. However, I never worked with synchronized blocks, and I wanted to ask if there was something wrong. So far, the code has worked fine, but you never know when synchronization problems can occur, so I'm not quite sure if this is the best way.

private class DrawingThread extends Thread{
    @Override
    public void run() {
        super.run();
        while (!isInterrupted() && holder != null) {
            Canvas drawingCanvas = null;
            synchronized (this) {
                if (holder.getSurface().isValid()) {
                    drawingCanvas = holder.lockCanvas();
                }
            }
            if (drawingCanvas != null && drawingCanvas.getWidth() > 0) {
                drawThisView(drawingCanvas);
                synchronized (this) {
                    if(holder.getSurface().isValid()) {
                        holder.unlockCanvasAndPost(drawingCanvas);
                    }
                }
            }
        }
    }
}


@Override
public void surfaceCreated(SurfaceHolder holder) {
    if(drawingThread != null){
        drawingThread.interrupt();
    }
    drawingThread = new DrawingThread();
    drawingThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    if(drawingThread.isInterrupted()){
        drawingThread = new DrawingThread();
        drawingThread.start();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    drawingThread.interrupt();
}
+4
source share
4 answers

, , . , , , Android. , :

  • SurfaceHolder.getSurface.isValid() , , ( ) - .
  • , unlockCanvasAndPost(), , canvas, lockCanvas() null.
  • canvas - null, ( ).
  • null, , , ​​ ( onPause() SurfaceHolder Surface, canvas , , ).

, "". , SurfaceView ( try/catch ):

DrawingThread drawingThread;

private class DrawingThread extends Thread{
    public volatile boolean canDraw = true;

    @Override
    public void run() {
        try {
            while (canDraw) {
                Canvas drawingCanvas = null;
                if (canDraw && holder.getSurface().isValid()) {
                    drawingCanvas = holder.lockCanvas();
                    if (drawingCanvas != null) {
                        drawThisView(drawingCanvas);
                        holder.unlockCanvasAndPost(drawingCanvas);
                    }
                }
            }
        }catch(IllegalStateException e){
            e.printStackTrace();
            canDraw = false;
        }
    }
}


@Override
public void surfaceCreated(SurfaceHolder holder) {
    if(drawingThread != null){
        drawingThread.canDraw = false;
    }
    drawingThread = new DrawingThread();
    drawingThread.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    if(!drawingThread.canDraw){
        drawingThread = new DrawingThread();
        drawingThread.start();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if(drawingThread != null) {
        drawingThread.canDraw = false;
    }
}

fadden .

+2

synchronized . synchronized (this) # 1, , , , # 1 .

, . , , Surface Canvas, , , , .

interrupt() isThreadInterrupted() . , , , volatile boolean . interrupt() , , . , isInterrupted():

, , (true) (false)

(Emphasis mine.) , " , , , ".

SurfaceView . Grafika, Surface, .

+2

- SurfaceView , , , . , lockCanvas unlockCanvasAndPost.

, , . Callback.surfaceDestroyed ( ) , .

DrawingThread drawingThread;
ReentrantLock paintLock = new ReentrantLock();

private class DrawingThread extends Thread{
    public volatile boolean canDraw = true;

    @Override
    public void run() {
        try {
            while (canDraw) {
                Canvas drawingCanvas = null;
                paintLock.lock();
                if (canDraw)) {
                    drawingCanvas = holder.lockCanvas();
                    if (drawingCanvas != null && drawingCanvas.getWidth() > 0) {
                        drawThisView(drawingCanvas);
                        holder.unlockCanvasAndPost(drawingCanvas);
                    }
                }
                paintLock.unlock();
            }
        }catch(IllegalStateException e){
            e.printStackTrace();
            canDraw = false;
        }
    }
}

...

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    paintLock.lock();
    if(drawingThread != null) {
        drawingThread.canDraw = false;
    }
    paintLock.unlock();
}
0
source

Placing locks, draws, and unlocks within a single synchronized statement caught an exception.

I found out about the synchronized statement after reading this thread, correct me if this is a bad idea.

private class DrawingThread extends Thread{
@Override
public void run() {
    super.run();
    while (!isInterrupted() && holder != null) {
        Canvas drawingCanvas = null;
        synchronized (this) {
            if (holder.getSurface().isValid()) {
                drawingCanvas = holder.lockCanvas();
            }

            if (drawingCanvas != null && drawingCanvas.getWidth() > 0) {
                drawThisView(drawingCanvas);
            }

            if(holder.getSurface().isValid()) {
                 holder.unlockCanvasAndPost(drawingCanvas);
            }
        } // Lock -> Draw -> Unlock in a single synchronised statement

      }
   }
}
0
source

Source: https://habr.com/ru/post/1613269/


All Articles