Handler机制几乎是面试必考
前向问题
在需要在子线程中进行UI界面的更新时,如果直接像下述代码一样对textView这个控件进行更新,会导致系统报错。
原因是Android并不允许在子线程中进行UI操作。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
changeTextBtn.setOnClickListener {
thread {
textView.text = "Nice to meet you"
}
}
}
解决方法
解决方法就是用到了 Handler机制,通过使用Handler,将上述代码修改为以下代码。
class MainActivity : AppCompatActivity() {
val updateText = 1
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 {
val msg = Message()
msg.what = updateText
handler.sendMessage(msg) // 将Message对象发送出去
}
}
}
}
详细解析
由于在子线程中并不能直接对UI进行操作,于是就在子线程中创建了一个Message对象msg,并将其的what字段的值指定为updateText,然后调用handler的sendMessage(msg)方法将msg发送出去。
在发送出去后,Handler就会接收到这条Message,然后会调用其自身的handleMessage()方法,并且这个方法是在主线程中运行的(可以更新UI)。
在这里我们重写了handleMessage()方法,判断当msg.what=updateText(即1)的时候,就执行textView.text = "Nice to meet you"语句,更新界面UI。
拓展说明
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。
1. Message:
Message主要用于在线程之间传递消息,其可以携带少量的信息,例如what字段,arg1、arg2(都只能携带整形数据)和obj字段(Object对象)。
- what;
- arg1和arg2:携带整形数据;
- obj:携带Object对象
2. Handler:
Handler主要用于发送和处理消息,其部分方法如下:
- 发送消息:sendMessage()、post();
- 处理/接收消息:handleMessage()
3. MessageQueue:
MessageQueue是消息队列,主要用于存放所有通过Handler发送的消息。存放在这个队列中等待Handler的处理(即handleMessage()方法)。
4. Looper:
Looper针对的是每个MessageQueue,是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。
了解了上述的Android的异步消息处理后,就可以详细解释一下上述“解决方法”的整个工作流程了,整个流程大体如下:
- 首先是在子线程创建一个Message;
- 使用一个Handler将这个Message发送出去;
- 发送出去之后,这条Message就会被添加到MessageQueue队列中等待被处理;
- Looper则会一直尝试从MessageQueue中取出待处理消息,然后发回Handler的handleMessage()方法中进行处理。
由于我们在Handler的构造函数中我们传入了Looper.getMainLooper(),所以此时handleMessage()方法中的代码也会在主线程中运行。其整个过程的流程可以用如下图所示的流程图进行解释。(图片来源:《第一行代码》郭霖)
面试真题(某大厂)
-
Handler机制是什么?在没有message的时候为什么不会导致应用程序ANR?
问题分析:Handler机制可以根据上述介绍答出,而第二个问题则需要进一步的了解。
原因在于:当没有message的时候,即没有任何消息需要处理,则此时Handler会保持空闲,并不会调用handleMessage()方法执行操作,故此时并不会阻塞主线程。(Handler是异步的)