我们先看一段简单的代码,如果让你在SurfaceView上面绘制一些东西,你可能会写出类似的一段代码,让Surface在一个新的线程去绘制内容
class MainActivity : AppCompatActivity() {
private var drawThread: DrawThread? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val surfaceView = findViewById<SurfaceView>(R.id.surface)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
drawThread = DrawThread(holder.surface)
drawThread?.start()
}
override fun surfaceChanged(
holder: SurfaceHolder,
format: Int,
width: Int,
height: Int
) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
drawThread?.interrupt()
}
})
}
class DrawThread(var surface: Surface): Thread() {
override fun run() {
super.run()
while (!isInterrupted) {
val canvas = surface.lockCanvas(null)
val paint = Paint()
paint.textSize = 200f
//示例代码
canvas.drawColor(Color.WHITE)
for (i in 0..1000) {
canvas.drawText("test", 0f, 0f, paint)
}
surface.unlockCanvasAndPost(canvas)
}
}
}
}
当你退出页面的时候,你可能会遇到这样的一个错误
java.lang.IllegalStateException: Surface has already been released.
它报错了,原因很简单,因为Surface已经被销毁了,而你还在使用它,你想既然报错了,那我try-catch就行了,类似下面的代码,如果你觉得这样可以解决你的问题了,你确实可以划走了
try {
val canvas = surface.lockCanvas(null)
val paint = Paint()
paint.textSize = 200f
//示例代码
canvas.drawColor(Color.WHITE)
for (i in 0..1000) {
canvas.drawText("test", 0f, 0f, paint)
}
surface.unlockCanvasAndPost(canvas)
} catch (_: Exception) {
}
那有没有更好的办法呢?我们知道SurfaceView要销毁的时候会通过surfaceDestroyed告诉我们,那这个时候我们停止绘制不就可以了吗?事实上,一个Thread是不能直接stop的,那我们怎么办呢?这里说一下SurfaceView内部调用流程,当surfaceDestroyed产生的时候Surface还没有被销毁,所以我们可以在surfaceDestroyed等待DrawThread执行完成,这样就不会报错了,那如何等待某个Thread执行完成呢?答案是join,注释说明了这方法的作用
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
所以我们只要将代码稍微改造一下即可
class MainActivity : AppCompatActivity() {
private var drawThread: DrawThread? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val surfaceView = findViewById<SurfaceView>(R.id.surface)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
drawThread = DrawThread(holder.surface)
drawThread?.start()
}
override fun surfaceChanged(
holder: SurfaceHolder,
format: Int,
width: Int,
height: Int
) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
drawThread?.interrupt()
drawThread?.join()
}
})
}
class DrawThread(var surface: Surface): Thread() {
override fun run() {
super.run()
while (!isInterrupted) {
val canvas = surface.lockCanvas(null)
val paint = Paint()
paint.textSize = 200f
//示例代码
canvas.drawColor(Color.WHITE)
for (i in 0..1000) {
canvas.drawText("test", 0f, 0f, paint)
}
surface.unlockCanvasAndPost(canvas)
}
}
}
}