Android源码分析之消息机制Handler
1 前言
Handler在Android开发中使用的比较多,通常Handler是在主线程中创建,子线程拿到这个Handler向主线程中发送消息。
那么如果需要主线程中向子线程中发送消息呢?
本文只是提出这个问题,并不在此提供实现这种场景的例子,写这篇文章的目的主要是理解Handler的运行原理,以便更好、更灵活的运用Handler。
2 Android消息机制简介
- Android的消息机制主要是指Handler的运行机制,Handler的运行依赖于MessageQueue和Looper,当然,既然是消息机制,通常也需要用到Message。
- Handler、Looper、MessageQueue和Message的工作原理就像是生产线,Looper是发动机,MessageQueue就是传送带,Handler是工人,Message就是待处理的产品
3 Handler的工作流程
一个应用的启动需要有main方法作为启动的入口,在Android中这个main方法在ActivityThread类中,查看这个类的main方法可以看到:
public static final void main(String[] args) {
//省略无关代码
...
//主线程中调用Looper.prepareMainLooper()方法创建Looper
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//主线程中调用Looper.loop(),开始轮询,取消息
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
...
}
}
3.1 Looper轮询器
在ActivityThread的main方法中首先调用Looper.prepareMainLooper()方法,我们来看一下,这个方法中做了哪些工作:
在Looper类中
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
该方法中首先调用本类中的prepare()方法,这个方法的主要作用是创建Looper对象,并且把该对象绑定到当前线程中,在这里既然是主线程调用的,那么该Looper对象也就是在主线程当中
在Looper类中
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//把Looper绑定到当前线程
sThreadLocal.set(new Looper());
}
在Looper的构造方法中,我们可以看到:
private Looper() {
//创建消息队列
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
首先,Looper这个类的构造方法是私有的,也就是说不允许外界直接new出来Looper对象。在创建Looper的时候,同时创建了一个消息队列MessageQueue。
所以请注意,消息队列MessageQueue是在Looper创建的时候,同时创建的
通过调用Looper.loop()方法,开始轮询消息:
public static final void loop() {
Looper me = myLooper();
//通过拿到Looper对象,取获取与之对应的消息队列MessageQueue
MessageQueue queue = me.mQueue;
...
//通过死循环方式去取消息
while (true) {
//调用MessageQueue的next()方法取消息,这个过程也是死循环
Message msg = queue.next(); // might block
if (msg != null) {
...
//取到消息之后,交给发送该消息的Handler取处理消息
msg.target.dispatchMessage(msg);
...
//回收消息,在Message中维护的有一个消息池
msg.recycle();
}
}
}
通过上述简单的叙述,我们可以明白,在Activity启动的时候创建的一个与主线程相关的Looper和对应的消息队列MessageQueue,并且通过消息队列通过阻塞的方式去取消息。
3.2 Handler的工作过程
Handler的工作过程主要包含发送消息和处理消息。Handler的构造方法有多个,他们的共同点是均会获取当前线程的Looper对象和消息队列MessageQueue对象
//Handler的构造方法1
public Handler() {
...
//获取当前线程的Looper对象
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;
}
//Handler的构造方法2
public Handler(Callback callback) {
...
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;
}
//Handler的构造方法3
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
//Handler的构造方法4
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
发送消息可以通过一系列send方法,也可以通过一系列post方法,不过post方法最终还是通过send方法去实现。
用send方法,最终也会调用一个方法如下:
//在Handler中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//调用msg.target的方法把Message和发送它的Handler绑定,所以Looper能够把正确的Message交给发送它的Handler处理
msg.target = this;
//调用MessageQueue的enqueueMessage方法把消息加入消息队列
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
可以看到,Handler发送消息的过程就是向消息队列中插入一条消息。前面已经讲到,MessageQueue调用next方法去轮询消息,那么当MessageQueue拿到消息之后,把消息传递给Looper,最终交给Handler去处理,即dispatchMessage方法会被调用。此时,Handler开始处理消息。值得一提的是,在消息队列中可能有不同Handler发送的多个消息,通过在发送消息的时候把Message和发送它的Handler绑定,Looper就会把消息正确的交给发送它的Handler来处理。dispatchMessage方法如下:
//Handler中
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler处理消息的过程如下:
-
查看Message的callback是否为null,不为null的话就通过handleCallback(msg)方法处理消息。这里的callback实际上就是一个Runnable对象,如果以post方式去发送消息,最终就会调用handleCallback(msg)方法去处理,这个方法内容为:
private final void handleCallback(Message message) { message.callback.run(); }
检查mCallBack是否为null,如果不为null就调用mCallBack的handleMessage(msg)方法。这个mCallBack是CallBack接口的子类对象,前面已经说过Handler的构造方法中有两个可以用到这个CallBack。
如果mCallBack为null,最终就会调用Handler的handleMessage(msg)方法,这个方法通常是在创建Handler时被使用者重写的方法。
需要说明的是:在主线程当前我们创建Handler时并没有自己创建Looper对象,这是因为主线程已经为我们创建好了;如果要在子线程当前创建Handler,一定要在之前创建Looper对象,即调用Looper.prepare()方法。
3.3 MessageQueue消息队列
前面的内容已经讲了很多关于MessageQueue的东西,这里也就不再赘述,MessageQueue主要包含两个操作:插入消息(enqueueMessage方法)和读取消息(next方法)。读取的过程也伴随着删除操作。MessageQueue的的内部实际上是通过一个单链表的数据结构来维护消息列表,这主要也是因为单链表在插入和删除上比较有优势。
final boolean enqueueMessage(Message msg, long when) {
...
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 当前发送的message需要马上被处理调,needWake唤醒状态置true
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 当前发送的message被排队到其他message的后面,needWake唤醒状态置为false
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
}
}
// 是否唤醒主线程
if (needWake) {
nativeWake(mPtr);
}
return true;
}
enqueueMessage的主要操作其实就是单链表的插入操作,根据时间看当前发送的Message是否需要马上处理。这个enqueueMessage方法是Handler发送消息的时候调用。
下面来看next方法:
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
...
}
可以看到,next方法返回一个Message对象。next方法中也是采用阻塞的方式去获取消息队列中的消息,一旦有消息立即返回并且将它从单链表中移除。如果没有消息就一直阻塞。前面已经提到,这个取消息的next方法是在Looper的loop()方法中调用。
3.4 Message消息载体
Message只有一个无参构造方法,但是Message有多个obtain静态方法来返回Message对象。
采用哪种方式创建Message对象都可以,但是建议采用obtain方法来创建。这是因为Message通过在内部构建一个链表来维护一个被回收的Message对象池。当用户调用obtain方法时会优先从池中获取,如果池中没有则创建新的Message对象。同时在使用完毕之后,进入池中以便于复用。这个在Looper.loop()方法可以看到一点端倪,在使用完毕时候调用了Message的recycle()方法。
下面是obtain方法创建Message对象的流程:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
4 小结
- 在应用启动时,会开启一个主线程(即UI线程),会创建一个Looper对象,并把该对象绑定到主线程中。Looper对象被封装在ThreadLocal中,使得不同线程间的Looper不能共享
- 创建Looper时同时创建了MessageQueue消息队列对象
- Handler通过send方法或者post方法,把消息加入消息队列MessageQueue中
- 主线程中调用Looper的loop()方法,会开启消息循环,不断的从消息队列中取出消息
- Looper拿到消息之后调用Handler的dispatchMessage方法来处理消息
下一篇: 「Java基础_Day4」学习心得