Android 消息机制
Android 的消息机制主要指 handler 的运行机制以及 Handler 所附带的 MessageQueue 和 Looper 的工作过程。
为什么要提供这个功能?
因为 Android 规定访问 UI 只能在主线程中进行,如果再子线程中访问 UI,程序会抛出异常。ViewRootImpl 中对 UI 操作做了验证 checkThread 方法来完成,如下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
这一限制导致必须在主线程中访问 UI,但是 Android 又建议不要在主线程进行耗时操作,否则会导致 ANR。因此,系统提供 Handler,主要原因就是为了解决这一的矛盾。
相关知识点概述:
消息机制简单概述:
Handler 创建时会采用当前线程的 Looper 来构建内部的消息循环系统,如果当前线程没有 Looper 会报错
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
主线程的 Looper 由系统自动创建,上述错误仅仅会在子线程中创建 Handler 时候出现,解决方法是手动为子线程创建 Looper:
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
Handler 创建完毕,其内部的 Looper 和 MessageQueue 就可以和 Handler 一起协调工作了:
-
通过 Handler 的 一系列 post 或者 send 方法发送一条消息到 MessageQueue。
queue.enqueueMessage(msg, uptimeMillis); //Handler 的 enqueueMessage 方法中将消息放入消息队列
-
当 Looper 发现有新消息到来时就会处理该消息。
-
最终消息中的 Runnable 或者 Handler 的 handleMessage 就会被调用。
Looper 是运行在创建 Handler 所在的线程中的,这样一来 Handler 中的业务逻辑就被切换到创建 Handler 所在的线程中去执行了。
MessageQueue 工作原理
MessageQueue 主要包含两个操作:
- 插入:往消息队列插入一条消息,对应 enqueueMessage()
- 读取:从消息队列中取出一条消息并将其从消息队列中移除,对应 next()
enqueueMessage():单链表插入操作
next():是一个无限循环的方法,如果消息队列中没有消息,它会一直阻塞在这里。当有新消息到来时,next() 会返回这条消息并将其从单链表中移除
for (;;) {
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;
}
}
Looper 工作原理
Looper 在 Android 的消息机制中扮演着消息循环的角色,具体来说它会不停地从 MessageQueue 中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。
构造方法中会创建一个 MessageQueue,然后将当前线程对象保存起来
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
子线程中创建 Handler 时需要手动为其创建关联的 Looper:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop(); //调用后消息循环系统真正起作用
}
}).start();
如果再子线程中手动创建了 Looper,那么应该在所有事情完成后退出 Looper,否则这个子线程就会一直处于等待的状态,而如果退出 Looper 以后,这个线程会立刻终止。可以调用 quit 或 quitSafely 退出 Looper。二者的区别是 quit 会直接退出 Looper ,而 quitSafely 只是设定一个退出标记,然后把消息队列中已有消息处理完毕后才安全的退出。
主线程中 Looper 由系统自动创建,具体是 ActivityThread 的 main 方法中调用:
Looper.prepareMainLooper(); //ActivityThread 的 main 方法中
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
主线程的 Looper 比较特殊,所以 Looper 提供了一个 getMainLooper 方法,通过它可以在任何地方获取到主线程的 Looper。
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
Looper 的 loop 方法的工作过程:
loop 方法是一个死循环,唯一跳出循环的方式是 MessageQueue 的 next 方法返回了 null。当 Looper 的 quit 方法被调用时,Looper 就会调用 MessageQueue 的 quit(boolean safe) 方法来通知消息队列退出.当消息队列被标记为退出状态时(mQuitting = true),它的 next 方法就会返回 null 。也就是说,Looper 必须退出,否则 loop 方法就会一直循环下去。loop 方法会调用 MessageQueue 的 next 方法来获取新消息,而 next 是一个阻塞操作,当没有消息时会一直阻塞在那里。如果 MessageQueue 的 next 方法返回了新消息,Looper 就会处理这条消息:msg.target.dispatchMessage(msg),这里的 msg.target 是发送这条消息的 Handler 对象,这样 Handler 发送的消息最终又交给它的 dispatchMessage 方法来处理了。这里不同的是, Handle 的 dispatchMessage 方法是在创建 Handler 时所使用的 Looper 中执行的,这样就成功地将代码逻辑切换到指定的线程中去执行了。
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(); // 从消息队列中读取消息,阻塞操作
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);//处理新消息
msg.recycleUnchecked();
}
}
Handler 工作原理
发送消息:一系列 post 以及 send 的一系列方法;post 最终也是调用的 send 。
接收消息:
看一条发送消息的调用链:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler 发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue 的 next 方法 就会返回这条消息给 Looper ,Looper 收到消息后就开始处理了,最终消息由 Looper 交给 Handler 处理,即 Handler 的 dispatchMessage 被调用:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) { // mCallback 是一个接口,另一种创建 Handler 的方式,类似于 创建线程时候不 new Thread 而是使用 implements Runnable
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//callback 是一个 Runnable 对象,即 Handler 的 post 方法所传递的 Runnable
private static void handleCallback(Message message) {
message.callback.run(); //调用 Runnable 的 run 方法
}
//可通过实现该接口创建 handler 对象
public interface Callback {
public boolean handleMessage(Message msg);
}
最后,调用 Handler 的 handleMessage(msg) 方法来处理消息。
主线程的消息循环
Android 的主线程就是 ActivityThread,主线程的入口方法为 main,在 main 方法中系统会通过 Looper.prepareMainLooper()来创建主线程的 Looper 以及 MessageQueue ,并通过 Looper.loop()方法来开启主线程的消息循环。
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");
}
主线程的消息循环开始以后 ActivityThread 还需要一个 Handler 来和消息队列进行交互,这个 Handler 就是 ActivityThread.H ,它内部定义了一组消息类型,主要包含了四大组件的启动和停止等过程。
final Handler getHandler() {
return mH;
}
final H mH = new 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;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
ActivityThread 通过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后回调 ApplicationThread 中 Binder 方法,然后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
网友评论