05.异步消息机制Handler

253 阅读2分钟

Android系统规定只能在主线程更新UI,当子线程有更新UI的需求时,可以通过线程间通信,将信息传递给主线程,让主线程进行UI更新。

Handler

Handler是实现线程间通信的主要手段,Message是信息的载体。子线程通过Handler将Message发送到消息队列MessageQueue,主线程的Looper循环访问消息队列,发现一有消息就取出进行处理。这样便实现了异步消息机制。

Handler作用:异步消息处理,实现线程间通信

使用方法及原理

  1. 继承Handler,重写handleMessage(Message)方法

    inner class MyHandler : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    WHAT_SAY_HELLO -> {
                        tvContent.setText("Hello,what is your name!")
                    }
                    WHAT_SAY_HI -> tvContent.setText("Hi,my name is mmc.")
                    else -> {}
                }
                super.handleMessage(msg)
            }
        }
    
  2. 当子线程需要更新UI时,创建Message,通过Handler实例发送消息

    private val mHandler: MyHandler by lazy { MyHandler() }
    thread {
                        Thread.sleep(500)
                        mHandler.run {
                            //sendMessage(obtainMessage(WHAT_SAY_HI))
                            obtainMessage(WHAT_SAY_HI).sendToTarget()
                        }
                    }
    
  3. Message被推送到MessageQueue队尾,Looper循环遍历该队列,取出待处理的消息分发给Handler,Handler调用handleMessage(Message)方法进行处理。

注意:

  1. 实现Handler时,需要显示传入一个Looper,一般传入主线程的Looper。如果调用了无参的Handler(),将隐式传入当前线程的Looper,如果当前线程没有Looper,该Handler将无法接收消息,导致异常。

  2. 获取Message时,可以直接new一个实例,也可以通过Handler.obtain()从全局消息池返回一个Message。后者省去了创建实例的开销,更加高效。可直接调用Message.sendToTarget()发送消息。

    • Handler.sendMessage(Message)将消息发送到消息队列,实现异步消息机制;

    • Handler.handleMessage(Message)直接调用了Hanler的handleMessage(Message)方法,不通过线程池,不能实现异步消息;

    • Handler.dispatchMessage(Message),间接调用了handleMessage(Message),类似Handler.handleMessage(Message)

AsyncTask

如果觉得Handler使用起来麻烦,也可以直接使用线程的AsyncTask,它的底层就是Handler。

  1. 使用方法:实现AsyncTask,指定三个泛型参数类型,第一个泛型参数为创建实例时传入的参数,第二个为执行后台任务过程中通知前台线程更新UI的数据类型,第三个参数为后台任务执行完成的返回值类型。

    private inner class MyAsyncTask() : AsyncTask<Int, Int, String>() {
           //后台任务开始前准备动作
           override fun onPreExecute() {
               btn.run {
                   setText("Start loading...")
                   isEnabled = false
               }
           }
    
           //执行后台任务
           override fun doInBackground(vararg params: Int?): String {
               var progress = params?.get(0) ?: 0
               while (progress < 100) {
                   Thread.sleep(100)
                   publishProgress(progress++) //通知前台线程更新UI
               }
               return "Loading done!"
           }
    
           //接收后台任务的消息更新UI
           override fun onProgressUpdate(vararg values: Int?) {
               values?.get(0)?.let { progress.progress = it }
           }
    
           //后台任务执行完成更新UI
           override fun onPostExecute(result: String?) {
               progress.progress = 100
               btn.run {
                   setText(result)
               }
           }
       }
    
  2. 执行AsyncTask,注意一个AsyncTask实例只能执行一次,并且AsyncTask只能在主线程创建。

    myAsyncTask.execute(5)