深入理解Handler机制之引路篇
要想深入理解Handler机制,就要理解:
- Android为何要引入Handler机制?
- Handler机制究竟是什么?
如果去查阅官方Handler文档,会发现官方没有给Handler下过定义,更没有介绍为何要引入Handler机制。官方文档的第一句就说Handler允许你发送和处理与一个线程的消息队列关联的消息和任务。这是对Handler能力和用法的一种描述,并没有讲为什么和是什么,这就造成初学者只知道怎么用Handler,但是不知道其背后的设计思想和原理。
Android为何引入Handler
要知道Android为何引入Handler机制,首先要了解Java线程机制:
- 线程执行任务
- 任务在线程的run方法中执行
- run方法结束,线程终止
这种机制的问题在于,创建线程的开销非常大,即使通过线程池重用线程,线程调度的开销对于数量多、密度大、要求快速响应的UI更新任务来说也是不可接受的,于是Android引入Handler线程解决这个问题。Handler线程就是Handler机制的核心:Handler线程永不退出,在run方法中轮询任务,有任务就处理,没任务就等待。
有些Handler线程是允许退出的。
Android应用的主线程是一个不允许退出、生命周期与进程一样长的线程。
Handler机制工作原理
Handler机制是一整套任务处理机制,需要Handler、Looper、Message和MessageQueue配合。
- Looper在线程中死循环,使线程不退出
- Handler发送和处理Message
- Message包含任务或者执行任务所需的数据
- Message发送给MessageQueue,MessageQueue负责Message的排序与出队
Handler机制工作原理Message是Android中的一个重要概念,它把Android应用从Java的任务处理系统变成了消息处理系统。
Looper
要把一个线程变成Handler线程,只需在线程的run方法中调用Looper.prepare()和Looper.loop()。
class LooperThread extends Thread {
public void run() {
Looper.prepare();
Looper.loop();
}
}
Looper的创建非常巧妙,是在Looper.prepare()中完成的。Looper实例存储在ThreadLocal中,可以通过Looper.myLooper()获取。Looper默认是可以退出的,要创建不可退出的Looper,需要调用Looper.prepare(false),主线程Looper正是这样创建的。要注意的是,一个线程只能与一个Looper关联。
Looper创建后通过Looper.loop()完成工作。正如上文所说,Looper.loop()在Thread.run方法中轮询消息,使线程不退出、消息串行处理。
// Looper.java
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
}
MessageQueue
MessageQueue是在Looper的构造函数内创建的。由此可知,线程、Looper和MessageQueue三者是一一对应的。
MessageQueue的主要工作是处理Message入列和安排Message出列。MessageQueue并不是常规的先进先出队列,而是根据Message期望执行的时间排列的。
- 入列:boolean enqueueMessage(Message msg, long when)
- 出列:Message next()
这两个方法都有用到native代码,实现比较复杂,就不分析了(因为没看懂😄)。
Message
Message是一个数据类,携带要执行的任务(Runnable)或执行任务所需的数据(what、arg1、arg2、obj、data)以及任务处理者(Handler)和任务执行时间(when)。Message的实现是一个单链表结点,因此可以携带下一条Message。MessageQueue正是基于Message单链表实现的。
由于是数据类,且应用中需要大量Message,因此Message实现了重用缓存池,通过Message.obtain()获取。与MessageQueue一样,Message缓存池也是基于其单链表实现的,不同的是缓存池是栈而非队列。
Handler
官方文档说,Handler与创建它的线程及该线程的MessageQueue关联(When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it)。这说的是不指定Looper的情况下,Handler自动与该线程的Looper关联,上文说,线程、Looper和MessageQueue三者一一对应,因此默认情况下,Handler与创建它的线程及该线程的MessageQueue关联。但在创建Handler时是可以指定Looper的,这样就会与Looper所在的线程和MessageQueue关联。
不管Handler在哪个线程创建,它发出的消息都是在与之关联的Looper所在的线程处理的,这正是Handler用于线程间通信的原理。由于后台线程不允许更新UI,因此后台线程通过一个与主线程Looper关联的Handler(new Handler(Looper.getMainLooper()))来更新UI。
Handler发送消息有两种方式:
- sendMessage(Message)
- post(Runnable)
但其实就一种,因为Runnable也会转为Message。Handler发送消息还可以设置延时或定时,但最终也是一种,因为延时也会被转为定时任务。因此所有的发送方式最终都调用下列方法处理:
// Handler.java
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
放入MessageQueue中的Message会被逐个取出,串行处理。有3处对象可以处理消息:
- Message自身callback(一般是post(Runnable)方式发送的消息)
- Handler创建时设置的mCallback
- Handler
// Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
HandlerThread
Android应用的大部分消息都可以由主线程处理,如果需要在后台线程中处理消息,可以创建一个新的Handler线程。
开发者并不需要自己实现Handler线程,直接使用内置的HandlerThread即可:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
源码篇和实现篇
要想深入理解Handler机制,光凭一篇引路文章是不够的,更重要的是阅读源码、动手实践和总结分享。
是不是很期待源码篇和实践篇?你来写!
看好你哦~