Android 复盘——帮你彻底了解消息机制
# 1. 什么是消息机制
说到消息机制,作为一名 Android 开发者一定先想到的是 Handler。Handler 就是 Android 消息机制的上层接口,我们可用通过 Handler 轻松的在不同的线程中切换任务,但 Handler 的实现还有两个很重要的概念 MessageQueue
和 Looper
。
MessageQueue 的翻译是消息队列,它的内部采用了单链表的结构存储 Handler 对象发送的消息。
Looper 的作用是不断地查询 MessageQueue 中是否有消息,如果 Looper 发现 MessageQueue 中存入了新的消息,它就会去处理这条消息,如果没有新消息,Looper 就会以无限循环的方式去查询 MessageQueue 中是否有新消息。
# 2. 为什么要有 Handler
## 2.1)官方文档中 Handler 的主要作用
(1)安排将来某个时间点执行的 Message
和 Runnables
;
(2)在不同于当前的线程上执行的操作;
## 2.2)Handler 被用来做的最多的一件事就是更新主线程的 UI。
在 Android 开发中,默认子线程是不可以更新 UI 的,这一点可以从 View 的最高层级 ViewRootImpl 类中找到答案
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}
ViewRootImpl 类中的 checkThread
方法会在更新 UI 前被执行,如果当前线程不是主线程,就会抛出 Only the original thread that created a view hierarchy can touch its views.
的异常
## 2.3)那么 Android 为什么要设计为只能在主线程中更新 UI 呢?
- Android 在子线程中更新 UI 是不安全的,如果多个子线程同时修改一个控件的数据,后果是不可控的
- 如果给 UI 更新机制加锁,会降低 UI 的访问效率,并且可能阻塞某些线程的执行
# 3. Handler 的用法
## 3.1)在主线程中创建 Handler
通常,我们在主线程中创建 Handler 的写法如下:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
但这样写,系统会这样提示:
This Handler class should be static or leaks might occur (anonymous android.os.Handler)
这个Handler类应该是静态的,否则可能会发生泄漏
出现这个警告但原因是,Handler 在 Activity 中作为一个匿名内部类来定义,它的内部持有来 Activity 的实例。当 Activity 被用户关闭时,因为 Handler 持有了 Activity 的引用,就造成了 Activity 无法被回收,从而导致了内存泄漏。
因此,在这里推荐一种更加安全的写法:
private static class MyHandler extends Handler{
private WeakReference<Activity> weakReference;
public MyHandler(Activity activity){
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
break;
}
}
}
private MyHandler handler = new MyHandler(this);
通过静态内部类的方式实现一个 Handler,此时内部类并不持有外部类对象的应用,需要在内部类的构造方法内增加一个外部类(Activity)的弱应用。这样,即使 Activity 被关闭,Activity 也能顺利被回收。
onCreate() 中的代码如下:
btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
Message message = Message.obtain();
message.what = 0;
handler.sendMessage(message);
}
}.start();
}
});
这时候点击按钮的运行效果如下:
运行效果
## 3.2)在子线程中创建 Handler
在官方文档中 Handler 的主要作用是在不同于当前线程的线程中执行操作,那么如何用 Handler 解决两个子线程之间的通信呢?
请看代码:
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
Looper.prepare();
handler = new MyHandler(MainActivity.this);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Looper.loop();
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
Message message = Message.obtain();
message.what = 0;
handler.sendMessage(message);
}
}.start();
}
});
此时点击按钮:
运行效果
可见当前的处理线程已经变成了子线程。
# 4. Handler 工作原理
如果细心的观察代码,可以看到在子线程中创建 Handler 的时候调用了 Looper.prepare()
和 Looper.loop()
两个方法。这两句代码有什么用呢?
我们暂时可以把 Looper 理解为消息的管理者,它负责从 MessageQueue 中提取出消息,传递给 Handler 进行处理,每一个 Handler 都必须要有一个 Looper,在 Handler 创建的时候,它会自动使用当前线程的 Looper,而 Looper.prepare()
的作用就是为当前线程准备一个 Looper,Looper.loop()
的作用是开始查找当前 MessageQueue 中是否有了新的消息。
这就是 Handler 工作的第一步 :
## 4.1)采用当前线程的 Looper 创建 Handler
因为这里主要讲 Handler 的工作流程,创建 Looper 的具体过程放到文章的下面讲解。我们只要知道
Looper.prepare()
为当前的线程创建了一个 Looper 对象即可。
但是,在主线程中创建 Handler 的时候,我们并没有看到 Looper.prepare()
的执行,这是因为在 UI 线程,即 ActivityThread 的创建过程中,Looper 已经被创建好了。
我们可以在 ActivityThread 的 main() 方法中看到这样一句代码:
Looper.prepareMainLooper();
这个方法内部也调用了 Looper.prepare()
为 UI 线程创建了一个 Looper。
## 4.2)通过 Handler 的 sendMessageAtTime()
方法发送 Message
为什么是 sendMessageAtTime
?不是还有 sendMessage()
,sendEmptyMessage()
,sendEmptyMessageDelayed()
,sendEmptyMessageAtTime()
,sendMessageDelayed()
这么多方法吗?
通过阅读这些方法的源码可以发现,这些方法最终调用的都是 sendMessageAtTime()
。
其次还有 post()
,postAtTime()
,postDelayed()
方法最终调用的也都是 sendMessageAtTime()
方法,只是多了一步调用 getPostMessage(Runnable r, Object token)
将 Runnable 封装为一个 Message 对象的 callback 里。
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
那么 sendMessageAtTime()
里的具体操作是什么呢?我们去源码里一探究竟
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 先获取当前 Handler 中的 MessageQueue,mQueue 在 Looper 的构造方法中进行初始化。
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// queue 不为空,则执行 Handler.java 里的另一个 enqueueMessage() 方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 指定 msg 的 Target 对象为当前的 Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在 enqueueMessage() 中,会最终调用 MessageQueue.java 中的 enqueueMessage() 方法。
在 sendMessageAtTime() 方法内部 调用了 enqueueMessage() 方法,将 Message 对象传递到 MessageQueue 即消息队列里中,在消息队列里的具体处理逻辑在文章的 MessageQueue 工作原理 部分会具体解释。
## 4.3)Looper 处理消息后调用 Handler 的 dispatchMessage() 方法
在第二步将消息插入消息队列后,Looper 就开始遍历消息队列,找到新的消息,再通知 Handler 去执行这条消息,调用的就是 Handler 的 dispatchMessage()
方法。
public void dispatchMessage(Message msg) {
// msg 的 callback 对象就是一个 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
// 检查 mCallback 是否为空,不为空就执行它内部定义的 handleMessage() 方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果 mCallback 为空,就执行在实例化 Handler 过程中我们自己定义的 handleMessage() 方法中的内容
handleMessage(msg);
}
}
dispatchMessage()
方法首先会检查 Message 的 Callback 对象是否为空,callback 就是通过 post() 方法传递的 Runnable 对象,如果 callback 不为空,就去执行 handleCallback() 方法。
handleCallback() 方法的实现也很简单,它在内部执行了 Runnable 的 run()
方法
private static void handleCallback(Message message) {
message.callback.run();
}
如果 callback 对象为空,就检查 mCallback 是否为空,不为空就执行它的定义的 handleMessage() 方法,若没有 mCallback,最终将直接执行我们在继承 Handler 时自己定义的 handleMessage() 方法中的代码。
Callback
是 Handler 中定义的的一个接口,它的代码如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
如果使用 Callback 接口的话,我们可以直接实例化一个 Handler 而不用去实现一个 Handler 的子类,
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
# 5. MessageQueue 工作原理
我们从上一部分的 MessageQueue.java 中的 enqueueMessage() 方法开始入手。
## 5.1)enqueueMessage()
代码量有点多,要耐心看哦!
boolean enqueueMessage(Message msg, long when) {
// 检查当前 msg 的 target 是否为空
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// msg 如果正在被执行,就抛出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
// 在 quit() 方法中,mQuitting 会被设置为 true
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
// 标记当前的 msg 正在执行
msg.markInUse();
// 设置 msg 的 when 为传进来的 when 参数,when 是 Message 想要被执行的时间
msg.when = when;
// 得到当前消息队列的头部消息
Message p = mMessages;
boolean needWake;
// 当前消息队列为空,新消息的触发时间为 0,或者新消息的触发时间早于消息中第一条消息的触发时间
// 则将新消息插入到队列的头部,作为当前消息队列的第一条消息
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// 将当前消息的下一条消息指向头部消息
msg.next = p;
// 头部消息修改为当前消息
mMessages = msg;
// 当阻塞时,需要唤醒
needWake = mBlocked;
} else {
// 将新消息插入到当前消息队列当中,(不是头部)
// 通常我们不必唤醒事件队列,
// 除非队列头部有消息障碍,并且消息是队列中最早的异步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 开始循环便利消息队列,比较新消息和队列中消息的 when(触发事件)的值,将新消息插入到适当位置
for (;;) {
// 循环第一次遍历时,将当前队列中的头部消息赋值给 prev
prev = p;
// p 指向队列中的第二个消息
p = p.next;
// 如果下一个消息为空,或者新消息的触发时间早于下一个消息,找到了要插入的位置,退出循环
if (p == null || when < p.when) {
break;
}
// needWake 为 true,并且 下一条消息是异步的,则不需要唤醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 将新消息插入到 p 之前,头消息之后。
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 如果需要唤醒,调用 nativeWake 方法去唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
执行完 enqueueMassage 方法,我们新发送的 Message 就成功的插入了消息队列当中。
但是除了插入新消息,我们还需要从消息队列中读取消息,这又要怎么做呢?
## 5.2)next()
Message next() {
// 如果消息循环已退出,并且被丢弃,则返回空。
// 这个将在应用重启一个 looper 时发生
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 记录空闲时处理的 IdlerHandler 数量,只在第一次迭代时为 -1
// IdleHandler 只在队列为空 或者 是头部消息时执行
int pendingIdleHandlerCount = -1;
// native 层使用的变量,设置的阻塞超时时长,0 为不阻塞,-1 为阻塞
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// 尝试检索下一条消息。 如果找到则返回。
synchronized (this) {
// 获取系统从开机到现在到时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
// 将队列中到头部消息赋值给 msg
Message msg = mMessages;
if (msg != null && msg.target == null) {
// msg 不为空,但是这个 msg 没有 handler,则这个 msg 为栅栏
// 开始遍历,指到获取第一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 如果当前时间不到 msg 的触发时间,则计算时间差,设置阻塞超时时长
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 当前时间到了 msg 的触发时间,则获取消息并返回
mBlocked = false;
// 如果当前的 msg 不是头部消息,则上一条消息的 next 指向 msg 的 next
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 当前 msg 为头部消息,则将下一个 msg 设置为头部消息
mMessages = msg.next;
}
// msg 的下一个 Message 对象置空,表示从消息队列中取出来了这条 msg
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
// 标记 msg 正在使用
msg.markInUse();
return msg;
}
} else {
// 如果没有消息,则设置阻塞时长为 -1,直到被唤醒
nextPollTimeoutMillis = -1;
}
// 所有的消息都被处理后,判断是否退出,并返回 null。
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
// 第一次循环时,消息队列为空,或 当前时间未到消息的触发时间,获取 IdleHandler 的数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// pendingIdleHandlerCount 的数量为 0 时,线程会继续堵塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 判断当前空闲时处理任务的handler是否是为空,如果为空,就实例化出新的对象
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 运行 IdleHandler,只有第一次循环时才会运行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
// 释放 IdleHandler 的引用
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
// 执行 IdleHandler 的方法
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置 IdleHandler 的数量为 0,确保不会重复运行它们
pendingIdleHandlerCount = 0;
// 在执行 IdleHandler 时,一个新的消息可能插入或消息队列中的消息到了触发时间
// 所以将 nextPollTimeoutMillis 设为 0,表示不需要阻塞,重新检查消息队列。
nextPollTimeoutMillis = 0;
}
}
至此,MessageQueue 的两个最重要的方法已经分析完了,下面来看 Looper 如何循环地从消息队列中取出消息。
# 6. Looper 工作原理
在讲 Looper 之前,需要先理解 ThreadLocal 的工作原理
## 6.1)ThreadLocal 的工作原理
ThreadLocal 是一个线程内存储数据的类,当不同的线程去访问同一个 ThreadLocal 对象时,获得的值都是不一样的,下面用一段代码来证明
private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
mThreadLocal.set("Thread_A");
Log.d("ThreadLocalValue",mThreadLocal.get());
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
mThreadLocal.set("Thread_B");
Log.d("ThreadLocalValue",mThreadLocal.get());
}
}.start();
}
);
我在两个线程中分别存入在 mThreadLocal 中存入了不同的值,然后在控制台输出它们的内容
不同线程访问 ThreadLocal 对象可见不同线程访问同一个 ThreadLocal 对象得到的值也是不一样的。
ThreadLocal 实现这种特性的原因也很简单,下面来看它内部的 set 方法:
public void set(T value) {
// 获取当前线程 t
Thread t = Thread.currentThread();
// 根据当前线程 t,获取当前线程的 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null)
// map 不为空,调用 ThreadLocalMap 的 set() 方法。
map.set(this, value);
else
// map 为空,则为当前线程创建一个新的 ThreadLocalMap 对象
createMap(t, value);
}
在 set 方法中,先获取当前线程,然后获取当前线程的 ThreadLocalMap 对象。getMap() 的 和 createMap() 的实现如下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
那么 ThreadLocalMap 又是什么呢,这里是它的一部分源码:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 初始的 table 容量
private static final int INITIAL_CAPACITY = 16;
// Entry 数组用于存储数据
private Entry[] table;
// table 的大小
private int size = 0;
// 负载因子,用于扩容
private int threshold; // Default to 0
// 设置负载因子为当然容量大小的 2 / 3
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
// 初始化 Entry 数组
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
可以将 ThreadLocalMap 当作一个哈希表,它的内部用 Entry 存储相应的数据。
在 Thread 的属性中有 ThreadLocal.ThreadLocalMap threadLocals = null;
,所以每一个线程内部,都持有一个 ThreadLocalMap 对象,系统才可以通过 getMap()
方法获取当前线程的 ThreadLocalMap 对象。
在 ThreadLocal 中调用 set 方法,实际上会调用 ThreadLocalMap 中的 set 方法,源码如下:
// ThreadLocalMap 的 set 方法
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
// 首先获取当前 ThreadLocal 对象的 table 属性,table 一个 Entry 的数组
// Entry 相当于一个 HashMap,存储了当前 ThreadLocal 对象和 Object 类型的 value 对象
Entry[] tab = table;
int len = tab.length;
// 计算出存储的位置
int i = key.threadLocalHashCode & (len-1);
// 遍历 tab
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// 如果 tab 中已经存在了相同的 key 值,就覆盖它原有的 value
if (k == key) {
e.value = value;
return;
}
// 如果 当前 entrt 的 key 为 null,调用 replaceStaleEntry 方法清楚所有 key 为 null 的数据
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 都不满足,就新建一个 Entry 对象
tab[i] = new Entry(key, value);
int sz = ++size;
// ThreadLocalMap 的容量到达阀值后扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
ThreadLocal 中的 get() 方法和 set() 方法一样,都是对 Thread 中对 ThreadLocalMap 进行操作
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的 ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取 ThreadLocalMap 中对应当前线程的 Entry 对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 将 Entry 对象中的 value 取出来
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
## 6.2)Looper 中的 prepare() 方法
那么 ThreadLocal 和 Looper 有什么关系呢?我们知道每一个线程都有自己的 Looper,Looper 的作用域就是当前的线程,Android 系统中便通过 ThreadLocal 对象来存储不同线程中的 Looper。
Looper 中 prepare() 方法为当前线程创建一个 Looper 对象,我们看一下它的实现:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 将 Looper 对象保存到当前线程的 ThreadLocalMap 当中
sThreadLocal.set(new Looper(quitAllowed));
}
这里再看一下 Looper 的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在一个 Looper 中创建了一个 MessageQueue,这里我们就可以搞清楚 Handler、Looper 和 MessageQueue 的对应关系了:
每个线程都有一个 Looper 对象,在 Looper 对象的初始化过程中,会为当前线程创建一个 MessageQueue,而一个线程中可以有多个 Handler。
## 6.3)Looper 中的 loop() 方法:
prepare() 调用后,就是调用 loop() 方法:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 通过 Thread Local 获取当前线程的 Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取当前 Looper 对象的 MessageQueue
final MessageQueue queue = me.mQueue;
// 清空远程调用端进程的身份,确保此线程的身份是本地进程的身份,并跟踪该身份令牌
// 这里主要用于保证消息处理是发生在当前 Looper 所在的线程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
// 取出来下一条消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 用 logging 打印日志,默认为 null,可通过 setMessageLogging() 方法来指定
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 开始跟踪,并写入跟踪消息,用于 debug 功能
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
...
...
try {
// // 通过 Handler 分发消息
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
// 停止跟踪
Trace.traceEnd(traceTag);
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 确保在分发消息的过程中线程的身份没有改变
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
// 回收消息,并把消息放入消息池
msg.recycleUnchecked();
}
}
可以看到 loop() 方法就是不停的遍历消息队列中的消息,当发现有新的消息时,便调用 Handler 的 dispatchMessage()
方法。
## 6.4)getMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
getMainLooper() 方法用于返回当前 UI 线程的 Looper,UI 线程的 Looper 在 ActivityThread 的建立时通过调用
prepareMainLooper()
方法创建。
## 6.5)quit() 和 quitSafely()
在子线程中,如果手动为其创建了Looper,那么在所有消息处理完成之后应该调用 quit() 方法终止消息循环,不然 Looper 就会一直处于等待状态。
public void quitSafely() {
mQueue.quit(true);
}
public void quit() {
mQueue.quit(false);
}
可以看到这两个方法都调用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的参数为 true,quit 的参数为 false。
void quit(boolean safe) {
// 主线程不退出消息循环
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
// 如果已经退出了,直接 return
if (mQuitting) {
return;
}
// 标记为已经退出
mQuitting = true;
// 如果 safe 的值为 true,执行完当前的消息后退出消息循环
if (safe) {
removeAllFutureMessagesLocked();
} else {
// 直接退出消息循环
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
quitSafely()
会等待当前消息执行完毕后退出消息循环,而 quit()
方法会直接退出消息循环。
private void removeAllMessagesLocked() {
// 获取当前 MessageQueue 的头部消息
Message p = mMessages;
while (p != null) {
// 循环遍历所有的 Message
Message n = p.next;
// 回收消息,并把消息放入消息池
p.recycleUnchecked();
p = n;
}
// 将头部消息置为空
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
// 获取系统从开机到现在到时间
final long now = SystemClock.uptimeMillis();
// 将当前的头部消息赋值给 p
Message p = mMessages;
if (p != null) {
if (p.when > now) {
// 如果当前头部消息将要执行的时间大于系统开机到现在的时间,则执行 removeAllMessagesLocked() 方法
// 清空 MessageQueue 队列
removeAllMessagesLocked();
} else {
Message n;
// 遍历当前的 MessageQueue,直到某个消息的执行时间小于 now 值(即这个消息正在执行)
// 将这个消息的 next 赋值为 null
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
// 回收不会被执行的 Message
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
终于讲完了,希望大家能通过我的文章,彻底理解 Handler 的机制,但我的能力有限,如果存在错误的地方,还请指出。
零碎的东西很多,为了方便大家记忆,我把上面的内容做成了思维导图,需要的朋友可以保存下来,偶尔看一下,帮助自己记忆。
Android 消息机制欢迎关注本文作者:
扫码关注并回复「干货」,获取我整理的千G Android、iOS、JavaWeb、大数据、人工智能等学习资源。