消息机制的概述
- Handler:Android消息机制的上层接口,日常开发中被开发者用来更新UI,可以轻松将任务切换到Handler所在线程去执行。
- Android的消息机制:主要是指Handler的运行机制,需要底层的MessageQueue和Looper支撑。MessageQueue是消息队列,结构是单向链表,而Looper是以死循环的形式查询消息并处理新消息。
- ThreadLocal:Looper中的一个成员变量,在不同线程中互不干扰存储并提供数据,通过ThreadLocal可以轻松获取每个线程中的Looper。线程默认没有Looper,如果要用Handler,就必须为线程创建Looper。
- UI线程:本质是ActivityThread,它被创建的时候就会初始化Looper,所以在主线程中默认可以使用Handler。
- 只能在主线程中访问UI,是通过ViewRootImpl的checkThread方法检查的,之所以有这个限制,是因为Android的UI控件是非线程安全的,如果多个线程同事访问可能导致UI控件处于不可预期状态。
Android消息机制分析
- ThreadLocal工作原理
- ThreadLocal是一个线程内部数据存储内,在指定线程存储数据,数据存储之后,只能在指定线程中获取到数据,其他线程无法获取数据。
- 使用场景:①数据以线程为作用域,且不同线程有不同数据副本,就可以考虑使用ThreadLocal ②复杂逻辑下的对象传递
- get/set方法理解:首先获取当前线程t,然后获取t.threadLocals:ThreadLocalMap对象,set方法会把ThreadLocal作为key,泛型T作为value存储。如果threadLocals对象为空,那么先创建这个对象,然后存储key/value。get也是同样的逻辑。
- MessageQueue工作原理
- 主要包含两个操作,插入和读取,读取伴随删除。插入和读取分别对应enqueueMessage和next,enqueueMessage作用是往队列中插入一条消息,next是从队列中读取一条消息并将其从消息队列中移除。next是一个无限循环方法,队列中没有消息,一直阻塞在这里,有新消息时,next方法返回消息并从单链表中移除该消息。
- Looper工作原理
- 在线程中通过prepare方法初始化Looper对象,prepareMainLooper是专门用来给主线程ActivityThread创建Looper对象的,本质也是调用prepare方法。在任何线程中都可以通过getMainLooper获取主线程的Looper。
- Looper的构造方法中会初始化一个MessageQueue对象,并且保存当前线程。
- 调用loop方法开启消息循环,loop方法是一个死循环,会一直从MessageQueue中通过next方法获取消息,并且处理消息。当没有消息时,next方法阻塞,导致loop方法也一直阻塞。唯一跳出循环的方式是MessageQueue的next方法中返回null。
- 可以通过Looper的quit和quitSafety退出一个Looper。quit直接退出Looper,而quitSafety是设置一个退出标记,把消息队列中的消息处理完毕之后安全退出。Looper退出后,Handler发送消息会失败,send方法返回false。Looper的quit或quitSafety也会调用MessageQueue中的对应方法,把队列标记为退出状态,
这时候next方法就会返回null
。子线程中的Looper使用完后都需要调用quit或quitSafety方法,不然子线程一直处于等待状态,无法结束。
- Handler的工作原理
- Handler包含消息的发送和接收过程,消息发送通过一些列post方法和send方法实现,post方法的本质也是调用send方法。而发送消息的过程很简单,就是往Looper.MessageQueue中插入一条消息。
- Handler发送消息后,MessageQueue通过next把消息交给Looper处理,Looper调用Handler的dispatchMessage方法。
- dispatchMessage中有三层逻辑:第一:如果消息Message设置了callback(Runnable对象),那么直接执行这个Runnable。第二:如果Handler设置了Handler.Callback,则执行callback的handlerMessage方法。第三:如果第一第二不满足,则执行Handler自身的handleMessage方法
- 从上面的分析可以看出,Handler的实现有两种方式:
//派生Handler子类创建Handler对象
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO: 2018/7/9 处理消息
}
};
或
//通过传参Handler.Callback创建Handler对象
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO: 2018/7/9 处理消息,false=需要进一步处理,true=不需要进一步处理
return false;
}
});
- 主线程的消息循环
- 主线程ActivityThread的入口方法为main,在main方法中调用Looper.prepareMainLooper创建主线程的Looper和MessageQueue(Looper构造方法中初始化)。然后调用looper.loop开始主线程消息循环。
- 开启消息循环之后,主线程还需要一个Handler和消息队列交互,这个Handler就是ActivityThraed.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;
...
}
一个问题
关于Handler消息机制,经常会有这样一个问题:Looper.loop死循环为什么不会卡死?
要解答这个问题,首先,我们来说一下线程和进程,每个应用运行时都会创建一个进程,多数情况下App运行在一个进程中,除非指定android:process
或者通过native代码fork进程。在CPU看来,线程是一段可执行代码,代码执行完毕,线程生命周期结束,退出线程。
而对于主线程,肯定不希望运行一段时间就自动退出,那么如何保证一直存活,简单的做法是通过死循环让代码一直执行下去。
那么死循环是否会浪费CPU资源呢?其实不会,主线程的MessageQueue没有消息是,变阻塞在loop的next中,此时线程释放CPU资源进入休眠状态,直到下个消息发生,唤醒主线程工作。所以主线程多数情况处于休眠状态,并不会消耗大量CPU资源。
Activity/Service生命周期如何在死循环外执行的?ActivityThread的内部类H继承自Handler,通过Handler发送各种类型的消息(包括Activity/Service生命周期消息),死循环处理消息,就可以回调Activity/Service的生命周期方法。
这里再用简单的描述来总结这个问题:
Android是基于事件驱动,应用启动后会进入一个死循环监听事件,而Android中几乎所有的系统调用都是通过主线程的Handler发送消息,然后回调handleMessage,如果退出死循环,就不能响应事件了,应用程序就会退出了。当没有Message的时候,主线程释放CPU资源,处于休眠状态(Linux pipe/epoll机制)
Android消息机制总结
用一张图来总结Handler消息机制: