Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/188.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 同步SurfaceHolder';s锁定画布并解锁CanvasandPost_Java_Android_Multithreading_Surfaceview_Synchronized - Fatal编程技术网

Java 同步SurfaceHolder';s锁定画布并解锁CanvasandPost

Java 同步SurfaceHolder';s锁定画布并解锁CanvasandPost,java,android,multithreading,surfaceview,synchronized,Java,Android,Multithreading,Surfaceview,Synchronized,我知道关于这个话题有很多问题,但是,我仍然对提供的答案不完全满意 情况: 我使用SurfaceHolder在另一个线程中使用drawing实现了SurfaceView,就像《开发人员指南》中建议的那样: 问题: 有时我会得到java.lang.IllegalStateException:Surface已经发布: 调用SurfaceHolder.lockCanvas()时 或者在调用SufraceHolder.unlockCanvasAndPost()时 这意味着有时在锁定画布之前释放曲面,有

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

情况: 我使用SurfaceHolder在另一个线程中使用drawing实现了SurfaceView,就像《开发人员指南》中建议的那样:

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

  • 调用SurfaceHolder.lockCanvas()时
  • 或者在调用SufraceHolder.unlockCanvasAndPost()时
这意味着有时在锁定画布之前释放曲面,有时在锁定和解锁画布之间释放曲面

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

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();
}

synchronized
语句用于多个线程之间的互斥访问。如果在线程#1中执行
synchronized(this)
,则尝试执行相同操作的任何其他线程都将阻塞,直到线程#1退出同步块

如果您只在一个线程中使用它,那么它就没有任何用处。当然,如果您有多个线程试图锁定和解锁曲面的画布,那么您应该首先通过不这样做来解决问题,而不是在事后尝试强制独占访问

使用
interrupt()
isThreadInterrupted()
没有太大意义。如果您想提升一个标志,告诉线程该停止运行了,那么
volatile boolean
就可以了。“
interrupt()
的唯一优势是,如果等待对象发出信号,它将唤醒线程。”。此外,考虑代码< ISCOD> ISCODIDED()<代码>调用:< /P> 返回一个布尔值,指示接收器是否有挂起的中断请求(true)或未挂起的中断请求(false)

(我的重点是。)您的代码允许这样的情况,“如果线程处于活动状态,但已为其引发中断,请继续,在旧线程仍在运行时创建一个新线程”


有关使用SurfaceView的一些信息,请参阅。这链接到Grafika中的一个示例,该示例根据您可以找到的表面回调启动和停止线程。

经过几个小时的尝试和错误,我想我已经找到了答案。毕竟,我所要做的就是阅读android参考资料。以下是一些我弄错的重要事情:

  • SurfaceHolder.getSurface.isValid()
    仅检查曲面是否可以 被锁定,因此该检查可能(并且确实)失败,因为它可以看到画布 可以解锁
  • 要检查是否可以调用
    解锁canvasandpost()
    , 您只需检查
    lockCanvas()
    返回的
    canvas
    是否为空
    null
  • 如果
    画布
    ,则不应解锁它(如果 尝试它将引发异常)
  • 如果它不为null,则必须解锁它,否则如果您的活动试图停止(在
    onPause()
    之后,
    SurfaceHolder
    试图破坏
    表面,但由于
    画布
    被锁定,它将无法冻结,因此您会陷入死锁)
希望这能帮助其他“noobs”。最后,这是我为
SurfaceView
编写的最后一段代码(不再需要try/catch块):


感谢fadden澄清了关于在另一个线程中绘制的一些要点。

我也有同样的问题-在SurfaceView中使用后台线程绘制,有时,当活动关闭时,应用程序挂起。追踪显示,本例中的曲面在
lockCanvas
unlockCanvasAndPost
之间被破坏

这种行为的原因似乎是我对文档有点误解<代码>回调。必须使用surfaceDestroyed
来防止曲面被破坏(通过不从中返回),直到后台线程使用surface完成

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();
}

在单个同步语句中放置Lock、Draw和Unlock修复了异常

我是在读了这篇文章后才知道同步语句的,如果这是个坏主意,请纠正我

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

      }
   }
}

谢谢你的回答。我想我明白你的意思。我将改变创建/销毁线程的方式,但我想重新表述我的主要问题:在我调用
holder.getSurface().isValid()
holder.lockCanvas()
之间,是否有可能在我的绘图线程中曲面被销毁,我如何避免它?曲面完全有可能消失,当你在一个不同的线程中运行,并且没有告诉系统你正在使用它。你不能回避它。检查
lockCanvas()
中的返回值,如果返回值为null,则不要尝试绘制。一旦你调用了
lockCanvas()
,系统会保证曲面在你解锁之前不会消失。这就是我的想法,也是我试图做的,但有时我在尝试
lockCanvas()
时会得到这些
IllegalStateException
(曲面已被释放)。即使画布不为null,也不意味着在调用unlockCanvasAndPost()时曲面必须存在。如果系统只是忽略了我在曲面被破坏时锁定/解锁曲面的调用,我会非常满意,但这些异常会把一切都搞砸。一个曲面有多个渲染器线程会导致不愉快。同样,最好的修复方法是防止原位断裂
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

      }
   }
}