深入研究Android Handler机制
2015-05-04 23:43
453 查看
http://www.xuebuyuan.com/705712.html
项目终于没那么忙了!闲下来几天,想想应该学点什么,总结点什么。总体上来,要学的东西实在太多了,看了看自己写的代码,结果发现连最基本的消息机制都没有了解清楚,虽然一直在用Handler发消息(Message),但一直没有去探究它们内部是如何运作的。于是花了一天的时间仔细分析了一下几个基本类的源码,略有所悟,浅析一下。
在看源码前,我们先需要熟悉一下它们的概念及作用。
Message:用于封装消息的简单数据结构。里面包含消息的ID、数据对象、处理消息的Handler引用和Runnable等。
Handler:消息的发送者和最终消息处理者。
MessageQueue:消息队列,提供消息的添加、删除、获取等操作来管理消息队列。
Looper:用于建立消息循环并管理消息队列(MessageQueue),不停的从消息队列中抽取消息,分发下去并执行。
注:以下分析均以 android 2.3.3 源码为基础。
成员变量
我们先看一下它的成员变量。
[java]
view plaincopy
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
......
}
简单参数就不需要解释了,重点在以下几个成员变量。
long when:该消息何时被处理的绝对时间戳。
Handler target:谁来处理该消息。如果它为空,那说明该消息可能被recycle掉了,存放在Message Pool中,或者,它代表一个QUIT消息。
Runnable callback:Runnable对象,如果为该Message设置了该对象,那么有优先执行它。这里需要看Handler的消息处理机制。在分析Handler时再提。
Message next:这个看起来有点奇怪,有种似曾相识的感觉,想想,到底什么情况。哦,想起来了,就是C语言里面链表的数据结构。
[cpp]
view plaincopy
typedef struct LNode{
ElemType data;
Struct LNode* next;
}
由于J***A是没有指针这个概念的,所以内部维护了一个next的引用。所以,实际上,Message本身不单纯是一个简单的只包含数据的类,它实际上是一个链式结构的类,也就是说,一个Message本身就是一个消息队列,它通过next将所有消息串联起来。既然Message本身就是消息队列,那MessageQueue又是如何建立消息队列的又是怎么回事?实际上,MessageQueue内部只有一个Message成员,它所要做的工作就是把Message实体串连起来,形成消息链。
接着再看静态成员变量:
[java]
view plaincopy
private static Object mPoolSync = new Object(); // 用于访问mPool时进行同步操作
private static Message mPool; // 全局的废弃消息池(链)(就是废品收购站)
private static int mPoolSize = 0; // 消息池当前大小
private static final int MAX_POOL_SIZE = 10; // 消息池上限值
从这上面能看出,有个叫mPool的Message对象,如果理解了Message本身就是链表结构,那么,应该就明白了为什么一个消息叫Pool(池),因为一个Message本身就代表着一群Message,通过next把一系列Message给串联起来。对于无数个message实体来说,他们共享同一个全局的消息池(链),里面存放废弃掉的message。很明显,这是在做缓存机制。
在该类中,核心函数有:
[java]
view plaincopy
public static Message obtain()及系列函数
public void recycle()
public void sendToTarget()
obtain()及其系列函数
obtain()系列函数最核心的函数就只有obtain()方法,其它函数只不过提供了更多的可选参数,内部都是调用obtain()方法,因此,我们只需要关注核心函数的实现即可。
[java]
view plaincopy
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (mPoolSync) {
if (mPool != null) {
Message m = mPool;
mPool = m.next;
m.next = null;
return m;
}
}
return new Message();
}
该函数内部首先是从全局的废弃消息池(链)中去取,看看有没有废弃掉的Message,如果有,那我们就获取消息链中第一个废弃掉的Message,这样,就无需再创建一个新的Message;如果消息池中没有,那就只能new一个新的消息出来。这样做的好处就是废物再利用,减少创建时间。实际上,这种思想很值得我们借鉴。对于其它重载版的obtain方法,内部都是先调用它,然后再使用其它额外的参数进行填充的。如:
[java]
view plaincopy
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
recycle()函数
[java]
view plaincopy
/**
* Return a Message instance to the global pool. You MUST NOT touch
* the Message after calling this function -- it has effectively been
* freed.
*/
public void recycle() {
synchronized (mPoolSync) {
if (mPoolSize < MAX_POOL_SIZE) {
clearForRecycle();
next = mPool;
mPool = this;
}
}
}
其中:clearForRecycle代码如下
[java]
view plaincopy
/*package*/ void clearForRecycle() {
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
这个函数首先是看当前消息池中废弃个数已达上限(池子是不是满了),如果没有达到上限,则调用clearForRecycle()函数把当前消息的各种信息清空,然后添加到消息链的头部。注意:该函数的if (mPoolSize < MAX_POOL_SIZE)实际上是没有起到任何作用的,搜遍Message所有代码也没有发现mPoolSize的值有任何变化,始终为0,也就是说,这句话是恒成立的。只要该Message被recycle掉,那他就会加入到废弃链中。
可以用以下图示表示该过程:
值得说明一点的是,该recycle()函数何时被调用?有以下两个时机被调用:
MessageQueue类中的removeMessages(...)及其系列函数,即当我们要从消息队列中干掉一个Message时,该Message被回收到废弃消息链。
[java]
view plaincopy
final boolean removeMessages(Handler h, int what, Object object, boolean doRemove) {
synchronized (this) {
Message p = mMessages;
boolean found = false;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
if (!doRemove) return true;
found = true;
Message n = p.next;
mMessages = n;
p.recycle();
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)) {
if (!doRemove) return true;
found = true;
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
return found;
}
}
Looper类中的loop函数。即当我们使用完了某个Message后,该Message被回收到废弃消息链。
[java]
view plaincopy
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
sendToTarget()函数
[java]
view plaincopy
public void sendToTarget() {
target.sendMessage(this);
}
该函数比较简单,就是通过Message内部引用的Handler将消息发送出去。
成员变量
我们先看一下它的成员变量:
[java]
view plaincopy
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
Handler作为一个管理者,其重要做用就是创建并发送消息,最后再处理消息。
发送消息即为把指定的Message放入到消息队列中,等到合适的时机,消息泵从消息队列中抽取消息,再分发下去,进行处理。
因此,在Handler中,有必要维护当前线程的MessageQueue和Looper的引用。对于一个线程来说,MessageQueue和Looper都是唯一的,而多个handler是可以共享同一个线程的MessageQueue和Looper的引用。
Handler里面有以下几类核心函数共同完成上面的功能。
构造函数
创建消息函数
发送消息函数
移除消息函数
消息分发及处理函数
构造函数
构造函数主要是对成员变量进行初始化,获取线程中的Looper、MessageQueue等对象。
[java]
view plaincopy
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
默认构造函数通过Looper.myLooper()函数从当前线程中获取Looper对象,如果Looper对象不存在,那么Handler构造就失败了,会抛出RuntimeException,而MessageQueue是由Looper对象创建出来的,因此,mQueue直接便能从Looper中获取。对于UI线程,在程序初始化时,实际上looper对象就已被创建出来(通过调用Looper.prepare()进行创建,并把looper对象存放到一个静态的sThreadLocal中),因此,正常情况下,当我们new出来的Handler不指明任何参数时,实际上就是会默认关联到UI线程。但是,但如果该对象是在某个线程的Run方法中被创建出来,那么它会被关联到该后台线程。“关联到该线程”的意思实际上就是,当Handler关联到UI线程,那最终发送的消息是加到了UI线程的消息队列,如果它关联到后台线程,则发送的消息加到了后台线程的消息队列。下面的这种方式,mHandler会被直接关联到指定的线程。
[java]
view plaincopy
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
});
创建消息函数:obtainMessage()及其系列函数
[java]
view plaincopy
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
这些函数都很简单,无非是通过Message.obtain(…)方法创建消息。obtain方法已在Message类中进行相关说明。还记得obtain方法的调用过程吗?忘记了的请回过头再看看。
发送消息函数
发送的过程是由post其系列函数和send系列函数进行的。如下:
[java]
view plaincopy
post(Runnable)
postAtTime(Runnable, long)
postAtTime(Runnable, Object, long)
postDelayed(Runnable, long)
postAtFrontOfQueue(Runnable)
sendMessage(Message)
sendEmptyMessage(int)
sendEmptyMessageDelayed(int, long)
sendEmptyMessageAtTime(int, long)
sendMessageDelayed(Message, long)
sendMessageAtTime(Message, long)
sendMessageAtFrontOfQueue(Message)
先说sendXXXMessage系列函数,这些函数提供了很多可选接口,主要是可使用delayMillis。如sendMessageDelayed(Message msg, long delayMillis),该函数并不是说把消息延迟xxx毫秒后发送,而是延迟将消息分发下来。即消息加入消息队列后,message会隔xxx毫秒后从消息队列中被取出来执行。
这些函数最终都是调用的sendMessageAtTime函数。
[java]
view plaincopy
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
该方法就是调用MessageQueue的enqueueMessage(…)方法将指定的Message插入到消息队列中去,即加入Message链,并指明何时应该从消息队列中取出来执行。其中uptimeMillis就是绝对时间戳,uptimeMillis = current time + delayMillis。
postXXX系列函数来说,需要指定一个Runnable对象,在合适的时间执行。
实际上,他们最终调用的还是sendMessageAtTime函数,只不过中间多了一步,即根据Runnable创建Message对象。如:
[java]
view plaincopy
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
其中,getPostMessage代码如下:就是通过obtain方法获取一个Message,并设置其callback。
[java]
view plaincopy
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
移除消息系列函数
[java]
view plaincopy
removeCallbacks(Runnable)
removeCallbacks(Runnable, Object)
removeCallbacksAndMessages(Object)
removeMessages(int)
removeMessages(int, Object)
这些函数的作用就是从当前消息队列中移除掉所有指定ID或指定Runnable对象的Message。如:
[java]
view plaincopy
public final void removeCallbacks(Runnable r)
{
mQueue.removeMessages(this, r, null);
}
上面的所有函数完成了第一个功能,即消息的发送,并加入到消息队列中去。
但发送完后,并不是立即就能执行,当消息从消息泵中被取出来后,才行执行。因此,消息的发送和处理实际上是一个异步的过程。
消息分发及处理函数
当消息从消息泵中抽取出来后,就会进行消息的分发。
消息抽取的过程需要参见Looper的核心处理函数:
[java]
view plaincopy
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
<strong> </strong>msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
从上面可以看出,loop函数本身就是一个回路(死循环),不停的调用queue.next()函数从消息链中取出消息(如果取不到消息就会被阻塞住)。然后通过MSG的target成员变量(Handler)来调用其dispatchMessage方法将消息分发下去,然后执行消息处理函数。
[java]
view plaincopy
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在消息的分发的过程中,其执行是有优先级的:
如果Message中包含callback(即通过post系列函数设置的Runnable对象),那么它会被优先执行。
否则,如果给当前的Handler设置了mCallback,那么它会优先执行。如果该方法返回true,分发结束,处理完毕。如果返回false,那么他还有机会执行默认的handleMessage函数。
以下是三种情况的示例代码:
[java]
view plaincopy
1.
Handler mHandler = new Handler();
mHandler.post(new Runnable() {
@Override
public void run() {
// TODO something
}
});
2.
Handler mHandler = new Handler(new Handler.Callback()
{
@Override
public boolean handleMessage(Message msg) {
// TODO something
return false;
}
});
3.
protected Handler m_handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO something
}
});
成员变量
我们先看一下它的成员变量
[java]
view plaincopy
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuiting;
boolean mQuitAllowed = true;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
mMessages:在最初不了解Message类时,以为MessageQueue里面,存放的是一个类似于LinkedList<Message>的数据结构。当了解Message数据结构后,才知道MessageQueue里面维护的只有一个Message,因为Message本身就能构建一个Message链。MessageQueue主要功能就是维护这个Message链,如插入和删除Message链中的元素,并提供获取Message链中next Message的方法。这里,mMessages始终指向的是消息链的第一个节点,即头节点。
mIdleHandlers:外部注册的回调列表(listeners)。如果当前消息队列已没有新的Message能被取出来时,线程即将被阻塞前被调用。即线程处于空闲时间时,被调用。
mPendingIdleHandlers:该成员变量配合上面的mIdleHandlers使用,我觉得它没有必要保存起来,完全用一个临时变量即可。
mQuiting:当前消息队列是否已准备退出。实际上,如果它为True,也就表明当前线程将会立马结束掉。
mQuitAllowed:是否允许退出消息队列。对于主线程(UI线程),该标志量为true。
mBlocked:标志当前消息队列是否处于阻塞状态。
下面的接口定义了线程处于空闲状态时的回调函数。由此可以看出,当你想在线程不忙的时候干点其它事情的话,这个接口就能派得上用场了。
[java]
view plaincopy
public static interface IdleHandler {
boolean queueIdle();
}
以下方法用来注册和移除线程处于空闲状态时的回调函数。
[java]
view plaincopy
public final void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public final void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
再来看一看MessageQueue的两个核心函数:enqueueMessage(…)与next()函数。
enqueueMessage(Message msg, long when)函数
该函数的目的就是把指定的Message按照绝对时间插入到当前的消息队列中去。还记得该函数是在哪里被调用的吗?请回过头看看Handler的源代码:sendMessageAtTime(Message
msg, long uptimeMillis)
[java]
view plaincopy
final boolean enqueueMessage(Message msg, long when) {
// 1. 如果MSG的绝对时间戳不为0(说明已被初始化,并已加入到队列中),抛出异常
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
// 2. 如果MSG的target为null,说明该消息是QUIT消息。如果此时又发现mQuitAllowed为false,则抛出异常。
// 实际上,如果你调用主线程的Looper.quit()方法,你会发现该异常会被抛出来。因为主线程的消息循环是不允许退出的。
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
// 3. 如果消息队列正在退出,则直接返回false;否则,查看当前要加入的MSG是否是要求退出消息队列的MSG
//(QUIT MSG:判断依据是MSG的target是否为空),如果是,则将mQuiting设置为true,然后把该消息加入到消息
// 队列的头部,以保证下一次通过next()函数取出的消息就是QUIT消息,能快速的终止掉线程。
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
// 4. MSG最终从消息队列中取出来的绝对时间戳。NOTE:MSG的when字段只有在这一个地方被主动设置过。
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
// NOTE:以下部分就是真正如何将消息插入消息链的过程。
Message p = mMessages;
// 5. 如果当前的消息链为空,或者要插入的MSG为QUIT消息,或者要插入的MSG时间小于消息链的第一个消息。
// 那么,强势插入到消息链的头部。显示,消息链的头部被改变了,变成了新添加的消息。needWake需要根据
// mBlocked的情况考虑是否触发。
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 6. 否则,我们需要遍历该消息链,将该MSG插入到合适的位置。整个消息链是按消息被取出的绝对时间戳
// 由小到大链接起来的。时间轴为:msg1.when <= msg2.when <= msg3.when <= msg4.when <= msg5.when……
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
// 7. 对于needWake,如果该变量为true,说明mBlocked为true,即当前线程处于阻塞状,也即nativePollOnce处于阻塞状态。但此时,
// 我们已通过这个enqueueMessage方法已经向消息链中添加了一个消息,也就是说,此时我们需要把阻塞状态变成非阻塞状态,
// 让next()函数能够取到MSG。怎么办?通过执行nativeWake方法,便能触发nativePollOnce函数结束等待。实际上,
// nativePollOnce和nativeWake内部是通过管道的机制来实现阻塞和接触阻塞的。我的理解是: nativePollOnce函数从管道中
// 读数据,如果发现管道中有数据,则立即返回,否则,一直等待。而nativeWake就是向管道中写数据,只要往管道的另一端写数据,
// 则nativePollOnce就能立马从管道中读出数据来,从而变成非阻塞状态。(请参考:http://book.51cto.com/art/201208/353352.htm)<
深入研究Android Handler机制
项目终于没那么忙了!闲下来几天,想想应该学点什么,总结点什么。总体上来,要学的东西实在太多了,看了看自己写的代码,结果发现连最基本的消息机制都没有了解清楚,虽然一直在用Handler发消息(Message),但一直没有去探究它们内部是如何运作的。于是花了一天的时间仔细分析了一下几个基本类的源码,略有所悟,浅析一下。
相关概念
在看源码前,我们先需要熟悉一下它们的概念及作用。Message:用于封装消息的简单数据结构。里面包含消息的ID、数据对象、处理消息的Handler引用和Runnable等。
Handler:消息的发送者和最终消息处理者。
MessageQueue:消息队列,提供消息的添加、删除、获取等操作来管理消息队列。
Looper:用于建立消息循环并管理消息队列(MessageQueue),不停的从消息队列中抽取消息,分发下去并执行。
注:以下分析均以 android 2.3.3 源码为基础。
Message源码分析
成员变量
我们先看一下它的成员变量。
[java]
view plaincopy
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
......
}
简单参数就不需要解释了,重点在以下几个成员变量。
long when:该消息何时被处理的绝对时间戳。
Handler target:谁来处理该消息。如果它为空,那说明该消息可能被recycle掉了,存放在Message Pool中,或者,它代表一个QUIT消息。
Runnable callback:Runnable对象,如果为该Message设置了该对象,那么有优先执行它。这里需要看Handler的消息处理机制。在分析Handler时再提。
Message next:这个看起来有点奇怪,有种似曾相识的感觉,想想,到底什么情况。哦,想起来了,就是C语言里面链表的数据结构。
[cpp]
view plaincopy
typedef struct LNode{
ElemType data;
Struct LNode* next;
}
由于J***A是没有指针这个概念的,所以内部维护了一个next的引用。所以,实际上,Message本身不单纯是一个简单的只包含数据的类,它实际上是一个链式结构的类,也就是说,一个Message本身就是一个消息队列,它通过next将所有消息串联起来。既然Message本身就是消息队列,那MessageQueue又是如何建立消息队列的又是怎么回事?实际上,MessageQueue内部只有一个Message成员,它所要做的工作就是把Message实体串连起来,形成消息链。
接着再看静态成员变量:
[java]
view plaincopy
private static Object mPoolSync = new Object(); // 用于访问mPool时进行同步操作
private static Message mPool; // 全局的废弃消息池(链)(就是废品收购站)
private static int mPoolSize = 0; // 消息池当前大小
private static final int MAX_POOL_SIZE = 10; // 消息池上限值
从这上面能看出,有个叫mPool的Message对象,如果理解了Message本身就是链表结构,那么,应该就明白了为什么一个消息叫Pool(池),因为一个Message本身就代表着一群Message,通过next把一系列Message给串联起来。对于无数个message实体来说,他们共享同一个全局的消息池(链),里面存放废弃掉的message。很明显,这是在做缓存机制。
在该类中,核心函数有:
[java]
view plaincopy
public static Message obtain()及系列函数
public void recycle()
public void sendToTarget()
obtain()及其系列函数
obtain()系列函数最核心的函数就只有obtain()方法,其它函数只不过提供了更多的可选参数,内部都是调用obtain()方法,因此,我们只需要关注核心函数的实现即可。
[java]
view plaincopy
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (mPoolSync) {
if (mPool != null) {
Message m = mPool;
mPool = m.next;
m.next = null;
return m;
}
}
return new Message();
}
该函数内部首先是从全局的废弃消息池(链)中去取,看看有没有废弃掉的Message,如果有,那我们就获取消息链中第一个废弃掉的Message,这样,就无需再创建一个新的Message;如果消息池中没有,那就只能new一个新的消息出来。这样做的好处就是废物再利用,减少创建时间。实际上,这种思想很值得我们借鉴。对于其它重载版的obtain方法,内部都是先调用它,然后再使用其它额外的参数进行填充的。如:
[java]
view plaincopy
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
recycle()函数
[java]
view plaincopy
/**
* Return a Message instance to the global pool. You MUST NOT touch
* the Message after calling this function -- it has effectively been
* freed.
*/
public void recycle() {
synchronized (mPoolSync) {
if (mPoolSize < MAX_POOL_SIZE) {
clearForRecycle();
next = mPool;
mPool = this;
}
}
}
其中:clearForRecycle代码如下
[java]
view plaincopy
/*package*/ void clearForRecycle() {
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
这个函数首先是看当前消息池中废弃个数已达上限(池子是不是满了),如果没有达到上限,则调用clearForRecycle()函数把当前消息的各种信息清空,然后添加到消息链的头部。注意:该函数的if (mPoolSize < MAX_POOL_SIZE)实际上是没有起到任何作用的,搜遍Message所有代码也没有发现mPoolSize的值有任何变化,始终为0,也就是说,这句话是恒成立的。只要该Message被recycle掉,那他就会加入到废弃链中。
可以用以下图示表示该过程:
值得说明一点的是,该recycle()函数何时被调用?有以下两个时机被调用:
MessageQueue类中的removeMessages(...)及其系列函数,即当我们要从消息队列中干掉一个Message时,该Message被回收到废弃消息链。
[java]
view plaincopy
final boolean removeMessages(Handler h, int what, Object object, boolean doRemove) {
synchronized (this) {
Message p = mMessages;
boolean found = false;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
if (!doRemove) return true;
found = true;
Message n = p.next;
mMessages = n;
p.recycle();
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)) {
if (!doRemove) return true;
found = true;
Message nn = n.next;
n.recycle();
p.next = nn;
continue;
}
}
p = n;
}
return found;
}
}
Looper类中的loop函数。即当我们使用完了某个Message后,该Message被回收到废弃消息链。
[java]
view plaincopy
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
sendToTarget()函数
[java]
view plaincopy
public void sendToTarget() {
target.sendMessage(this);
}
该函数比较简单,就是通过Message内部引用的Handler将消息发送出去。
Handler源码分析
成员变量我们先看一下它的成员变量:
[java]
view plaincopy
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
Handler作为一个管理者,其重要做用就是创建并发送消息,最后再处理消息。
发送消息即为把指定的Message放入到消息队列中,等到合适的时机,消息泵从消息队列中抽取消息,再分发下去,进行处理。
因此,在Handler中,有必要维护当前线程的MessageQueue和Looper的引用。对于一个线程来说,MessageQueue和Looper都是唯一的,而多个handler是可以共享同一个线程的MessageQueue和Looper的引用。
Handler里面有以下几类核心函数共同完成上面的功能。
构造函数
创建消息函数
发送消息函数
移除消息函数
消息分发及处理函数
构造函数
构造函数主要是对成员变量进行初始化,获取线程中的Looper、MessageQueue等对象。
[java]
view plaincopy
/**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
默认构造函数通过Looper.myLooper()函数从当前线程中获取Looper对象,如果Looper对象不存在,那么Handler构造就失败了,会抛出RuntimeException,而MessageQueue是由Looper对象创建出来的,因此,mQueue直接便能从Looper中获取。对于UI线程,在程序初始化时,实际上looper对象就已被创建出来(通过调用Looper.prepare()进行创建,并把looper对象存放到一个静态的sThreadLocal中),因此,正常情况下,当我们new出来的Handler不指明任何参数时,实际上就是会默认关联到UI线程。但是,但如果该对象是在某个线程的Run方法中被创建出来,那么它会被关联到该后台线程。“关联到该线程”的意思实际上就是,当Handler关联到UI线程,那最终发送的消息是加到了UI线程的消息队列,如果它关联到后台线程,则发送的消息加到了后台线程的消息队列。下面的这种方式,mHandler会被直接关联到指定的线程。
[java]
view plaincopy
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
});
创建消息函数:obtainMessage()及其系列函数
[java]
view plaincopy
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
这些函数都很简单,无非是通过Message.obtain(…)方法创建消息。obtain方法已在Message类中进行相关说明。还记得obtain方法的调用过程吗?忘记了的请回过头再看看。
发送消息函数
发送的过程是由post其系列函数和send系列函数进行的。如下:
[java]
view plaincopy
post(Runnable)
postAtTime(Runnable, long)
postAtTime(Runnable, Object, long)
postDelayed(Runnable, long)
postAtFrontOfQueue(Runnable)
sendMessage(Message)
sendEmptyMessage(int)
sendEmptyMessageDelayed(int, long)
sendEmptyMessageAtTime(int, long)
sendMessageDelayed(Message, long)
sendMessageAtTime(Message, long)
sendMessageAtFrontOfQueue(Message)
先说sendXXXMessage系列函数,这些函数提供了很多可选接口,主要是可使用delayMillis。如sendMessageDelayed(Message msg, long delayMillis),该函数并不是说把消息延迟xxx毫秒后发送,而是延迟将消息分发下来。即消息加入消息队列后,message会隔xxx毫秒后从消息队列中被取出来执行。
这些函数最终都是调用的sendMessageAtTime函数。
[java]
view plaincopy
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
该方法就是调用MessageQueue的enqueueMessage(…)方法将指定的Message插入到消息队列中去,即加入Message链,并指明何时应该从消息队列中取出来执行。其中uptimeMillis就是绝对时间戳,uptimeMillis = current time + delayMillis。
postXXX系列函数来说,需要指定一个Runnable对象,在合适的时间执行。
实际上,他们最终调用的还是sendMessageAtTime函数,只不过中间多了一步,即根据Runnable创建Message对象。如:
[java]
view plaincopy
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
其中,getPostMessage代码如下:就是通过obtain方法获取一个Message,并设置其callback。
[java]
view plaincopy
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
移除消息系列函数
[java]
view plaincopy
removeCallbacks(Runnable)
removeCallbacks(Runnable, Object)
removeCallbacksAndMessages(Object)
removeMessages(int)
removeMessages(int, Object)
这些函数的作用就是从当前消息队列中移除掉所有指定ID或指定Runnable对象的Message。如:
[java]
view plaincopy
public final void removeCallbacks(Runnable r)
{
mQueue.removeMessages(this, r, null);
}
上面的所有函数完成了第一个功能,即消息的发送,并加入到消息队列中去。
但发送完后,并不是立即就能执行,当消息从消息泵中被取出来后,才行执行。因此,消息的发送和处理实际上是一个异步的过程。
消息分发及处理函数
当消息从消息泵中抽取出来后,就会进行消息的分发。
消息抽取的过程需要参见Looper的核心处理函数:
[java]
view plaincopy
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
<strong> </strong>msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
从上面可以看出,loop函数本身就是一个回路(死循环),不停的调用queue.next()函数从消息链中取出消息(如果取不到消息就会被阻塞住)。然后通过MSG的target成员变量(Handler)来调用其dispatchMessage方法将消息分发下去,然后执行消息处理函数。
[java]
view plaincopy
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在消息的分发的过程中,其执行是有优先级的:
如果Message中包含callback(即通过post系列函数设置的Runnable对象),那么它会被优先执行。
否则,如果给当前的Handler设置了mCallback,那么它会优先执行。如果该方法返回true,分发结束,处理完毕。如果返回false,那么他还有机会执行默认的handleMessage函数。
以下是三种情况的示例代码:
[java]
view plaincopy
1.
Handler mHandler = new Handler();
mHandler.post(new Runnable() {
@Override
public void run() {
// TODO something
}
});
2.
Handler mHandler = new Handler(new Handler.Callback()
{
@Override
public boolean handleMessage(Message msg) {
// TODO something
return false;
}
});
3.
protected Handler m_handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO something
}
});
MessageQueue源码分析
成员变量
我们先看一下它的成员变量
[java]
view plaincopy
Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuiting;
boolean mQuitAllowed = true;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
mMessages:在最初不了解Message类时,以为MessageQueue里面,存放的是一个类似于LinkedList<Message>的数据结构。当了解Message数据结构后,才知道MessageQueue里面维护的只有一个Message,因为Message本身就能构建一个Message链。MessageQueue主要功能就是维护这个Message链,如插入和删除Message链中的元素,并提供获取Message链中next Message的方法。这里,mMessages始终指向的是消息链的第一个节点,即头节点。
mIdleHandlers:外部注册的回调列表(listeners)。如果当前消息队列已没有新的Message能被取出来时,线程即将被阻塞前被调用。即线程处于空闲时间时,被调用。
mPendingIdleHandlers:该成员变量配合上面的mIdleHandlers使用,我觉得它没有必要保存起来,完全用一个临时变量即可。
mQuiting:当前消息队列是否已准备退出。实际上,如果它为True,也就表明当前线程将会立马结束掉。
mQuitAllowed:是否允许退出消息队列。对于主线程(UI线程),该标志量为true。
mBlocked:标志当前消息队列是否处于阻塞状态。
下面的接口定义了线程处于空闲状态时的回调函数。由此可以看出,当你想在线程不忙的时候干点其它事情的话,这个接口就能派得上用场了。
[java]
view plaincopy
public static interface IdleHandler {
boolean queueIdle();
}
以下方法用来注册和移除线程处于空闲状态时的回调函数。
[java]
view plaincopy
public final void addIdleHandler(IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public final void removeIdleHandler(IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
}
再来看一看MessageQueue的两个核心函数:enqueueMessage(…)与next()函数。
enqueueMessage(Message msg, long when)函数
该函数的目的就是把指定的Message按照绝对时间插入到当前的消息队列中去。还记得该函数是在哪里被调用的吗?请回过头看看Handler的源代码:sendMessageAtTime(Message
msg, long uptimeMillis)
[java]
view plaincopy
final boolean enqueueMessage(Message msg, long when) {
// 1. 如果MSG的绝对时间戳不为0(说明已被初始化,并已加入到队列中),抛出异常
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
// 2. 如果MSG的target为null,说明该消息是QUIT消息。如果此时又发现mQuitAllowed为false,则抛出异常。
// 实际上,如果你调用主线程的Looper.quit()方法,你会发现该异常会被抛出来。因为主线程的消息循环是不允许退出的。
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
// 3. 如果消息队列正在退出,则直接返回false;否则,查看当前要加入的MSG是否是要求退出消息队列的MSG
//(QUIT MSG:判断依据是MSG的target是否为空),如果是,则将mQuiting设置为true,然后把该消息加入到消息
// 队列的头部,以保证下一次通过next()函数取出的消息就是QUIT消息,能快速的终止掉线程。
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
// 4. MSG最终从消息队列中取出来的绝对时间戳。NOTE:MSG的when字段只有在这一个地方被主动设置过。
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
// NOTE:以下部分就是真正如何将消息插入消息链的过程。
Message p = mMessages;
// 5. 如果当前的消息链为空,或者要插入的MSG为QUIT消息,或者要插入的MSG时间小于消息链的第一个消息。
// 那么,强势插入到消息链的头部。显示,消息链的头部被改变了,变成了新添加的消息。needWake需要根据
// mBlocked的情况考虑是否触发。
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 6. 否则,我们需要遍历该消息链,将该MSG插入到合适的位置。整个消息链是按消息被取出的绝对时间戳
// 由小到大链接起来的。时间轴为:msg1.when <= msg2.when <= msg3.when <= msg4.when <= msg5.when……
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
// 7. 对于needWake,如果该变量为true,说明mBlocked为true,即当前线程处于阻塞状,也即nativePollOnce处于阻塞状态。但此时,
// 我们已通过这个enqueueMessage方法已经向消息链中添加了一个消息,也就是说,此时我们需要把阻塞状态变成非阻塞状态,
// 让next()函数能够取到MSG。怎么办?通过执行nativeWake方法,便能触发nativePollOnce函数结束等待。实际上,
// nativePollOnce和nativeWake内部是通过管道的机制来实现阻塞和接触阻塞的。我的理解是: nativePollOnce函数从管道中
// 读数据,如果发现管道中有数据,则立即返回,否则,一直等待。而nativeWake就是向管道中写数据,只要往管道的另一端写数据,
// 则nativePollOnce就能立马从管道中读出数据来,从而变成非阻塞状态。(请参考:http://book.51cto.com/art/201208/353352.htm)<
相关文章推荐
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- Android布局中长度单位的深入研究
- Android 深入的研究一下蓝牙
- android 对话框深入研究
- Android 布局中长度单位的深入研究
- Android 深入研究adb(二)
- Android LLVM-Obfuscator C/C++ 混淆编译的深入研究
- Android 深入的研究一下蓝牙
- 推荐android几本研究深入的书籍(对开发人员很有帮助)
- 【Android 学习】深入理解Handler机制
- android 布局长度单位深入研究
- Android 深入研究adb(三)
- android 深入研究ratingbar自定义
- 深入研究Android事件传递与重绘