阅读本文大概需要 4 分钟。
上一篇,我们了解到Android里触摸事件是如何一步一步转入UI线程的message queue里被执行的,这种事件是由外部事件触发的。
我接着对小张说:其实Android里还有一种UI queue里的事件更为大家熟知,你天天写代码都在与之打交道,你知道吗?
小张有些丈二和尚摸不着头脑,想了一会儿问道:能给一些提示吗?
我提示道:它是Android系统框架层产生的事件,你在四大组件上写的代码均无法逃脱它的掌控!
小张虽然不是很清楚为什么,但是由于提示太明显,问道:你说的难道是四大组件的生命周期?
我肯定道:没错,比如你天天写Activity,在其onCreate, onResume等生命周期里写业务代码,那你知道四大组件的生命周期是怎么来的吗?
小张怀疑到:难道它们也是handler消息机制触发的吗?
我说道:你没有听错!就连四大组件的生命周期也遵循了这个事件驱动模型,它们均是由Android系统框架层产生相应的message扔进UI queue触发的。
小张紧接着问道:如果这样的话,UI线程里必然存在一个handler在处理对应的message,以辨别这个message是哪个组件,是什么生命周期阶段。
我点了点头,道:你说得没错!你在Android源码里见过这个handler吗?
小张摇了摇头:我平时业务做得比较多,对Android系统框架层的源码看得比较少。
我听了后说道:那你平时可得多关注关注一些底层原理类的东西了,业务是永远在变动,而越是底层的东西越是相对稳定的,只有弄清楚基础才能知其所以然,更好的为业务服务。
小张听后,连忙点头:你说得是,回去一定恶补这块短板。
我继续说道:好了,Android源码里有个ActivityThread内部类H就是刚才所说的handler了,你看看它的源码,你就知道它都在干些什么了。
1private final class H extends Handler {
2 ...
3 public void handleMessage(Message msg) {
4 if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
5
6 switch (msg.what) {
7
8 case LAUNCH_ACTIVITY: {
9 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
10 r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo);
11 handleLaunchActivity(r, null);
12 }
13 break;
14
15 case RELAUNCH_ACTIVITY: {
16 ActivityClientRecord r = (ActivityClientRecord)msg.obj;
17 handleRelaunchActivity(r);
18 }
19 break;
20
21 case PAUSE_ACTIVITY:
22 handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
23 maybeSnapshot();
24 break;
25
26 case PAUSE_ACTIVITY_FINISHING:
27 handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
28 break;
29
30 case STOP_ACTIVITY_SHOW:
31 handleStopActivity((IBinder)msg.obj, true, msg.arg2);
32 break;
33
34 case STOP_ACTIVITY_HIDE:
35 handleStopActivity((IBinder)msg.obj, false, msg.arg2);
36 break;
37 ...
38 case RESUME_ACTIVITY:
39 handleResumeActivity((IBinder)msg.obj, true, msg.arg1 != 0);
40 break;
41 case SEND_RESULT:
42 handleSendResult((ResultData)msg.obj);
43 break;
44 case DESTROY_ACTIVITY:
45 handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,msg.arg2, false);
46 break;
47 ...
48 case NEW_INTENT:
49 handleNewIntent((NewIntentData)msg.obj);
50 break;
51 case RECEIVER:
52 handleReceiver((ReceiverData)msg.obj);
53 maybeSnapshot();
54 break;
55 case CREATE_SERVICE:
56 handleCreateService((CreateServiceData)msg.obj);
57 break;
58 case BIND_SERVICE:
59 handleBindService((BindServiceData)msg.obj);
60 break;
61 case UNBIND_SERVICE:
62 handleUnbindService((BindServiceData)msg.obj);
63 break;
64 ...
65 case STOP_SERVICE:
66 handleStopService((IBinder)msg.obj);
67 maybeSnapshot();
68 break;
69 ...
70 }
71 if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
72 }
73 ...
74}
从上面的代码,我们可以清晰的看到四大组件的生命周期函数调用赫然在列!
小张看了代码后发问了:我看到Activity生命周期相应的每个msg.obj前面都用了IBinder进行了强转,是不是说明这些message都不是App自己进程里产生扔过来的?
我笑道:你的眼力不错嘛。这就问到message的事件来源问题了,你说的不错,你平时打开一个Activity都有哪些方式?
小张说道:要么显示,要么隐式的startActivity。
我继续问道:嗯,那你知道这个过程大概是怎么样的吗?
小张说道:这个我知道,用户App会通过Binder IPC通信询问AMS(ActivityManagerService),向其索要满足条件的Activity。
我说道:没错,我们知道AMS是在系统的SystemServer进程中,统管Android上的所有Activity(这又是典型解耦手段--集中管理的HUB思想)。当找到了对应的Activity之后,由于跨进程,就通过Binder IPC手段来通知用户App进程所在的UI线程来打开对应的Activity。
小张接道:所以,AMS就把这种意图打包进message里,通过Binder IPC扔进UI线程的message queue中[注],当UI线程唤醒时,取出message交由H实例handler处理时,就进入了上述代码的switch case分支,发现了是LAUNCH_ACTIVITY,就调用handleLaunchActivity处理逻辑,其中就嵌入了Activity的onCreate, onStart调用,预留给开发者重写具体的业务逻辑。
我哈哈笑道:你都学会抢答了,进步很快啊,孺子可教也。所以你看,为什么平时说在UI线程的生命周期做繁重的耗时任务会导致UI卡顿或者ANR?
小张答道:由于任何UI线程的业务代码均逃离不了组件的生命周期,而生命周期又源于UI queue中的message的处理,所以如果在任何一个生命周期做了耗时任务,这会导致queue中后面的message无法得到及时的处理,所以看起来就是有反应延时,也就是视觉上的卡顿,严重的会长时间得不到处理,从而导致ANR的发生。
我肯定的点了点头:你看,底层原理一通百通,平时Android开发时要遵循的在这里得到了真实的解答,是不是感觉理解更加深刻了?
小张兴奋的说道:是啊,知道了为什么之后,感觉有种入木三分的感觉,以后在开发中就绝对不会犯这样的错误了。
最后我又继续补充道:其实这其中也蕴含了一种设计思想。当你想设计一套底层框架系统,而且又希望上层应用遵循你的规则,就需要预留这样的接口或者抽象函数,以供开发者来具体实现。
小张说道:这就是依赖倒置思想吧?
我说道:是的。其实Android系统源码里有大量的设计模式的运用,有机会可以好好看看。
小张:嗯,看来底层原理还挺有意思的,弄懂后还能加强对上层应用的理解,真是非常有必要系统性的学习了。
[注]:这里AMS其实并不是和UI线程直接打交道,而是通过App端的Binder线程,然后再传递给UI线程。如下图:
有热爱Android技术的同学,欢迎加微信公众号 xh18310039919。用诙谐的方式学习Android硬核知识点。