Android消息机制

关于Android消息机制的源码分析

1.应用程序入口分析

应用程序入口是在ActivityThread类里main方法,(当应用启动后,底层通过C/C++调用main方法)。

核心代码:

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}

1.1 初始化了Environment对象,然后创建了Lopper,然后开始消息循环。如果程序没有死循环,执行完main函数就会立马退出。之所以App可以一直运行是因为Looper.loop()是一个死循环。

1
2
3
4
public static void loop() {
for (;;) {
}
}

这里用for(;;)而不是while(true)是为了防止一些人通过黑科技修改这个标志位(通过反射机制)(这里不是很懂)!

在非主线程里面我们也可以创建一个Handler,但是需要我们主动去为当前的子线程绑定一个Looper,并且启动消息循环

Looper有两个核心方法:prepare()与loop()。通过Looper、Handle、Message、MessageQueue等组成了Android的消息处理机制。

2.为什么需要这样一个消息机制

2.1 不阻塞主线程

Android应用启动时,会创主线程, 负责与UI组件(widget,view)进行交互, 比如控制UI界面显示,更新等。这种单线程模型导致运行性大大降低,只能处理简单、短暂的操作。过重的任务比如下载访问数据库等都会导致ANR。Android大部分耗时操作都应该放到子线程,不要在主线程做操作

2.2 并发程序的有序性

单线程模型的UI主线程也是不安全的,会造成不可确定的结果。

线程不安全简单理解为:多线程访问资源时,有可能出现多个线程先后更改数据造成数据不一致。比如,A工作线程(也称为子线程)访问某个公共UI资源,B工作线程在某个时候也访问了该公共资源,当B线程正访问时,公共资源的属性已经被A改变了,这样B得到的结果不是所需要的的,造成了数据不一致的混乱情况。

线程安全简单理解为:当一个线程访问功能资源时,对该资源进程了保护,比如加了锁机制,当前线程在没有访问结束释放锁之前,其他线程只能等待直到释放锁才能访问,这样的线程就是安全的。

Android只允许主线程更新UI界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到Handler机制来解决此问题。基于Handler机制,在子线程先获得Handler对象,该对象将数据发送到主线程消息队列,主线程通过Loop循环获取消息交给Handler处理。

有了消息机制,我们可以发送消息,然后Looper把消息发给主线程,然后就可以执行了。

消息包括:

我们自己的操作消息,即客户端的Handler

操作系统的操作消息,系统的Handler,例如来电话跳出界面,就需要。

先分析系统的Handler,再去深入理解消息机制里面各个组件

3.系统的Handler

ActivityThread的成员变量有这样一个H(继承自Handler),这个就是系统的Handler。

1
final H mH = new H();

回顾一下ActivityThread的初始化,在main方法中ActivityThread thread = new ActivityThread();在new的时候就已经完成了初始化成员变量,单例饿加载。

在H中定义了大量的常量,然后用case做操作。

从系统的Handler中,在handlerMessage我们可以看到四大组件的生命周期操作,创建,销毁,切换等。跨进程通信及Application进程的销毁。

比如说我们有一个 应用程序A 通过 Binder 去跨进程启动另外一个 应用程序B 的 Service(或者同一个应用程序中不同进程的Service),如图:AMS是四大组件的生命周期的一个比较重要的类,IPC机制会涉及

58e5affdc7e6c.png

最后AMS接收到消息后,发送消息到MessageQueue里面,最后由系统的Handler处理启动Service的操作:

58e5b282509cd.png

在handlerCreateService()里通过反射的方式去newInstance(),并且毁掉了Service的Oncreate方法。

58e5b2de61799.png

又例如我们可以通过发SUICIDE消息可以自杀,这样来退出应用程序。

1
2
3
case SUICIDE:
Process.killProcess(Process.myPid());
break;

应用程序退出

实际上我们要退出应用程序的话,就是让主线程结束,换句话说就是要让 Looper 的循环结束。这里是直接结束 Looper 循环,因此我们四大组件的生命周期方法可能就不会执行了,因为四大组件的生命周期方法就是通过 Handler 去处理的,Looper 循环都没有了,四大组件还玩毛线!因此我们平常写程序的时候就要注意了,onDestroy 方法是不一定能够回调的。

58e5b38e7a18e.png

这里实际上是调用了 MessageQueue 的 quit,清空所有 Message。

1
2
3
public void quit() {
mQueue.quit(false);
}

4.消息机制的分析

4.1 消息对象Message的分析

提到消息机制,在MessageQueue里面存在的就是我们Message对象。

58e5b4e08f7e6.png

Message 里面有一些我们常见的参数,arg1 arg2 obj callback when 等等。这里要提一下的就是这个 target 对象,这个对象就是发送这个消息的 Handler对象,最终这条消息也是通过这个 Handler 去处理掉的。

4.2 消息的循环过程分析

1.拿到Looper()对象(me),如果当前线程没有Looper,那么就抛出异常。这就是为什么在子线程里面创建Handler如果不手动创建启动Looper会报错的原因。

2.拿到Looper的成员变量MessageQueue,在MessageQueue里面不断地去区消息,关于MessageQueue的next用法如下:

这里可以看到消息的取出用到了一些native方法,这样做是为了获得更高的效率,消息的去取出并不是直接就从队列的头部取出的,而是根据了消息的when时间参数有关的,因为我们可以发送延时消息、也可以发送一个指定时间点的消息。因此这个函数有点复杂,我们点到为止即可。

58e5ba6c7b791.png

58e5baf15d1d6.png

3、继续分析 loop方法:如果已经没有消息了,那么就可以退出循环,那么整个应用程序就退出了。什么情况下会发生呢?还记得我们分析应用退出吗?

在 系统Handler 收到 EXIT_APPLICATION 消息的时候,就会调用 Looper 的 quit方法:

58e5bacc9d2ee.png