开篇
核心源码
123PY`(GH()4}Z9MTYA4M26.png简述
在整个 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 回收到消息池,以便重复利用。
明天继续更完剩下的
image.png
网友评论