重点: 和IOS一样,android也是不允许在子线程中更新UI的。
19.1 异步执行任务的方式
- 继承Thread
// 声明
class MyThread: Thread() {
override fun run() {
super.run()
for (i in 1..100) {
Log.d("Thread", "$i")
}
}
}
// 调用
MyThread().start()
- 实现Runnable接口
// 声明
class CustomThread: Runnable {
override fun run() {
for (i in 1..100) {
Log.d("Thread", "$i")
}
}
}
// 调用
val customThread = CustomThread()
Thread(customThread).start()
- Lambda方式。 更方便
Thread {
for (i in 1..100) {
Log.d("Thread", "$i")
}
}.start()
- thread。kotlin中提供的更简单的方式thred闭包。超级方便
thread {
for (i in 1..100) {
Log.d("Thread", "$i")
}
}
19.2 子线程中更新UI
和IOS一样,android也是不允许在子线程中更新UI的。
可以使用Message发送消息到主线程中,来更新UI
class MainActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
val updateText = 1
val handler = object: Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
updateText -> {
viewBinding.textView.text = "My god!"
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
viewBinding.changeTextBtn.setOnClickListener {
thread {
val msg = Message()
msg.what = updateText
handler.sendMessage(msg)
}
}
}
}
19.3 解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。其中Message和Handler在上一小节中我们已经接触过了,而MessageQueue和Looper对于你来说还是全新的概念,下面我就对这4个部分进行一下简要的介绍。
- Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。上一小节中我们使用到了Message的what字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
- Handler
Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法、post()方法等,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。
- MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
- Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
19.4 使用AsyncTask-已弃用,可使用协程替代
Android提供的更加方便在子线程中对UI进行操作的方式。底层也是基于异步消息处理机制。
AsyncTask的基本用法,AsyncTask是抽象类,需要创建子类继承它。在继承时需要指定三个参数:
- Params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
- Progress: 在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
- Result: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
...
}
需要重写4个方法:
- onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
- doInBackground(Params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress (Progress...)方法来完成。
- onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate (Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
- onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。
class DownloadTask: AsyncTask<Unit, Int, Boolean>() {
override fun onPreExecute() {
super.onPreExecute()
Log.d("DownloadTask", "onPreExecute")
}
override fun doInBackground(vararg params: Unit?): Boolean {
Log.d("DownloadTask", "doInBackground")
for (i in 1..100) {
// Log.d("DownloadTask", "$i")
publishProgress(i)
}
return true
}
override fun onProgressUpdate(vararg values: Int?) {
super.onProgressUpdate(*values)
val last = values.last()
Log.d("DownloadTask", "onProgressUpdate: $last")
}
override fun onPostExecute(result: Boolean?) {
super.onPostExecute(result)
val res = if (result == true) "success" else "failure"
Log.d("DownloadTask", "onPostExecute: $res")
}
}