SurfaceHolder的lockCanvas和unlockCanvasAndPost的同步

时间:2015-10-26 22:30:07

标签: java android multithreading surfaceview synchronized

我知道有很多关于这个主题的问题,但是,我仍然不完全满意所提供的答案。

情况: 我使用SurfaceHolder在另一个线程中实现了SurfaceView,就像开发人员指南中提到的那样:http://developer.android.com/guide/topics/graphics/2d-graphics.html

问题: 有时候我得到java.lang.IllegalStateException:Surface已经发布了:

  • 调用SurfaceHolder.lockCanvas()
  • 或调用SufraceHolder.unlockCanvasAndPost()

这意味着我的表面有时会在我锁定画布之前释放,有时候 - 在锁定和解锁之间。

我的解决方案: 我在同步块中执行表面检查和锁定/解锁画布,因此我确信表面不会在这些动作之间被破坏。但是,我从未使用过synchronized块,我想问一下它是否有问题。到目前为止,代码运行良好,但你永远不知道同步问题何时可能会出现,所以我并不完全相信这是最好的方法。

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 个答案:

答案 0 :(得分:2)

synchronized语句用于多个线程之间的互斥访问。如果你在线程#1中synchronized (this),那么试图做同样事情的任何其他线程都会阻塞,直到线程#1退出同步块。

如果你只在一个线程中使用它,它没有任何用处。当然,如果你有多个线程试图锁定和解锁Surface的Canvas,你应该首先解决这个问题,而不是在事后强制执行独占访问。

您对interrupt()isThreadInterrupted()的使用并没有多大意义。如果你想举一个告诉线程有时间停止运行的标志,volatile boolean就可以了。 interrupt()的唯一优点是,如果它正在等待一个对象发出信号,它将唤醒该线程。此外,请考虑isInterrupted()调用的文档:

  

返回一个布尔值,指示接收者是否有待处理中断请求(true)或不是(false)

(强调我的。)你的代码允许类似的情况,"如果线程处于活动状态,但是已经为它提出了一个中断,继续创建一个新线程,而旧线程仍在运行&#34 ;

有关使用SurfaceView的一些信息,请参阅this appendix。这链接到Grafika中基于Surface回调启动和停止线程的示例,您可以找到here

答案 1 :(得分:2)

经过几个小时的试验和错误后,我想我已经弄明白了。毕竟,我所要做的就是阅读android引用。以下是我遇到的一些重要事情:

  • SurfaceHolder.getSurface.isValid()仅检查表面是否可以 被锁定,所以这个检查可以(并且确实)看不到画布 可以解锁。
  • 检查您是否可以致电unlockCanvasAndPost(), 您只需检查canvas返回的lockCanvas()是否不是 null
  • 如果canvasnull,则不应解锁(如果您 试试它会引发异常)。
  • 如果它不为空,则必须将其解锁,否则如果您的活动试图停止(onPause() SurfaceHolder Surface试图销毁Canvas,您的应用就会冻结},但由于SurfaceView已被锁定,因此无法解决问题。

希望这可以帮助其他" noobs"。最后,这是我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; } } 的最终代码(不再需要try / catch块):

.htaccess

感谢fadden澄清了关于在另一个帖子中画画的一些观点。

答案 2 :(得分:0)

我有同样的问题 - 使用后台线程在SurfaceView中绘图,有时,当活动关闭时,应用程序挂起。追踪显示,此案例中的表面在lockCanvasunlockCanvasAndPost之间被破坏。

这种行为的原因似乎是我误解了一些文档。必须使用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();
}

答案 3 :(得分:0)

在单个synchronized语句中放置Lock,Draw和Unlock修复了Exception。

在读完这个帖子后我开始了解同步语句,如果这是一个坏主意,请纠正我。

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

      }
   }
}