文章目录
一、Android 消息机制概述
Android 消息机制主要由 Handler,Looper,MessageQueue 三者组成。
1.1 他们的关系
-
MessageQueue 是 Looper 内的对象,Looper 是 Handler 内的对象。
-
一个线程拥有一个 Looper,一个 Looper 拥有一个消息队列,一个 Looper 可以服务于多个 Handler。
( 一个线程拥有一个 Looper 的原因是在添加第二个 Looper 时会抛出异常,其实 ThreadLocal 的机制其是允许存在两个 TreadLocal<Looper> 的)
1.2 Handler 为什么能用来进行线程间沟通
消息机制用来在两个线程之间沟通,既然是沟通,那么就需要一个桥梁,Android 里这个桥梁就是 Handler。
前提条件:
- 线程之间共享的区域有堆、方法区。
- 变量按作用域可分为:静态变量,成员变量和局部变量。
- 静态变量存放在方法区中,成员变量存放在堆中,局部变量存放在虚拟机栈中。
我们一般将 Handler 作为成员变量,所以,两个线程都能够对它进行访问,从而进行沟通。
二、Android 消息机制分析
2.1 ThreadLocal 的工作原理
ThreadLocal 用于为线程提供线程私有变量。具体解析可见Java 基础:ThreadLocal 解析。
Android 中用 ThreadLocal 为 Thread 添加了一个 Looper 类型的成员变量。
ps:上图中的 ThreadLocal.Values 在后来的版本改为 ThreadLocal.ThreadLocalMap,但含义一致
Thread 中有一个 ThreadLocalMap 类型的成员变量 threadLocals,代表一个 ThreadLocal->Object
的映射。
Looper 中有一个 ThreadLocal<Looper>
类型的静态变量 sThreadLocal,用于在各线程的 ThreadLocalMap 中作为 key,通过这个 key,可以 set、get 一个 Looper 类型的 Object。
-
set 时,sTreadLocal 会找到当前线程的 threadLocals,为它添加一个
Looper
对象。 -
get 时,sTreadLocal 会找到当前线程的 threadLocals,从它中间里面去get()。
在调用 Looper.prepare()
方法时,调用 sThreadLocal 的 set 方法,从而为当前线程添加一个 Looper
对象。
主线程 ActivityThread 运行时,会调用 Looper.parpareMainLooper() (主线程特殊的 Looper.prepare()),同样是为主线程添加了一个 Looper 成员变量。
//ActivityThread.java
Looper.prepareMainLooper(); // 会调用 Looper.prepare(false);
//Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
//ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
创建 Handler 时,Looper.myLooper() 方法会调用 sThreadLocal 的 get 方法,找不到会报错,在主线程中,因为已经创建,一定找得到,找到 Looper,相应的消息队列也就找到了。
//Handler.java
mLooper = Looper.myLooper();
//Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//ThreadLocal.java
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
使用时,在子线程中利用 Handler.sendMessage() 发出消息,发出消息的动作会将消息存入 MessageQueue 中,Looper 时刻监督着 MessageQueue,这时它就会启动,将消息再传给Handler,Handler 来处理。
2.2 MessageQueue 的工作原理
MessageQueue 包含两个操作:插入(enqueueMessage)和读取(next)。读取本身会伴随着删除操作。
MessageQueue 是用单链表实现的,单链表在插入删除上比较有优势。
enqueueMessage 主要就是单链表的插入操作,主要看一下 next 方法。
Message next() {
......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
......
}
}
从for (;;)
可以发现 next 方法是一个无限循环的方法,如果没有消息,next 方法会一直阻塞在这里。当有新消息到来时,next 方法会返回这条消息并将其从单链表中删除。
next 方法在主线程无限循环,而没有导致 ANR,是因为在 nativePollOnce
方法中执行了阻塞操作。主线程大多数时候都是处于休眠状态,并不会消耗大量 CPU 资源。
nativePollOnce
的阻塞采用 Linux pipe/epoll 机制。当执行 nativePollOnce
方法后,如果没有消息,主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的 epoll 机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
线程阻塞的情况分三种:
等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
nativePollOnce
就是一个 I/O 请求阻塞。
2.3 Looper 的工作原理
2.3.1 Looper 的创建
Looper.prepare()
可以为当前线程创建一个 Looper 成员变量。
Looper.prepareMainLooper()
可以为主线程创建一个 Looper 成员变量。
当 Looper 的构造方法中它会创建一个 MessageQueue。
2.3.2 Looper 的循环
Looper.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;
// 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;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
可以看出,Looper.loop() 中开启了一个无限循环,里面调用了 MessageQueue 的 next 方法,上面说过,next 方法是一个阻塞操作,没有消息时,next 方法会阻塞,于是 loop 方法也阻塞在那里。
有消息时,Looper 就会处理消息,msg.target.dispatchMessage(msg);
,这里的 msg.target
就是发送消息的 Handler,它的 dispatchMessage 方法运行在当前线程的 Looper 中,这就完成了线程切换。
2.3.3 Looper 的退出
Looper 的 quit()、quitSafely() 都可以用来退出。quit() 直接退出,quitSafely() 在处理完现有消息后才退出。
退出 Looper 后,线程就会立即终止。
quit()、quitSafely() 会调用 MessageQueue 的 quit() 方法,之后 next 方法就会返回 null,next 的无限循环、loop 的无限循环都随之结束,Looper 退出。
2.4 Handler 的工作原理
handler 的工作原理包括发送和处理消息。
发送使用 post 和 send 方法,post 最终也是调用 send 方法。
send 方法中通过 queue.enqueueMessage
将消息插入消息队列。之后 next
方法就会将消息传给 Looper,Looper 又调用 Handler 的 dispatchMesaage
方法进行处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
三、主线程的消息循环
主要包括:
主线程、 Binder 线程、主线程的 Handler、主线程的 Looper、主线程的 MessageQueue。
3.1 主线程
主线程就是 ActivityThread 所在的线程,ActivityThread 本身并不是一个线程,承载ActivityThread 的主线程是由 Zygote fork 而创建的进程。
ActivityThread 的入口方法为 main 方法。
public static void main(String[] args) {
......
Process.setArgV0("<pre-initialized>");
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主要过程:
调用 Looper.prepareMainLooper()
来创建主线程的 Looper 以及 MessageQueue。
生成 ActivityThread 对象后,调用 thread.attch(false)
,创建一个 Binder 线程(ApplicationThread 在其中运行,它是 Binder 的服务端,用于接收系统服务 AMS 发送来的事件),该 Binder 线程通过 Handler 将 Message 发送给主线程。
调用 Looper.loop()
开启主线程的消息循环。
3.1.1 主线程的 Handler
主线程中的 Handler 为 ActivtyThread 类中的内部类 H。H 中定义了一组消息类型,包含了四大组件的启动和停止。
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
......
}
3.2 消息循环
ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的 Binder 方法,然后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。