我知道有很多关于这个主题的问题,但是,我仍然不完全满意所提供的答案。
情况: 我使用SurfaceHolder在另一个线程中实现了SurfaceView,就像开发人员指南中提到的那样:http://developer.android.com/guide/topics/graphics/2d-graphics.html
问题: 有时候我得到java.lang.IllegalStateException:Surface已经发布了:
这意味着我的表面有时会在我锁定画布之前释放,有时候 - 在锁定和解锁之间。
我的解决方案: 我在同步块中执行表面检查和锁定/解锁画布,因此我确信表面不会在这些动作之间被破坏。但是,我从未使用过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();
}
答案 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
。canvas
为null
,则不应解锁(如果您
试试它会引发异常)。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中绘图,有时,当活动关闭时,应用程序挂起。追踪显示,此案例中的表面在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();
}
答案 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
}
}
}