上一章讲解了Android异步消息处理机制的基本使用,下面将简单地探寻一下异步机制背后的奥妙,源码版本为:API22。
首先,声明一下本文是在我参考了一下各位大神的文章之后才慢慢熟悉的, 若有不足之处,还望各位批评指正!。菜鸟上路,,,,
郭霖博客
鸿洋博客
刘超 深入解析android5.0系统
任玉刚博客
先后顺序按照拼音排序,无关技术本身。
先简单地总结一下Looper,MessageQueue,Message和Handler四者之间的关系:
Looper.prepare()
)。hanldeMessage()
处理消息。Hanlder在构造时和一个Looper对象关联在一起。Handler和Looper是多对一的关系,多个Handler对象可以和同一个Looper对象建立关系,反之则不行。
public final class Looper {
// 成员变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
// 成员方法
public static void prepare() {...}
private static void prepare(boolean quitAllowed) {...}
public static void prepareMainLooper() {...}
public static Looper getMainLooper() {...}
public static void loop() {...}
public static Looper myLooper() {...}
public static MessageQueue myQueue() {...}
}
public final class MessageQueue {
Message next() {...}
boolean enqueueMessage(Message msg, long when){...}
}
public class Handler {
//内部接口
public interface Callback {
public boolean handleMessage(Message msg);
}
//处理消息相关方法
public void handleMessage(Message msg) {}
public void dispatchMessage(Message msg) {...}
private static void handleCallback(Message message) {...}
// 构造器相关方法
public Handler() {...}
public Handler(Callback callback) {...}
public Handler(Looper looper) {...}
public Handler(Looper looper, Callback callback) {...}
public Handler(boolean async) {...}
public Handler(Callback callback, boolean async) {...}
public Handler(Looper looper, Callback callback, boolean async) {...}
// 获取Message相关方法
public final Message obtainMessage(){...}
public final Message obtainMessage(int what){...}
public final Message obtainMessage(int what, Object obj){...}
public final Message obtainMessage(int what, int arg1, int arg2){...}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){...}
// post相关方法
public final boolean post(Runnable r){...}
public final boolean postAtTime(Runnable r, long uptimeMillis){...}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){...}
public final boolean postDelayed(Runnable r, long delayMillis){...}
public final boolean postAtFrontOfQueue(Runnable r){...}
// send相关方法
public final boolean sendMessage(Message msg){...}
public final boolean sendEmptyMessage(int what){...}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...}
public final boolean sendMessageDelayed(Message msg, long delayMillis){...}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...}
public final boolean sendMessageAtFrontOfQueue(Message msg) {...}
// 进出消息队列
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {...}
}
public final class Message implements Parcelable {
// 主要成员变量
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
// 主要成员方法
public Message() {} // 不建议使用
// 获取Message相关方法
public static Message obtain() {...}
public static Message obtain(Message orig) {...}
public static Message obtain(Handler h) {...}
public static Message obtain(Handler h, Runnable callback) {...}
public static Message obtain(Handler h, int what) {...}
public static Message obtain(Handler h, int what, Object obj) {...}
public static Message obtain(Handler h, int what, int arg1, int arg2) {...}
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {...}
public void setTarget(Handler target) {...}
public void sendToTarget() {...}
public Handler getTarget() {...}
}
下面将以问答的方式解析异步消息处理机制。
解析:ThreadLocal是Thread Local Variable即线程本地变量的意思,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。这里保存Looper类的实例对象。
Looper.prepare()
,而在子线程中则需要Looper.prepare()
?解析:这是因为在ActivityThread类中的main()
方法调用了Looper.prepareMainLooper()
,简单代码如下:
public static void main(String[] args) {
...// 前面的省略了
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到main()
方法中调用了Looper.prepareMainLooper()
,而Looper.prepareMainLooper()
源码为:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 若不存在Looper对象,则从sThreadLocal获取
sMainLooper = myLooper();
}
}
而prepare(boolean)
代码如下:
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));
}
到这里我们可以看到main()
方法中调用了Looper.prepareMainLooper()
,而Looper.prepareMainLooper()
又调用了prepare(boolean)
。prepare(boolean)
方法就会创建一个Looper对象并保存在静态变量sThreadLocal(ThreadLocal类实例)。我们来看一下Looper的构造器:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
该构造器为私有的,且创建了一个消息队列实例,并将当前线程保存下来。
同时在prepare(boolean)
可以看到若sThreadLocal已经有一个Looper对象后再创建就会出现异常。这说明了两点:1. 一个线程中只能有一个Looper对象;2. 一个线程中只能最多调用一次prepare相关的方法(prepare()
(内部也是调用prepare(boolean)
),prepare(boolean)
,prepareMainLooper()
(UI线程中调用),)。
而我们在子线程中调用的是Looper.prepare()
,来看一下这个方法的源码:
public static void prepare() {
prepare(true);
}
可以看到同样也是调用了prepare(boolean)
。
解析:该问题主要涉及到Handler中以下几个方法:
public Handler() {this(null, false);}
public Handler(Callback callback) {this(callback, false);}
public Handler(Looper looper) {this(looper, null, false);}
public Handler(Looper looper, Callback callback) {this(looper, callback, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback, boolean async) {
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 = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在上述构造器中可能传递的参数为:
Looper.prepare()
方法。首先,在Handler(Callback callback, boolean async)
中我们看到调用了Looper.myLooper()
返回Looper对象,若Looper对象为null则会抛出异常。
根据以上构造器,我们能够有多种方式实例化一个handler对象,前五个构造器最终都会调用下面两个构造器。对于Handler(Callback callback, boolean async)
这个构造器没有指定Handler的Looper对象,则使用当前线程的Looper对象。同时我们看到传递了Callback接口对象,将其值赋值给了mCallback,我们来看一下相关的源码:
public interface Callback {public boolean handleMessage(Message msg);}
final Callback mCallback;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
通过上述代码我们可以看到,若传递Callback接口对象的话,在dispatchMessage(Message msg)
中将不会执行handleMessage(msg)
(当然这之前先判断msg.callback是否为空,这个问题之后再讲),而是调用mCallback.handleMessage(msg)
,而该方法就是我们传递的Callback接口对象实现的方法。因此,我们也可以通过这种方式处理消息。若不传递Callback接口对象的话,那么就会执行handleMessage(msg)
,我们可以通过继承Handler实现该方法处理消息。
Looper.loop()
?为了解决这个问题我们先看一下loop()
源码:
public static void loop() {
final Looper me = myLooper();
if (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(); // might block
if (msg == null) {
// msg等于null,就会退出
return;
}
.....
msg.target.dispatchMessage(msg);
.....
msg.recycleUnchecked(); // 保证消息循环利用。
}
}
在loop()
中首先获取Looper对象me,然后根据该对象获取对应的消息队列queue,下面就进入一个for(;;)
死循环不断地从queue取消息,若没有则next()
(该方法比较复杂,我没怎么看懂,,,)方法就会阻塞。
若消息为空的话就会退出loop()
;下面有一句:msg.target.dispatchMessage(msg);
,这个消息msg的target属性就是Handler对象,那么这个消息就是由该Handler来处理的。我们可以通过Message的重载方法obtainXXX()
和setTarget()
设置Handler对象。
通过loop()
,就能实现不断处理发送过来的消息。
我们再来看一下Message的sendToTarget()
:
public void sendToTarget() {
target.sendMessage(this);
}
可以看到通过这个方法发送消息和通过Handler发送消息一样。
sendMessage()
是如何将消息发送到消息队列中的,而消息队列又是如何管理消息的?首先,看一下,send
相关方法:
// send相关方法
public final boolean sendMessage(Message msg){...} // 发送的消息希望及时处理,但不插队
public final boolean sendEmptyMessage(int what){...} // 只发送带有what属性的消息,希望消息及时处理即可
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...} // 希望延迟处理
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...} // 希望消息在指定的时间处理
public final boolean sendMessageDelayed(Message msg, long delayMillis){...} // 希望延迟处理
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...} // 希望消息在指定的时间处理
public final boolean sendMessageAtFrontOfQueue(Message msg) {...} // 消息插队,希望马上处理
带有Empty
的方法甚至发送的不是消息,只是what
!!注意一下。
上面的send
方法,除了sendMessageAtFrontOfQueue(Message)
,都会调用sendMessageAtTime(Message,long)
。而这两个方法中则会返回消息队列的enqueueMessage(Message,long)
的方法,该方法就是将消息按照时间顺序插入到消息队列中。其实,消息队列是用链表实现的,排序方式是按照时间进行排列的。enqueueMessage(Message,long)
源码如下:
boolean enqueueMessage(Message msg, long when) {
....
synchronized (this) {
....
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
....
}
return true;
}
可以看到首先通过if
判断插入的消息的时间是否最小,若是的话直接插入到链表头部,否则进入for(;;)
依次遍历链表直到时间小于链表值,然后再插入。消息队列以mMessages为表头进行保存。
以上方法就通过Handler的send
方法将消息插入到消息队列中。此时还是在同一个线程中。
loop()
方法循环检测是否有消息进入队列,若有则取出并处理;sendMessage()
方法发送消息,发送过来的消息通过一系列的处理进入到消息队列,这样消息就进入了A线程中。通过上述两步实现了A线程和B线程之间的通信。
创建消息的方式有很多种,我们来对比一下:
obtainMessage()
系列;obtain()
系列;Handler中的方法实际上都是调用Message的静态方法obtain()
系列,而obtain()
系列最终都会调用obtain()
方法。我们来看一下它的源码:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
可以看到当前存在多余的消息时,则我们可以直接使用,而不是再创建一个新的消息,循环使用消息减少资源浪费。能够循环使用消息的原因是:每当处理完一个消息时都会调用消息的recycleUnchecked()
该方法就会将使用过的消息进行保存。源码如下:
void recycleUnchecked() {
... // 清空消息内容
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
sPool是静态成员变量,保存链表的头,当处理过一个消息时,(消息池大小小于给定值50时)该消息就会采用头插法插入链表中。
post
方法是干什么用的?我们首先回过头来看一下dispatchMessage()
方法,其中有一个判断:
if (msg.callback != null) {
handleCallback(msg);
}
只有当msg.callback
为空时才会执行下面的。消息的callback属性是Runnable变量,那么该属性是在哪里赋值的呢?我们发现在post
系列中都会调用一个方法getPostMessage()
:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到该方法可以对callback属性进行赋值,也就是说,通过post
方法发送消息的话和send
一样可以完成处理消息(实际上在post
中也是调用send
方法),只不过不在handleMessage()
中处理消息,而是在handleCallback()
中处理消息。该方法的源码会让我们大吃一惊的:
private static void handleCallback(Message message) {
message.callback.run();
}
直接调用了Runnable对象的run()
方法,这样一来就不是创建新线程,而是直接调用。
当然也可以在消息的obtain()
中设置callback属性。
post
方法和send
方法的区别post
方法在内部会调用send
方法,并且post
方法发送的是带有处理方法的消息,而send
方法发送的是不带有处理方法的消息。前者消息在自己携带的方法中处理,后者则只能通过handleMessage()
处理