android对于主线程的响应时间限制的非常严格,稍有不慎就会遇到Application Not Responding(ANR)的弹框。用户可以轻点手指关掉你的APP。官方文档写的非常明确!同时,保持应用随时响应用户的操作也是良好用户体验的前提。
线程的开始和结束
要做到以上多线程是必不可少的。课本会告诉你什么时候开辟一个线程,但是很少说的一个很重要的问题是结束。比如,我现在在Activity里有一个工作需要创建一个线程执行,但是这个Activity在进入后台后不幸遇到系统回收资源被销毁了。但是这个线程还在漫无目的的游走,耗费资源。
如何结束?先创建一个:
mThread = Thread(Runnable {
// do something here...
})
mThread?.start()
以上使用kotlin的lambda表达式简写了创建Runnable
对象部分的代码。主旨还是创建了一个Runnable
对象,并将其作为参数传入Thread
。
如何让一个Thread
能够退出呢?这就要在Runnable
身上下功夫了。首先添加一个是否停止的标识isCancelled
,一旦值为true则停止线程的运行,否则继续。我们这里不讨论Thread#interrupt()
这个方法,这个方法诡异的地方太多。
首先要给Runnable
“添加一个属性”作为上文的是否停止的标识。直接添加时不可能的,Runnable
只是一个interface,不是class。所以要实现这个借口为一个抽象类,这样就可以添加属性了。
abstract class CancelableRunnable() : Runnable {
var isCancelled: Boolean = false
}
这里使用抽象类,是因为run()
方法的实现留给使用的时候给出。
var runnable = object : CancelableRunnable() {
override fun run() {
if (isCancelled) {
var msg = mHandler.obtainMessage(THREAD_CANCELLED)
mHandler.sendMessage(msg)
return
}
Thread.sleep(2000)
if (isCancelled) {
var msg = mHandler.obtainMessage(THREAD_CANCELLED)
mHandler.sendMessage(msg)
return
}
var msg = mHandler.obtainMessage(THREAD_FINISHED)
mHandler.sendMessage(msg)
}
}
Thread.sleep(2000)
用来模拟一个费时的任务。开始之前检测是否取消了线程的执行,执行之后在检测。之后的检测是有的时候任务执行之后需要有持久化处理结果或者修改任务完成情况的标识之类的动作,如果已经取消了线程的执行,即使任务执行完成也不持久化结果、不修改完成情况。
最后都检测完成之后如果没有取消线程,则发出任务完成执行的消息。
发出和处理这些消息的Handler
的定义:
var mHandler = object : Handler() {
override fun handleMessage(msg: Message?) {
when (msg?.what) {
THREAD_CANCELLED -> {
mResultTextView.text = "thread cancelled"
}
THREAD_FINISHED -> {
mResultTextView.text = "thread finished"
}
else -> {
mResultTextView.text = "what's going on"
}
}
}
}
运行在UI线程的Handler
检测从线程发出的消息,如果是THREAD_CANCELLED
那就是线程已经取消了,如果是THREAD_FINISHED
那就是线程完全运行结束。之后根据message的消息设置TextView
的文本内容。
这里使用了两个按钮来启动和停止线程:
findViewById(R.id.start_button)?.setOnClickListener { v ->
runnable.isCancelled = false
mThread = Thread(runnable)
mThread?.start()
mResultTextView.text = "Thread running..."
}
findViewById(R.id.stop_button)?.setOnClickListener { v ->
this.runnable.isCancelled = true
}
上面用到的Runnable
是只做一件事的,如果是连续不断的循环很多事的话也可以使用white语句来控制是否一直执行线程的工作。一旦设置为停止线程,则停止线程任务的循环跳出Runnable#run()
方法,结束线程。
完整代码放在附录中。
所以,如果你在Activity里开辟了一个线程,在Activity被回收的时候结束线程就可以这么做:
override fun onDestroy() {
super.onDestroy()
this.runnable.isCancelled = true
}
这样就再也不用担心Activity挂了,线程还阴魂不散了。
AsyncTask
既然缘起AsyncTask
那就肯定需要读者一起了解一下相关的概念。
比起来使用Handler
+Thread
+Runnable
的多线程异步执行模式来说,使用AsyncTask
是简单了非常的多的。
先简单了解一下AsyncTask
。
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask
是一个抽象泛型类。三个类型Params,Progress,Result分别对应的是输入参数的类型,精度更新使用的类型,最后是返回结果的类型。其中任何一个类型如果你不需要的话,可以使用java.la