在了解 Handler 前,首先需要知道 Android 中,当程序启动时,Android 系统会启动一条线程,该线程也被称作 UI 线程,主要用于进行界面 UI 的操作。而又因为其不是线程安全的,所以 UI 线程有以下要求 : 只允许在 UI 线程中对界面 UI 进行操作且不允许在 UI 线程中进行耗时操作。这就导致了在进行操作时,必须启动一条新的线程,而想在新线程中,对 UI 进行操作,则需要借助到 Handler 类的消息传递机制来实现了
Handler 的作用
其作用主要有两个:进程间的消息发送和处理。借助这两个功能,可以在新线程中进行耗时操作,比如网络请求数据,在请求结束时,通过 Handler 类来向主线程发送消息;主线程则可以通过 Handler 来处理消息,根据消息来对界面 UI 进行操作。
Handler 的使用
按照正常的方法,创建类只需要调用它的构造函数即可,那么创建一个 Handler 类的代码应该如下
在创建完成后,就可以使用 Handler 的方法来发送消息了。sendMessage 方法接收的是一个 Message 参数,所以直接调用 Message 方法创建,创建之后就调用 sendMessage 方法即可。
val msg = Message()
// 设置 msg 的参数,可用于识别消息
msg.what= 10000
handler.sendMessage(msg)那么在发送完信息之后,应该有个地方对 Message 消息进行处理,这时候需要用到 Handler 类的 handlerMessage 方法。
但是在 Handler 类源码中进行查看,可以看到该方法并没有进行实现,需要调用者主动去实现它,所以我们在创建 Handler 类时就不能通过直接调用构造函数的方式创建,而是通过继承并重写 handlerMessage 方法来创建一个 Handler 的子类。代码如下
private val handler: Handler = object : Handler() {
override fun handleMessage(msg: Message?) {
println(msg?.what)
}
}
上述方法采用了 object 来创建一个 Handler 的匿名内部类,并重写了 handleMessage 方法,能够将消息的参数打印至控制台中。
这样,在调用 sendMessage 方法发送的消息,就能够在这里进行处理。
虽然上述 Handler 类的创建方法能够完成消息的发送处理的功能,但是此时编译器会提示该 Handler 可能会存在内存泄漏问题,原因是因为创建了一个内部 Handler 类,内存泄漏的具体原因在下面章节会讲解。既然内部类会出现问题,那么就直接写成嵌套类即可。
class MainActivity:BaseActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
class MyHandler:Handler(){
override fun handleMessage(msg: Message?) {
println(msg?.what)
}
}
}上述写法在 kotlin 语法下是不会提示内存泄漏的,可以正常使用。而在 Java 的写法下,还是会提醒可能会出现内存泄漏问题,这是因为在 Java 中,MyHandler 类仍然是内部类,而在 kotlin 中则是嵌套类。
嵌套类和内部类区别: 嵌套类只是单纯的将一个类写在另一个类的内部,两者没有关系,而内部类则是可以调用外部类的成员。
除了上述的创建方式外,还推荐另一种创建方式,更加方便快捷,并且在 Java 和 kotlin 语法中都适用。// 实现 Handler.Callback 接口
private val handlerCallback = object:Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
println(msg?.what)
return false
}
}
private val handler:Handler = Handler(handlerCallback)该方法在调用 Handler 类的构造函数时,传入了 Handler.Callback 这个接口,该接口是 Handler 类所提供的,其代码如下
public interface Callback {
public boolean handleMessage(Message msg);
}
该接口的作用时用来重写 Handler 的 handleMessage 方法,从而避免通过继承 Handler 子类来重写。在传入该接口的实现后,在处理消息时,就会调用该接口的方法。
所以 Handler 简单使用流程如下
class MainActivity : BaseActivity() {
// 接口
private val handlerCallback = object:Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
println(msg?.what)
return false
}
}
// Handler
private val handler:Handler = Handler(handlerCallback)
// 生命周期 onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 发送消息
val msg = Message()
msg.what = 1000
handler.sendMessage(msg)
}
}
上述代码中,会在控制台输出 1000
不同线程中使用
在文章最开始有提到,Handler 的主要作用是发送消息和处理消息,而且是在不同的线程间。上述的例子都只是在主线程中进行的消息的发送和处理。
虽然发送/处理消息都是一样的,但需要特别注意不同线程间使用会出现的问题。
对上述代码进行修改,在一个新的线程中进行消息的发送
// 接口
private val handlerCallback = object:Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
println("在 ${Thread.currentThread()} 进行处理")
println(msg?.what)
return false
}
}
// Handler
private val handler:Handler = Handler(handlerCallback)
// 生命周期 onCreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 新线程中发送消息
Thread(Runnable {
println("在 ${Thread.currentThread()} 进行发送")
val msg = Message()
msg.what = 1000
handler.sendMessage(msg)
}).start()
}
控制台输出如图所示,这表明了 Handler 确实能够做到在新线程中向主线程中发送消息(main 为主线程,Thread-2 为新线程)。
那么如果是主线程向新线程中发送消息会怎么样呢。首先我们先要在新线程中创建一个 Handler 类给主线程使用,修改上述代码如下
private val handlerCallback = object:Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
println("在 ${Thread.currentThread()} 进行处理")
println(msg?.what)
return false
}
}
private lateinit var handler:Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 新线程中创建 Handler 类
Thread(Runnable {
handler = Handler(handlerCallback)
}).start()
// 线程停止 2s,保证 handler 已经创建完了
Thread.sleep(2000)
println("在 ${Thread.currentThread()} 进行发送")
val msg = Message()
msg.what = 1000
handler.sendMessage(msg)
}
在执行上述代码时,系统抛出了异常,异常信息为 Can't create handler inside thread that has not called Looper.prepare(),这句话的意思是说不能够在一个没有调用 Looper.prepare() 的线程中创建 Handler 类。
那这个 Looper 类是什么呢,这个在下面章节再讲解,这里先给出能够正确执行的代码
private val handlerCallback = object:Handler.Callback{
override fun handleMessage(msg: Message?): Boolean {
println("在 ${Thread.currentThread()} 进行处理")
println(msg?.what)
return false
}
}
private lateinit var handler:Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 新线程中创建 Handler 类
Thread(Runnable {
// 新增 Looper 方法
Looper.prepare()
handler = Handler(handlerCallback)
// 新增 Looper 方法
Looper.loop()
}).start()
// 线程停止 2s,保证 handler 已经创建完了
Thread.sleep(2000)
println("在 ${Thread.currentThread()} 进行发送")
val msg = Message()
msg.what = 1000
handler.sendMessage(msg)
}
控制台输出结果如图,新增两个 Looper 的方法后,就能够正确的得到结果
Handler 的工作原理
首先需要知道的是使用 Handler 完成线程间的通信,会涉及到 4 个类,包括有 Handler,Message,Looper,MessageQueue。
- Handler 的作用就是发送和处理消息
- Message 则是消息的载体类。
- Looper 则是一个线程的消息循环类,在其源码的注释有写到:
该类用于为线程运行消息循环。默认情况下,线程没有与他们关联的消息循环,要创建一个消息循环,请现在运行循环的线程中调用 Looper.prepare() 方法,然后调用 Looper.loop() 方法来循环处理消息。 - MessageQueue 则是一个消息队列类,用来管理改线程下的所有 Message 消息
Handler 每次发送的消息都会被发送到消息队列中存储。而 Looper 则会循环遍历消息队列中的消息,并交给对应的 Handler 来处理。这样就能够完成了 Handler 的消息发送和处理。
在了解完基本的工作原理后,回顾一下上述章节所提到的异常: Can't create handler inside thread that has not called Looper.prepare(), 这个异常是在 Handler 的构造函数中抛出的
// Handler 构造函数
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
也就是说在 Looper.myLooper() 中获得了空值,该方法会获得该线程下的 Looper,如果该线程没有关联的 Looper,则会返回空值,从而报错。所有将 Looper 和线程关联起来的方法就是 Looper.prepare()。
需要注意的是每一个线程中都只有一个 Looper 和 MessageQueue,其中 MessageQueue 由 Looper 创建。也就是说当前线程只能调用一次 Looper.prepare() 方法,否则会报错。
private static void prepare(boolean quitAllowed) {
// 不为空说明已经关联过了
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
如果只是按照异常提示加上 Looper.prepare() 方法而不加上Looper.loop() 方法,虽然不会报错,但 Handler 也不会进行消息的处理。如下所示:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 新线程中创建 Handler 类
Thread(Runnable {
// 调用 Looper.prepare 方法
Looper.prepare()
handler = Handler(handlerCallback)
// 不调用该方法
// Looper.loop()
}).start()
// 线程停止 2s,保证 handler 已经创建完了
Thread.sleep(2000)
println("在 ${Thread.currentThread()} 进行发送")
val msg = Message()
msg.what = 1000
handler.sendMessage(msg)
}
控制台输出如上图所示,Handler 只发送了消息,但是却没有对消息进行处理。那是因为消息存储在消息队列中,但是没有 Looper 进行分发,Handler 无法获得该消息。
所以才需要调用 Looper.loop()方法,让 Looper 循环遍历消息队列中的消息进行分发。而在主线程使用 Handler 时,不需要执行 Looper 的方法,那是因为主线程在创建的过程中,就已经执行了这两步方法。
更多大樹雲資源,請參照: