Handler源码详解
- 简介
- 1,消息收发的主控制器 – Handler
- 1.1, 构造方法
- 1.2, 成员变量
- 2.1, 类的主体结构
- 2.2, Message对象的创建
- 4.1, Looper中定义的成员变量
- 4.2, Looper的初始化
- 5.1, 发送消息 – post和send
- 5.2, 移除消息 – removeMessages
- 6.1, 消息的循环获取 – Looper#loop()
- 6.2, 退出消息循环
简介
Handler
作为消息收发的处理器,在Android的多线程调度的使用中应用广泛。
Handler
有两种 主要用途:
- 调度消息和runnables,以在将来执行。
- 为要在不同线程上执行的操作排队。
调度消息 是通过一系列
postXxx()
和
sendXxx()
方法完成的。
-
post
允许我们加入可运行对象队列,以便在接收到消息队列时由消息队列调用。
-
send
允许我们加入一个
Message
对象,该对象包含一个数据束,该数据束将被
Handler
的
handleMessage
方法处理(实现
Handler
的一个子类或使用匿名类并重写
handleMessage
)。
在向Handler提交或发送消息时,可以在消息队列准备就绪时立即允许处理项,也可以在处理项之前指定一个延迟或处理项的绝对时间。后两者允许实现超时、ticks和其他基于时间的行为。
当为应用程序创建一个进程时,它的主线程专门用于运行一个消息队列,该消息队列负责管理顶级应用程序对象(activities、broadcast receivers等)和它们创建的任何窗口。我们可以创建自己的线程,并通过
Handler
与主应用程序线程通信。这是通过像以前一样调用相同的
postXxx()
或
sendXxx()
方法完成的,但是,是从新线程调用的。然后,将在处理程序的消息队列中调度给定的
Runnables
或
Message
,并在适当的时候进行处理。
单独介绍
Handler
没有任何意义,它与
Looper 、Message、MessageQueue
是一套“组合拳”。要想了解
Handler
的实现原理,就不得不先了解他们每个类的主要作用,然后再通过消息的收发结合来看他们之间的相互作用和关系才能更深刻的去理解。
本文将从源码的角度去讲Handler与Looper 、Message、MessageQueue的关系,和他们各自在消息收发的过程中所发挥的作何,以及其主要实现原理。
1,消息收发的主控制器 – Handler
Handler
是消息收发的处理器。它允许发送和处理
Message
和与线程的
MessageQueue
相关联的
Runnable
对象。每个
Handler
实例都与单个线程和该线程的消息队列相关联。当我们创建一个新的
Handler
时,
Handler
将被绑定到创建它的线程的线程/消息队列。从那时起,它将向该消息队列交付消息和runnables,并在它们从消息队列出来时执行它们。那么,Handler长什么样子?又是如何被创建的呢?
1.1, 构造方法
Handler
提供了多个重载的构造方法,其开放可供我们使用的构造方法最终都只指向以下两个。这两个方法都是被标注为
@hide
的,我们无法直接调用。
/*** 对具有指定回调接口的当前线程使用Looper,并设置处理程序是否应该是异步的。* 构造函数将这个处理程序与当前线程的Looper相关联,并接受一个可以处理消息的回调接口。* Handler默认是同步的,除非此构造函数用于创建严格异步的处理程序。** 异步消息表示与同步消息相比不需要全局排序的中断或事件。* 异步消息不受制于由MessageQueue#enqueueSyncBarrier(long)引入的同步障碍。** @param callback 用于处理消息的回调接口,可以为null* @param async 如果为true,处理程序会对发送给它的每个Message或Runnable调用* Message#setAsynchronous(true)。* @hide*/public Handler(@Nullable Callback callback, boolean async) {>>>mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException(\"Can\'t create handler inside thread \" + Thread.currentThread()+ \" that has not called Looper.prepare()\");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}
/*** 使用传入的Looper而不是默认的,并接受一个处理消息的回调接口。还要设置handler是否应该是异步的。* Handlers 默认是同步的,除非此构造函数用于创建严格异步的处理程序。* 异步消息表示与同步消息相比不需要全局排序的中断或事件。异步消息不受同步障碍的影响,例如显示垂直同步。* @hide*/public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}
1.2, 成员变量
从上面构造方法中可以看出,
Handler
中与消息收发相关的主要成员变量有 4个。
final Looper mLooper;final MessageQueue mQueue;final Callback mCallback;final boolean mAsynchronous;
-
Looper
是一个循环器,用于为线程运行消息循环。 -
MessageQueue
是发送的消息的列表。 -
Callback
是一个消息接收回调接口,只有一个抽象方法boolean handleMessage(Message msg)
使用它可以避免创建Handler的子类去处理接收消息。
-
mAsynchronous
是否异步。
2,消息载体 – Message
如果
Handler
是消息收发的处理器,那么
Message
就是承载消息内容的载体,了解
Message
类的结构需要知道以下几个类成员:
2.1, 类的主体结构
/*** 用户定义的消息代码,以便接收者能够识别此消息的内容。* 每个Handler都有自己的消息代码名称空间,因此不必担心Handler与其他Handler发生冲突。*/public int what;// 如果您只需要存储几个整数值,那么arg1和arg2可以做为setData(Bundle) setData()的替代品。public int arg1;public int arg2;/*** 要发送给接收者的任意对象。* 当使用Messenger跨进程发送消息时,只有当它包含框架类的Parcelable(而不是由应用程序实现的类)时,它才能是非空的。* 对于其他数据传输使用setData。* 注意,在android2.2之前,这里不支持Parcelable对象*/public Object obj;// 可选的信使,用以发送对此消息的响应。如何使用它取决于发送方和接收方。public Messenger replyTo;// 标志位,用于标识消息属性(是否设置UID、是在正在使用中、是否异步)/*package*/ int flags;// 具体数据的存储容器,从字符串键到各种Parcelable值的映射。/*package*/ Bundle data;// 控制消息收发的控制器@UnsupportedAppUsage/*package*/ Handler target;// 用于处理消息的回调接口@UnsupportedAppUsage/*package*/ Runnable callback;// 单向链表的下一个节点@UnsupportedAppUsage/*package*/ Message next;// 链表的主体,当前对象代表链表头部private static Message sPool;// 消息池当前的数量,即链表长度private static int sPoolSize = 0;// 消息池最大数量,即链表最大长度private static final int MAX_POOL_SIZE = 50;// 如果为true,回收消息时,若当前消息不可回收时会抛出异常。// 默认true;可通过updateCheckRecycle修改,在5.0之前的版本可修改为falseprivate static boolean gCheckRecycle = true;
以上可知:
-
Message
是一个最大长度限制为50的单向链式结构的消息载体。
-
what
作为当前消息的唯一标识。
-
Message
提供了三种不同的数据存储空间。当消息内容是整形时可使用
org1
和
org2
;当消息内容为单一对象时可以存储于
obj
中;当消息内容为一个或多个
String
键到
Parcelable
值的映射时使用
Bundle
。
2.2, Message对象的创建
Message
提供了一个无参构造方法,但是获取
Message
的首选方法是调用
Message.obtain()
。其目的是为了避免分配过多的新对象,以节省资源。
/*** 从全局池返回一个新的消息实例。允许我们在许多情况下避免分配新对象。*/public static Message obtain() {synchronized (sPoolSync) {// sPool 是一个静态的Message对象。使用静态独立于对象实例之外,保证该实例只存在一个。// 如果链表不为空,移出链表头部节点的Message对象作为新的消息实例并返回,否则就new一个。if (sPool != null) {// Message 本身是一个最大长度为Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // 清除标志sPoolSize--;return m;}}return new Message();}
Message obtain()
存在多个不同参数(类主体结构中的成员变量)的重载方法,本质上都是先调用
obtain()
方法获取实例,然后将传入的参数赋值给实例。
举个通过
Handler
对象获取
Message
对象的例子:
/*** 返回一个新的来自全局消息池的消息message。比创建和分配新实例更有效。*/@NonNullpublic final Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {return Message.obtain(this, what, arg1, arg2, obj);}
3,消息持有者 – MessageQueue
Handler
是消息收发的控制器,
Message
是承载消息内容的载体。但是,
Handler
并不直接持有
Message
对象,而是通过
MessageQueue
间接处理消息。
MessageQueue
是持有要由
Looper
发送的消息列表的类。消息不是直接添加到
MessageQueue
的,而是通过与
Looper
关联的
Handler
对象添加的。
MessageQueue的主体组成:
// 如果消息队列可以退出,则为真。@UnsupportedAppUsageprivate final boolean mQuitAllowed;// 消息链表@UnsupportedAppUsageMessage mMessages;// 空闲处理器回调接口的结合,该接口用于发现线程什么时候会处于阻塞状态,以等待更多消息。@UnsupportedAppUsageprivate final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();// 当发生与文件描述符相关的事件时会调用OnFileDescriptorEventListener监听器// FileDescriptorRecord是一个用于记录文件描述符及其监听器的对象private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;// 待处理的空闲处理器,即挂起处于等待状态下(延迟消息)private IdleHandler[] mPendingIdleHandlers;// 正在退出private boolean mQuitting;// 指示next()是否在pollOnce()中被阻塞。private boolean mBlocked;// 下一个屏障token。屏障由带有空Handler的消息表示,该空目标的arg1字段携带这个token。@UnsupportedAppUsageprivate int mNextBarrierToken;
/*** 回调接口,用于发现线程什么时候会阻塞,以等待更多消息。*/public static interface IdleHandler {/*** 当消息队列耗尽消息并等待更多消息时调用。返回true以保持空闲处理器活跃,返回false以删除它。* 如果队列中仍有挂起的消息,则可以调用此操作,但这些消息都计划在当前时间之后分派。*/boolean queueIdle();}
Handler
持有
MessageQueue
对象,但
MessageQueue
的创建并不在
Handler
中,而是在
Looper
的构造方法中创建。我们在
Handler
中想要获取
MessageQueue
对象,需要通过
mHandler.getLooper().getQueue()
的方式获取。
4,循环器 – Looper
类如其名,
Looper
是一个用于为线程运行消息循环的循环器。默认情况下,线程没有与之关联的消息循环。要创建一个循环,需要在运行循环的线程中调用
Looper#prepare
,然后调用
Looper#loop
让它处理消息,直到循环停止。
与消息循环的大多数交互是通过
Handler
类进行的。
举个例子,这是一个典型的
Looper
线程实现示例,使用
prepare
和
loop
来创建一个初始
Handler
来与
Looper
通信:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler(Looper.myLooper()) {public void handleMessage(Message msg) {// 在这里处理传入的消息}};Looper.loop();}}
该类包含设置和管理基于
MessageQueue
的事件循环所需的代码。影响队列状态的api是在
MessageQueue
和
Handler
上定义的,而不是在
Looper
本身上定义。例如,空闲处理程序和同步障碍是在队列上定义的,而准备线程、循环和退出是在
Looper
上定义的。具体实现我们在后面介绍消息收发的时候讲。
那么,Looper是如何被创建的 呢?
4.1, Looper中定义的成员变量
// sThreadLocal.get() will return null unless you\'ve called prepare().@UnsupportedAppUsagestatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal
是一个只属于当前线程的
Looper
备份。在我们的线程中通过
sThreadLocal.get()
获取
Looper
对象。在调用
prepare()
之前,
sThreadLocal.get()
的返回值为
null
。了解关于ThreadLocal请点击:传送门
// 主线程中的Looper@UnsupportedAppUsageprivate static Looper sMainLooper; // guarded by Looper.class// 该进程中的所有Looper的事务观察器。private static Observer sObserver;@UnsupportedAppUsagefinal MessageQueue mQueue;// 与Looper绑定的当前线程final Thread mThread;/*** 如果消息分发时间超过此时间,则该循环程序将显示警告日志。*/private long mSlowDispatchThresholdMs;/*** 如果消息传递(实际传递时间- post时间)时间超过此时间,则循环器将显示警告日志。*/private long mSlowDeliveryThresholdMs;
4.2, Looper的初始化
/*** 将当前线程初始化为循环器。在实际开始循环之前调用这个方法,且确保在其后调用loop(),* handler的创建应两者之间。结束循环通过调用quit()方法。*/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\");}sThreadLocal.set(new Looper(quitAllowed));}
代码中当
sThreadLocal.get() != null
时会抛出异常,所以
prepare()
在每个线程里只能调用一次。
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
综上可见:ThreadLocal是Thread的线程本地变量,ThreadLocal又拥有Looper的单一备份,Looper中持有当前线程Thread的实例。Looper与Thread就是依靠这种方式建立一一对应关系。
在调用了
prepare()
之后,我们就可以通过
Looper#myLooper()
获取当前线程的
Looper
/*** Return the Looper object associated with the current thread. Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}
5,消息发送
Handler
是消息收发的主控制器,
Message
是消息的载体,
MessageQueue
保存要发送的消息列表。
Looper
是一个循环器,用于为线程运行消息循环。
知道了这四个主角各自扮演的角色和获取方式之后,我们再来看消息是如何发送和移除的。
5.1, 发送消息 – post和send
Handler
中为我们提供了一系列
postXxx()
和
sendXxx()
方法用于消息的发送。他们的任务就是把将要发送的消息加入到消息队列中。
-
post
允许我们加入可运行对象队列,以便在接收到消息队列时由消息队列调用。
-
send
允许我们加入一个
Message
对象,该对象包含一个数据束。
在向
Handler
提交或发送消息时,可以在消息队列准备就绪时立即允许处理项,也可以在处理项之前指定一个延迟或处理项的绝对时间。后两者允许实现超时、ticks和其他基于时间的行为。
所有的
post
方法事实上并没有关于消息发送的具体实现,它是先通过
getPostMessage
获取
Message
对象,再调用
sendMessageAtTime
方法并传入
Message
,所有的
post
和
send
方法最终都走向下面这个方法:
/*** 在绝对时间(以毫秒为单位)uptimeMillis之前,在所有挂起消息之后将消息放入消息队列。* 这个时间是基于android.os.SystemClock#uptimeMillis的。** 在深度睡眠中花费的时间将增加执行的额外延迟。消息将在这个线程的handler的handleMessage方法中收到。** @return 如果消息成功放置到消息队列中,则返回true,否则返回false。* 失败通常是因为处理消息队列的looper退出。* 返回true并不意味着将处理该消息,* 如果在消息的传递时间发生之前退出了循环程序,该消息将被删除。*/public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + \" sendMessageAtTime() called with no mQueue\");Log.w(\"Looper\", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}
所有发送消息的代码最终都会经过
Handler#enqueueMessage
方法,方法中第一行
msg.target = this;
,即,所有被加入到消息队列的Message的target都不为空。如下代码:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
Handler#enqueueMessage
将把消息传递给
MessageQueue#enqueueMessage
方法,以完成对消息的排队。在
MessageQueue
中将对消息按照将要处理的时间先后(
uptimeMillis
)顺序进行排队。
boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {// handler==nullthrow new IllegalArgumentException(\"Message must have a target.\");}if (msg.isInUse()) {// Message的flag中存储使用状态throw new IllegalStateException(msg + \" This message is already in use.\");}synchronized (this) {if (mQuitting) {// 当调用的quit时,mQuitting==trueIllegalStateException 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.markInUse();// 将Message标记为使用中msg.when = when;// 消息将被处理的时间Message p = mMessages;boolean needWake;// 消息链表为空或者当前消息比消息链表中的第一个还早if (p == null || when == 0 || when < p.when) {// 把msg作为新头,如果被阻塞,唤醒事件队列。msg.next = p;mMessages = msg;needWake = mBlocked;} else {// 当前消息插入到队列的中间。通常不需要唤醒事件队列,// 除非队列头部有一个barrier,并且消息是队列中最早的异步消息。needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {// 消息队列遍历完毕或找到msg应该插入的位置(由小到大)时,跳出循环break;}if (needWake && p.isAsynchronous()) {// 如果是异步needWake = false;}}// 完成排队msg.next = p;prev.next = msg;}// 可以假设mPtr != 0,因为mQuitting==false。if (needWake) {// 唤醒标记为mPtr的native代码,何时被堵塞的呢?后面会再讲。nativeWake(mPtr);}}return true;}
至此,新发送的消息就完成了在消息队列中按照处理时间的顺序排队。
5.2, 移除消息 – removeMessages
Handler
中也提供多个
removeXxx()
方法,用于移除尚处在消息队列中排队等候处理的消息。
Handler
会将移除指令交由
Message#removeMessage
处理。
removeMessage
有两个重载方法:
-
void removeMessages(Handler h, int what, Object object)
-
void removeMessages(Handler h, Runnable r, Object object)
两个方法结构一致,区别仅仅在于将代码中
what
换为
runnable
,以前者为例:
void removeMessages(Handler h, int what, Object object) {if (h == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front. 删除前面的所有消息,这是源码中的注释,不是很准确。// 这个循环的作用是:如果消息链表第一个节点符合移除条件,才会进入循环;// 并移除 包括第一个和紧随其后且连续的符合条件的节点。// 一旦发生不符合的节点或链表遍历完毕,循环结束。while (p != null && p.target == h && p.what == what&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;// 回收可能正在使用的消息。在处理队列消息时由MessageQueue和Looper在内部使用。p.recycleUnchecked();p = n;}// Remove all messages after front.删除前面之后的所有消息。// 即,在上面一个循环移除以后,对剩下的链表进行全部遍历,移除其中符合条件的消息。while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && n.what == what&& (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}}
这个方法比较有意思,细心地同学可能已经看出问题了。第二个循环其实就可以完成移除所有符合条件的消息的任务,为什么要多此一举用两个循环来执行移除操作?很抱歉,博主也不清楚为何这么设计!如果有知道的大神,还望不吝赐教!
继续往下看
Message#recycleUnchecked()
:
void recycleUnchecked() {// 将消息标记为在使用中,而该消息仍保存在回收的对象池中。清除所有其他细节。flags = FLAG_IN_USE;>>> //省略部分代码synchronized (sPoolSync) {if (sPoolSize < MAX_POOL_SIZE) {// 消息虽然被移除了但是其对象并没有被回收,只是从MessageQueue的消息队列中移除。// 并被加入到全局的消息池中的头部。在调用Message.obtain()时取出使用。next = sPool;sPool = this;sPoolSize++;}}}
至此,消息已经从消息队列中移除。
6,消息接收
上文说消息发送的方法
post
和
send
的作用是将消息加入到消息队列中并按照处理时间先后顺序排队。通常我们在使用时,只需要实现
Handler
(创建子类或匿名类)的
handleMessage(Message msg)
即可,
msg
就是在我们设定的时间收到的消息:
那么,这些消息又是如何被接受者收到的呢?
6.1, 消息的循环获取 – Looper#loop()
消息被加入到消息队列中之后,会按照时间先后顺序出栈,并发送给接受者。这一过程是在
Looper
的静态方法
loop()
中完成的。
整体实现思想是:创建一个死循环,不断对消息链表进行遍历;取出当前时间刚好需要被处理的消息然后调用
Handler#dispatchMessage
方法分发消息到接收目标;如果当前时间没有消息需要被处理,则此方法处于阻塞状态;直到调用了
Looper#quit
,循环终止。
loop()方法的源码中又很多关于日志打印的代码,剔除这些代码,使代码看起来更清晰。关键代码如下:
/*** 在此线程中运行消息队列。结束循环需要调用quit()。*/public static void loop() {final Looper me = myLooper();// 获取当前线程的Looperif (me == null) {throw new RuntimeException(\"No Looper; Looper.prepare() wasn\'t called on this thread.\");}final MessageQueue queue = me.mQueue;>>>for (;;) {Message msg = queue.next(); // 这个方法可能会堵塞if (msg == null) {// 没有消息表明消息队列退出,关键点在12行的queue.next()中。下面讲return;}>>>try {// 派发消息msg.target.dispatchMessage(msg);>>>} catch (Exception exception) {>>>throw exception;} finally {>>>}>>>msg.recycleUnchecked();}}
删除不相关代码后,
loop()
就很好理解了,主要代码只有两行:第12行的
MessageQueue.next()
和第20行的
Handler#dispatchMessage(msg)
。
先来看
MessageQueue.next()
是怎么会一回事,为什么他会堵塞?什么时候堵塞?为什么返回null意味着退出?
直接从源码中逐行解释:
Message next() {// 如果消息循环已经退出并被释放,则此处返回为空,loop()循环结束。// 如果应用程序在退出后试图重新启动一个不支持的循环程序,就会出现ptr == 0情况。final long ptr = mPtr;if (ptr == 0) {// 此时符合退出的条件,返回nullreturn null;}// 待处理的闲置处理器数量,只有在第一次迭代时是-1int pendingIdleHandlerCount = -1;// 下一个将要被出栈分派的消息的分派时间int nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {// 意味着nextPollTimeoutMillis之后,将有消息需要被分派,在此之前将会堵塞。// 下面这个方法的主要作用是将当前线程中挂起的绑定命令刷新到内核驱动程序。// 在执行可能阻塞很长时间的操作之前调用这个函数,可以确保释放所有挂起的对象引用,// 以防止进程持有对象的时间超过需要。Binder.flushPendingCommands();}// 堵塞标识码为ptr的native代码,nextPollTimeoutMillis之后自动唤醒nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// 尝试检索下一条消息。如果发现就返回。final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;// target为空实际上是一种异常现象。从Handler#enqueueMessage方法可以知道,// 所有消息队列中的消息target都是被赋值了的,且只有在回收消息时才被置空。// 假如target==null,此方法返回一个msg之后,在loop()中调用msg.target.dispatchMessage时会抛出异常。if (msg != null && msg.target == null) {//// 这个循环用于查找队列中的下一个异步消息。do {prevMsg = msg;msg = msg.next;// msg != null意味着链表还未遍历完毕,!msg.isAsynchronous()意味着这条消息不是异步的} while (msg != null && !msg.isAsynchronous());}if (msg != null) {// 找到的下一条异步消息if (now < msg.when) {// 还没到需要分派这条消息的时间。设置一个超时以在它准备好时唤醒。nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 已经到了需要分派这条消息的时间,不堵塞。// 这个mBlocked就是enqueueMessage中是否唤醒native的一个条件,// mBlocked就是在这里被置为falsemBlocked = false;// 只有当前面出现过消息的msg.target == null的情况时,prevMsg 才不为空if (prevMsg != null) {// 将msg从链表中移出prevMsg.next = msg.next;} else {// 因为链表已经按时间排序,直接移出第一条消息mMessages = msg.next;}msg.next = null;if (DEBUG) Log.v(TAG, \"Returning message: \" + msg);msg.markInUse();// 返回这条消息return msg;}} else {// 消息队列中没有消息了。nextPollTimeoutMillis = -1;}if (mQuitting) {// 此方法中如果mPtr!=0,调用nativeDestroy(mPtr)销毁本地代码标记,并将mPtr置为0dispose();// 此时为退出,所有返回nullreturn null;}// 如果第一次空闲,则得到运行的空闲组的数量。// 空闲句柄仅在队列为空或队列中的第一个消息(可能是barrier)将在未来被处理时运行。if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) {pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) {// 没有要运行的空闲处理程序。循环并等待更多的时间。mBlocked = true;continue;}if (mPendingIdleHandlers == null) {mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);}// 运行空闲处理程序。只能在第一次迭代中到达这个代码块。for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // 释放对处理程序的引用boolean keep = false;try {keep = idler.queueIdle();} catch (Throwable t) {Log.wtf(TAG, \"IdleHandler threw exception\", t);}if (!keep) {synchronized (this) {mIdleHandlers.remove(idler);}}}// 将空闲处理程序计数重置为0,这样我们就不再运行它们了。pendingIdleHandlerCount = 0;// 在调用空闲处理程序时,可能已经传递了一条新消息,// 因此应返回并再次查找未挂起的消息,而无需等待。nextPollTimeoutMillis = 0;}}
上面的代码为我们完美呈现了消息从队列中到按照它应有的时机取出的全部过程。
Handler#dispatchMessage
则负责分派消息:
/*** 处理系统消息。*/public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}
如果我们在创建
Handler
时加入了
Runnable
,就会调用
Runnable#run()
,我们可以在
run
中做相应的处理;如果设置了
CallBack
,消息将由
CallBack#handleMessage(msg)
接收并处理;否则将交由
Handler
为我们默认创建的空方法
Handler#handleMessage(msg)
中。
至此,一条消息从发出到接收的全部过程就讲完了。
6.2, 退出消息循环
退出共有两种模式,安全和非安全。区别在于:安全模式下,会将所有已经正在发送的消息全部发送完毕才真正退出,而非安全模式下,不论当前是否有正在发送的消息都直接全部退出。
所有等待派送和正在派送中的消息都仍然处于消息队列中,只有当消息成功从消息队列中取出并即将交由接收方接收时,消息才会被从消息队列中移除。
MessageQueue#quit:
void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException(\"Main thread not allowed to quit.\");}synchronized (this) {if (mQuitting) {// 如果已经正在退出中,直接return,无需重复退出。return;}mQuitting = true;if (safe) {removeAllFutureMessagesLocked();} else {removeAllMessagesLocked();}nativeWake(mPtr);// 唤醒可能的堵塞}}
private void removeAllFutureMessagesLocked() {final long now = SystemClock.uptimeMillis();Message p = mMessages;if (p != null) {// 如果链表中的第一条消息的处理时间大于当前时间,则当前没有正在派送的消息if (p.when > now) {// 直接遍历全部消息队列,并回收。removeAllMessagesLocked();} else {// 当前队列第一条数据处于正在派发的状态中Message n;for (;;) {// 循环遍历消息队列n = p.next;if (n == null) {// 遍历完毕直接退出方法return;}if (n.when > now) {// 找到链表中第一条尚在等待发送的消息时,退出循环break;}p = n;}// 将第一条尚在等待发送的消息以及之后的消息全部回收清除。// 之前的消息不做处理,即正处于派发状态的消息仍可继续传达给接收者。p.next = null;do {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}}
removeAllMessagesLocked
直接遍历全部消息队列,并回收。
private void removeAllMessagesLocked() {Message p = mMessages;while (p != null) {Message n = p.next;p.recycleUnchecked();p = n;}mMessages = null;}
7,主线程中的Looper
我们知道,在线程中使用Handler发送消息,就必须有与县城对应的Looper为其消息队列不断轮询以获取待发送的消息。通常在使用中,我们可以这样写:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler(Looper.myLooper()) {public void handleMessage(Message msg) {// 在这里处理传入的消息}};Looper.loop();}}... ...new LooperThread().start();
可是我记得平时写代码在
Activity
中创建一个
Handler
,然后在需要的地方直接调用
post
或
send
就行了,并没有使用过
Looper
啊!那是因为
Activity
都是运行在主线程中的,在
Activity
中创建
Handler
即在主线程中创建,主线程中已经为我们创建好一个
Looper
了。这个创建在类
android.app.ActivityThread
中。
public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, \"ActivityThreadMain\");// Install selective syscall interceptionAndroidOs.install();// CloseGuard defaults to true and can be quite spammy. We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0(\"<pre-initialized>\");Looper.prepareMainLooper();// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.// It will be in the format \"seq=114\"long startSeq = 0;if (args != null) {for (int i = args.length - 1; i >= 0; --i) {if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {startSeq = Long.parseLong(args[i].substring(PROC_START_SEQ_IDENT.length()));}}}ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, \"ActivityThread\"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException(\"Main thread loop unexpectedly exited\");}
第20行和第47行。
/*** Initialize the current thread as a looper, marking it as an* application\'s main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself. See also: {@link #prepare()}*/public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException(\"The main Looper has already been prepared.\");}sMainLooper = myLooper();}}
总结
Handler
作为消息收发的处理器,在
Android
的多线程调度的使用中应用广泛。
Handler
是消息收发的主控制器,
Message
是消息的载体,
MessageQueue
保存要发送的消息列表。
Looper
是一个循环器,用于为线程运行消息循环。
-
Handler
通过一系列
postXxx()
和
sendXxx()
方法将要发送的消息(
Message
)加入到消息队列中。
-
MessageQueue
中维护着
Message
链表,消息的加入和移除都在这里完成。
MessageQueue
会对新加入队列中的消息按照其将要被处理的时间按先后排序,最先被处理的位于链表头。
-
Looper
为消息队列执行一个循环器,不断地从消息队列中检查是否有需要立即进行处理的消息。如果有则移除消息,并将其最终交由
Handler
的
handleMessage
方法(或我们传入的
CallBack
、
Runnable
)处理;如果没找到当前需要被处理的消息或消息队列为空时,循环器堵塞(通过
native
实现),知道有新的消息被加入时唤醒。