<>#开篇

<>#核心源码

#简述

在整个 Android 的源码世界里,有两大利剑,其一是 Binder 机制,另一个便是 Handler
消息机制。消息机制涉及**MessageQueue/Message/Looper/Handler **这4个类。

Handler 是 Android 中引入的一种让开发者参与处理线程中消息循环的机制。我们在使用 Handler 的时候与 Message
打交道最多,Message 是 Hanlder 机制向开发人员暴露出来的相关类,可以通过 Message 类完成大部分操作 Handler 的功能。

<>作为一名程序员,我们不仅需要知道怎么用 Handler ,还要知道其内部如何实现的,这就是我写这篇文章的目的。
#模型

#####消息机制(Handler)主要包含:
✨ Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息; ✨
MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);

Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
✨ Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。
<>#实例
* A typical example of the implementation of a Looper thread, * using the
separation of {@link #prepare} and {@link #loop} to create an * initial Handler
to communicate with the Looper. */ class LooperThread extends Thread { public
Handler mHandler; public void run() { Looper.prepare(); mHandler = new
Handler() { public void handleMessage(Message msg) { // process incoming
messages here } }; Looper.loop(); } }
######接下来我们就围绕这个实例展开讲解!

<>#一 Looper

消息队列 MessageQueue 只是存储 Message 的地方,真正让消息队列循环起来的是 Looper,我们先来重点分析 Looper。

Looper 是用来使线程中的消息循环起来的。默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列 MessageQueue
的。为了能够让线程能够绑定一个消息队列,我们需要借助于 Looper :首先我们要调用 Looper 的 prepare() 方法,然后调用 Looper 的
Loop() 方法。

需要注意的是 Looper.prepare() 和 Looper.loop() 都是在新线程的 run 方法内调用的,这两个方法都是静态方法。
public static void prepare() {...} private static void prepare(boolean
quitAllowed) {...} public static void loop() {...}
<>#1.prepare()

我们来看一下 Looper.prepare(),该方法是让 Looper 做好准备,只有 Looper 准备好了之后才能调用 Looper.loop()
方法。
public static void prepare() { prepare(true); // 无参,调用 prepare(boolean
quitAllowed) } private static void prepare(boolean quitAllowed) { if
(sThreadLocal.get() != null) { // 每个线程只允许执行一次该方法,第二次执行时已有 Looper,则会抛出异常! throw
new RuntimeException("Only one Looper may be created per thread"); } // 创建
Looper 对象,并保存到当前线程的本地存储区 sThreadLocal.set(new Looper(quitAllowed)); }
上面的代码首先通过 sThreadLocal.get() 拿到线程 sThreadLocal 所绑定的 Looper 对象,由于初始情况下
sThreadLocal 并没有绑定 Looper ,所以第一次调用 prepare 方法时,sThreadLocal.get() 返回
null,不会抛出异常。

####2.ThreadLocal
ThreadLocal:线程本地存储区(Thread Local Storage,简称为
TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

TLS 常用的操作方法:

####3.set()
public void set(T value) { Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); //查找当前线程的本地储存区 if (map != null) map.set(this,
value); // 保存数据 value 到当前线程 this else createMap(t, value); }
######我们看下 getMap() 函数:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
######判断 map 是否为空,如果为空则创建 ThreadLocalMap :
void createMap(Thread t, T firstValue) { t.threadLocals = new
ThreadLocalMap(this, firstValue); }
####4.get()
private T setInitialValue() { T value = initialValue(); Thread t =
Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null)
map.set(this, value); else createMap(t, value); return value; }
######Looper 通过如下代码保存了对当前线程的引用:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); //
sThreadLocal 为 ThreadLocal 类型
所以在 Looper 对象中通过 sThreadLocal 就可以找到其绑定的线程。ThreadLocal 中有个 set 方法和 get 方法,可以通过
set 方法向 ThreadLocal 中存入一个对象,然后可以通过 get 方法取出存入的对象。

ThreadLocal 在 new 的时候使用了泛型,从上面的代码中我们可以看到此处的泛型类型是 Looper ,也就是我们通过 ThreadLocal 的
set 和 get 方法只能写入和读取 Looper 对象类型。

#二 构造函数
源码中 Looper 的构造函数是 private 的,也就是在该类的外部不能用 new Looper() 的形式得到一个 Looper 对象。
private Looper(boolean quitAllowed) {...}
######我们看下上面代码中 new Looper() 创建 Looper 对象的工作:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed);
// 创建 MessageQueue 对象 mThread = Thread.currentThread(); // 记录当前线程 }
<>Looper.prepare()在每个线程只允许执行一次,该方法会创建 Looper 对象,Looper 的构造方法中会创建一个
MessageQueue 对象,再将 Looper 对象保存到当前线程 TLS。
#1.prepareMainLooper()

