Android的消息机制
总的来说,Android应用程序是通过消息来驱动的,Android也可以说是一个以消息驱动的系统,UI、生命周期等各种事件和消息处理机制息息相关,所以消息处理机制在整个Android知识体系中是非常重要的,但是网上关于Android消息机制的文章,大多都长篇大论,如果没有足够的耐心,很难阅读并理解下去,所以,我觉得在汲取别人文章知识的同时,也需要自己去花功夫去总结出自己的结论,才能记忆深刻。
组成成员
- Looper 消息轮询器
- MessageQueue 消息队列
- Message 消息载体
- Handler 发送与处理消息者
原理简述
App启动时,在ActivityThread的main方法里,调用了Looper.prepareMainLooper(),创建一个全局主线程的Looper(通过ThreadLocal将Looper与主线程绑定,并确保一个线程只有一个Looper),这个Looper在实例化的同时,会实例化一个消息队列MessageQueue,在Looper.prepareMainLooper()完毕后,main方法继续调用Looper.loop(),主线程的Looper进入一个无限的for循坏,在这个for循环里,looper会不断从自己的消息队列MessageQueue中查询消息,当队列中没有消息时,CPU会腾出资源给其他线程,当队列中有消息Message时,由于Message会持有一个handler对象,那么此时会迫使handler调用dispatchMessage方法,而在dispatchMessage方法里面,则会继续调用我们最熟悉的handleMessage方法了。
由于handler自身持有主线程的Looper(该handler是在主线程实例化的前提下),当我们利用handler发送消息时,会通过Looper中的消息队列MessageQueue插入消息,那么在loop方法里的无限for循坏就会取出这条消息,handler执行这条消息,因此其他线程可以通过这个handler,与主线程进行消息传递,从而形成这个完整的消息机制。
值得注意的是,在其他线程创建Handler时,必须首先调用Looper.prepare(),在该线程才会持有对应的Looper,否则会抛出异常
例子
我们通过一个例子来概括:
某公司有一个24小时运行的请假系统(即APP的主线程),系统会有一个请假列表(即MessageQueue),请假者(Handler)发送休假申请到系统时,会自动插一条请假记录(Message)到列表中,系统的工作就是不断查询列表是否有休假申请,如果有,就一条条进行审核,并将审核结果发送到请假者的手机上,请假者根据审核结果行动;如果列表中没有记录,系统就将进入休眠状态(即线程堵塞),但是当系统收到一条休假申请时,系统会自动恢复工作状态,继续进行审核。
源码分析
首先,从App的入口,即ActivityThread
public final class ActivityThread {
...
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
}
...
public static void main(String[] args) {
...
//创建一个主线程的Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//进入一个无限的消息读取操作
Looper.loop();
...
}
...
}
由以上源码可见ActivityThread并不是一个线程类,只是一个普通类,在其主方法中调用了
Looper.prepareMainLooper()
Looper.loop()
来大致看看Looper的源码
public final class Looper {
...
//ThreadLocal,线程本地存储区,这里用于存放线程对应的Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//主线程的Looper
private static Looper sMainLooper; // guarded by Looper.class
//Looper持有的消息队列
final MessageQueue mQueue;
//Looper的所属线程
final Thread mThread;
//实际上调用了prepare方法,并且利用synchronized锁,确保主线程只有一个Looper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//如果线程储存区已经有Looper,抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//保存一个新的Looper
sThreadLocal.set(new Looper(quitAllowed));
}
//私有构造方法
private Looper(boolean quitAllowed) {
//实例化一个队列
mQueue = new MessageQueue(quitAllowed);
//赋值当前线程给mThread变量
mThread = Thread.currentThread();
}
//获取线程的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//方便获取主线程的Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
}
//进入无限循环,不断读取消息,当队列中有消息时,通过消息中的handler对象的dispatchMessage方法分发消息
public static void loop() {
//获取Looper
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
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;
}
...
try {
//Message对象持有一个target即Handler对象,利用它的dispatchMessage方法进行消息分发
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
//将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。
msg.recycleUnchecked();
}
}
那么,APP启动时,并创建了一个属于主线程的Looper,而这个Looper也拥有自己的消息队列,主线程已经开始在无限循环中不断查询消息了,那么系统是怎样发送消息到主线程中去,而主线程又如何处理这些消息?
注:Binder线程:具体是指ApplicationThread,在App进程中接受系统进程传递过来的信息的线程(这个线程的创建时间先于主线程)。
接下来,我们来探讨一下Activity的启动流程:当APP准备启动一个Activity的时候,系统服务进程下的ActivityManagerService(AMS)线程会通过Binder线程发送IPC调用给APP进程,App进程接到到调用后,通过App进程下的Binder线程最终调用ActivityThread类下面的scheduleLaunchActivity方法来准备启动Activity,看下scheduleLaunchActivity方法:
//这个方法不是在主线程调用,是Binder线程下调用的
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
...
updatePendingConfiguration(curConfig);
//将信息内容封装成ActivityClientRecord,发送启动activity的指令
sendMessage(H.LAUNCH_ACTIVITY, r);
}
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async)
{
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
由上面的代码可以知道,最后通过变量mH来发送消息,那么重新查阅ActivityThread的源码,可以得知它就是一个继承Handler的H类,它帮我们处理许多activity生命周期的事情
public final class ActivityThread {
...
final H mH = new H();
...
private class H extends Handler {
...
public void handleMessage(Message msg) {
switch (msg.what) {
//进行一系列指令操作
}
}
...
}
}
到此为止,我们对启动流程有了较为清晰的了解,那么Handler又是如何将消息发送到线程的消息队列中去呢?再来简单看看Handler的源码
public class Handler {
...
//可见,内部有Looper和MessageQueue成员
final Looper mLooper;
final MessageQueue mQueue;
...
//我们常用的无参构造
public Handler() {
this(null, false);
}
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());
}
}
//得到当前线程的Looper
mLooper = Looper.myLooper();
/*
* 如果在一个新线程中,实例化Handler前,必须先调用Looper.prepare()
*/
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//得到Looper的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/**
*
* 所有发送消息的方法,最终都会调用到此方法
*/
public boolean sendMessageAtTime(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//将消息中的target赋值为当前handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//将消息插入队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
}
由Handler的源码可知,handler在发送消息时,会将消息插入其持有的Looper所对应的消息队列中,即以上代码的queue.enqueueMessage
public final class MessageQueue {
...
private final boolean mQuitAllowed;
...
//将消息按时间插入队列中
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//插入消息队列的时候需要做同步,因为会有多个线程同时做往这个队列插入消息
synchronized (this) {
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.markInUse();
//when 表示这个消息执行的时间,队列是按照消息执行时间排序的
//如果handler 调用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillis
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
// p==null 表示当前消息队列没有消息
msg.next = p;
mMessages = msg;
//需要唤醒主线程,如果队列没有元素,主线程会堵塞在管道的读端,这时
//候队列突然有消息了,就会往管道写入字符,唤醒主线程
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//将消息放到队列的确切位置,按照msg的when 排序的
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
}
回顾一下Looper的源码,在loop方法中无限的for循环中看到,当队列中有消息时,消息对象持有一个target即Handler对象,利用它的dispatchMessage方法进行消息分发
msg.target.dispatchMessage(msg);
再补充一下Handler的分发消息的部分代码
public class Handler {
...
//这里方法内容为空,因为后续由开发人员操作
public void handleMessage(Message msg) {
}
/**
* 可以看到,最后会调用handleMessage(当回调接口为空时)
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
...
}