线程 2 种基本用法
继承
//定义
class MyThread : Thread() {
override fun run() {
}
}
//启动
MyThread.start()
接口
//定义
class MyThread : Runnable {
override fun run() {
}
}
//启动
var myThread = MyThread()
Thread(myThread).start()
在子线程中更新UI
class MainActivity : AppCompatActivity() {
val updateText = 1
//1、需要在主线程当中创建一个`Handler` 对象,并重写 `handleMessage()` 方法。
val handler = object : Handler(Looper.getMaininLooper()) {
override fun handleMessage(msg: Message) {
// 在这里可以进行UI操作
when (msg.what) {
updateText -> textView.text = "Nice to meet you" }
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
changeTextBtn.setOnClickListener {
thread {
//2、当子线程中需要进行UI操作时,就创建一个 `Message` 对象,
val msg = Message()
msg.what = updateText
//并通过 `Handler` 将这条消息(Message对象)发送出去。
handler.sendMessage(msg)
}
}
}
}
之后,这条消息会被添加到 MessageQueue
的队列中等待被处理。
而,Looper
则会一直尝试从 MessageQueue
中取出待处理消息,最后分发回 Handler
的 handleMessage()
方法中。
由于 Handler
的构造函数中我们传入了 Looper.getMainLooper()
,所以此时 handleMessage()
方法中的代码也会在主线程中运 行,于是我们在这里就可以安心地进行UI操作了。
AsyncTask(抽象类)
/**
子类继承时,需要指定3个泛型参数:
Params:在执行AsyncTask 时需要传入的参数,可用于在后台任务中使用。
Progress:在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
*/
//Kotlin Unit = Java void
class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
//onPreExecute()
//这个方法会在后台任务开始执行之前调用。用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
override fun onPreExecute() {
progressDialog.show() // 显示进度对话框
}
//doInBackground(Params...)
//这个方法中的所有代码都会在`子线程`中运行,我们应该在这里去处理所有的耗时任务。
//任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask 的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。
override fun doInBackground(vararg params: Unit?) = try {
while (true) {
val downloadPercent = doDownload() // doDownload()是一个虚构的方法
//注意,在这个方法中是不可以进行UI 操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用 publishProgress (Progress...) 方法来完成。
publishProgress(downloadPercent)
if (downloadPercent >= 100) {
break
}
}
true
} catch (e: Exception) {
false
}
//onProgressUpdate(Progress...)
//在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
//当在后台任务中调用了publishProgress(Progress...)方法,onProgressUpdate (Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。
override fun onProgressUpdate(vararg values: Int?) {
// 在这里更新下载进度
progressDialog.setMessage("Downloaded ${
values[0]}%")
}
//onPostExecute(Result)
//当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数 据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。
override fun onPostExecute(result: Boolean) {
progressDialog.dismiss()// 关闭进度对话框
// 在这里提示下载结果
if (result) {
Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, " Download failed", Toast.LENGTH_SHORT).show()
}
}
}
简单来说,使用 AsyncTask 的诀窍就是,
在doInBackground()
方法中执行具体的耗时任务,
在onProgressUpdate()
方法中进行UI操作,
在onPostExecute()
方法中执行一些任务的收尾工作。
执行关系,
在doInBackground()
调用publishProgress(Progress...)
,触发onProgressUpdate (Progress...)
doInBackground()
执行完毕返回Result
,执行onPostExecute(Result)
启动任务:
DownloadTask().execute()
//当然,也可以给execute()方法传入任意数量的参数Params...,这些参数将会传递到doInBackground(Params...)方法当中。
只需要调用一下publishProgress()
方法,就可以轻松地从子线程切换到UI线程了。