十九、Android-多线程

83 阅读4分钟

重点: 和IOS一样,android也是不允许在子线程中更新UI的。

19.1 异步执行任务的方式

  1. 继承Thread
// 声明
class MyThread: Thread() {
    override fun run() {
        super.run()
        for (i in 1..100) {
            Log.d("Thread", "$i")
        }
    }
}
// 调用
MyThread().start()
  1. 实现Runnable接口
// 声明
class CustomThread: Runnable {
    override fun run() {
        for (i in 1..100) {
            Log.d("Thread", "$i")
        }
    }
}
// 调用
val customThread = CustomThread()
Thread(customThread).start()
  1. Lambda方式。 更方便
            Thread {
                for (i in 1..100) {
                    Log.d("Thread", "$i")
                }
            }.start()
  1. 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个部分进行一下简要的介绍。

  1. Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。上一小节中我们使用到了Message的what字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。

  1. Handler

Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法、post()方法等,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。

  1. MessageQueue

MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

  1. Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。

Looper.png

19.4 使用AsyncTask-已弃用,可使用协程替代

Android提供的更加方便在子线程中对UI进行操作的方式。底层也是基于异步消息处理机制。

AsyncTask的基本用法,AsyncTask是抽象类,需要创建子类继承它。在继承时需要指定三个参数:

  • Params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  • Progress: 在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  • Result: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
 ...
}

需要重写4个方法:

  1. onPreExecute()

这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

  1. doInBackground(Params...)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress (Progress...)方法来完成。

  1. onProgressUpdate(Progress...)

当在后台任务中调用了publishProgress(Progress...)方法后,onProgressUpdate (Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

  1. 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")
    }
}