另外,与 prepare() 相近功能的,还有一个 prepareMainLooper() 方法,该方法主要在 ActivityThread 类中使用。
public static void prepareMainLooper() { prepare(false); // 设置不允许退出的 Looper
synchronized (Looper.class) { // 将当前的 Looper 保存为主 Looper,每个线程只允许执行一次 if
(sMainLooper != null) { throw new IllegalStateException("The main Looper has
already been prepared."); } sMainLooper = myLooper(); } }
#2.loop()
Looper.loop()的代码如下:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()}
to end the loop. */ public static void loop() { final Looper me = myLooper();
// 获取当前线程绑定的 Looper 对象 if (me == null) { throw new RuntimeException("No Looper;
Looper.prepare() wasn't called on this thread."); } final MessageQueue queue =
me.mQueue; // 获取 Looper 对象中的消息队列 // Make sure the identity of this thread is
that of the local process, // and keep track of what that identity token
actually is. Binder.clearCallingIdentity(); // 确保在权限检查时基于本地进程,而不是基于最初调用进程 final
long ident = Binder.clearCallingIdentity(); for (;;) { // 进入loop的主循环方法 Message
msg = queue.next(); // might block(可能会堵塞) if (msg == null) { // 没有消息,则退出循环 //
No message indicates that the message queue is quitting. return; } // This must
be in a local variable, in case a UI event sets the logger // 默认为null,可通过
setMessageLogging() 方法来指定输出,用于 debug 功能 final Printer logging = me.mLogging; if
(logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what); } final long slowDispatchThresholdMs =
me.mSlowDispatchThresholdMs; final long traceTag = me.mTraceTag; if (traceTag
!= 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag,
msg.target.getTraceName(msg)); } final long start = (slowDispatchThresholdMs ==
0) ? 0 : SystemClock.uptimeMillis(); final long end; try {
msg.target.dispatchMessage(msg); // 用于分发 Message end = (slowDispatchThresholdMs
== 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) {
Trace.traceEnd(traceTag); } } if (slowDispatchThresholdMs > 0) { final long
time = end - start; if (time > slowDispatchThresholdMs) { Slog.w(TAG, "Dispatch
took " + time + "ms on " + Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what); } } if (logging !=
null) { logging.println("<<<<< Finished to " + msg.target + " " +
msg.callback); } // Make sure that during the course of dispatching the //
identity of the thread wasn't corrupted. final long newIdent =
Binder.clearCallingIdentity(); // 确保分发过程中 identity 不会损坏 if (ident != newIdent)
{ // 打印 identity 改变的 log,在分发消息过程中是不希望身份被改变的 } msg.recycleUnchecked(); // 将
Message 放入消息池 } }
我们接下来会重点分析 loop() 里面的几个函数:
#####myLooper()
前面我们说过,在执行完了 Looper.prepare() 之后,我们就可以在外部通过调用 Looper.myLooper() 获取当前线程绑定的
Looper 对象。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); // 还是通过
sThreadLocal.get()方法获取当前线程绑定的 Looper 对象 }
####3.MessageQueue
// Looper 构造函数中创建了 mQueue,即 MessageQueue private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 创建 MessageQueue 对象 mThread =
Thread.currentThread(); // 记录当前线程 } public static void loop() { final Looper
me = myLooper(); // 获取当前线程绑定的 Looper 对象 if (me == null) { ... ... } final
MessageQueue queue = me.mQueue; // 获取 Looper 对象中的消息队列
变量 me 是通过静态方法 myLooper() 获得的当前线程所绑定的 Looper,me.mQueue 就是当前线程所关联的消息队列。
####4.for()
for (;;) { // 进入loop的主循环方法
我们发现for循环没有设置循环终止的条件,所以这个for循环是个死循环。

####5.Message
Message msg = queue.next(); // might block
我们通过消息队列 MessageQueue 的 next 方法从消息队列中取出一条消息,如果此时消息队列中有 Message,那么 next
方法会立即返回该 Message,如果此时消息队列中没有 Message,那么 next 方法就会阻塞式地等待获取 Message。

####6.dispatchMessage()
/*package*/ Handler target; try { msg.target.dispatchMessage(msg); end =
(slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if
(traceTag != 0) { Trace.traceEnd(traceTag); } }
msg 的 target 属性是 Handler,该代码的意思是让 Message 所关联的 Handler 通过 dispatchMessage 方法让
Handler 处理该 Message ,关于 Handler 的 dispatchMessage 方法将会在下面详细介绍。

####7.recycleUnchecked()
msg.recycleUnchecked(); // 分发后的 Message 回收到消息池,以便重复利用
#小结
loop()进入循环模式,不断重复下面的操作,直到没有消息时退出循环:

####1、读取 MessageQueue 的下一条 Message;
####2、把 Message 分发给相应的 target;
####3、再把分发后的 Message 回收到消息池,以便重复利用。
明天继续更完剩下的



#想学习更多Android知识,或者获取相关资料请加入Android技术开发交流2群:935654177。本群可免费获取Gradle、RxJava、小程序、Hybrid、移动架构、NDK、React
Native、性能优化等技术教程!

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信