(七)App启动流程第2篇函数
书接上文,App启动一共有七个阶段,上篇文章篇幅所限,咱们只看了第一阶段,接下来说剩余的六个阶段,仍然是拿斗鱼App举例子。oop
简单回顾一下第一阶段的流程,就是Launcher向AMS发送一个跨进程通讯,经过AMN/AMP,告诉AMS,我要启动斗鱼App。插件
画一个图,描述一下启动App所经历的7个阶段:线程
第2阶段 AMS处理Launcher传过来的信息代理
这个阶段主要是Binder的Server端在作事情。由于咱们是没有机会修改Binder的Server端逻辑的,因此这个阶段看起来很是枯燥,我尽可能说的简单些。对象
前面讲过,Binder的双方进行通讯是平等的,谁发消息,谁就是Client,接收的一方就是Server。Client这边会调用Server的代理对象。blog
对于从Launcher发来的消息,经过AMS的代理对象AMP,发送给AMS。继承
那么当AMS想给Launcher发消息,又该怎么办呢?前面不是把Launcher以及它所在的进程给传过来了吗?它在AMS这边保存为一个ActivityRecord对象,这个对象里面有一个ApplicationThreadProxy,单单从名字看就出卖了它,这就是一个Binder代理对象。它的Binder真身,也就是ApplicationThread。队列
站在AIDL的角度,来画这张图,是这样的:进程
因此结论是,AMS经过ApplicationThreadProxy发送消息,而App端则是经过ApplicationThread来接收这个消息。
第3阶段 Launcher去休眠,而后通知AMS,我真的已经“停薪留职”了,没有吃空饷
ApplicationThread(简称APT),它和ApplicationThreadProxy(简称ATP)的关系,咱们在第三阶段已经介绍过了。
APT接收到来自AMS的消息后,就调用ActivityThread的sendMessage方法,向Launcher的主线程消息队列发送一个PAUSE_ACTIVITY消息。
前面说过,ActivityThread就是主线程(UI线程)
看到下面的代码是否是很亲切?
发送消息是经过一个名为H的Handler类的完成的,这个H类的名字真特么有个性,想记不住都难。
作App的同窗都知道,继承自Handler类的子类,就要实现handleMessage方法,这里是一个switch…case语句,处理各类各样的消息,PAUSE_ACTIVITY消息只是其中一种,由此也能预见到,AMS给Activity发送的全部消息,以及给其它三大组件发送的全部消息,都从H这里通过,为何要强调这一点呢,既然四大组件都走这条路,那么这里就能够作点手脚,从而作插件化技术,这个咱们之后介绍插件化技术的时候会讲到。
H对于PAUSE_ACTIVITY消息的处理,如上面的代码,是调用ActivityThread的handlePauseActivity方法。这个方法干两件事:
你可能会找不到H和APT这两个类文件,那是由于它们都是ActivityThread的内嵌类。
至此,Launcher的工做完成了。你能够看到在这个过程当中,各个类都起到了什么做用。芸芸众生,粉墨登场:
第4阶段 AMS启动新的进程
接下来又轮到AMS作事了,大家会发现我不太喜欢讲解AMS的流程,甚至都不画UML图,由于这部分逻辑和App开发人员关系不是很大,我尽可能说的简单一些,把流程说清楚就好。
AMS接下来要启动斗鱼App的首页,由于斗鱼App不在后台进程中,因此要启动一个新的进程。这里调用的是Process.start方法,而且指定了ActivityThread的main函数为入口函数。
第5阶段 新的进程启动,以ActivityThread的main函数做为入口
启动新进程,其实就是启动一个新的App。
在启动新进程的时候,为这个进程建立ActivityThread对象,这就是咱们耳熟能详的主线程(UI线程)。
建立好UI线程后,马上进入ActivityThread的main函数,接下来要作2件具备重大意义的事情:
1)建立一个主线程Looper,也就是MainLooper。看见没,MainLooper就是在这里建立的。
2)建立Application。记住,Application是在这里生成的。
- - - - - - - - - - - - - -华丽的分割线 开始- - - - - - - - - - - - - - -
App开发人员对Application很是熟悉,由于咱们能够在其中写代码,进行一些全局的控制,因此咱们一般认为Application是掌控全局的,其实Application的地位在App中并无那么重要,它就是一个Context上下文,仅此而已。
App中的灵魂是ActivityThread,也就是主线程,只是这个类对于App开发人员是访问不到的——使用反射却是能够修改这个类的一些行为。
- - - - - - - - - - - - - - -华丽的分割线 结束- - - - - - - - - - - - - - -
建立新App的最后,就是告诉AMS,我启动好了,同时把本身的ActivityThread对象发送给AMS,今后之后,AMS的电话簿中就多了这个新的App的登记信息,AMS之后向这个App发送消息,就经过这个ActivityThread对象。
第6阶段 AMS告诉新App启动哪一个Activity
AMS把传入的ActivityThread对象,转为一个ApplicationThread对象,用于之后和这个App跨进程通讯。还记得APT和ATP的关系吗?这就又回到第2阶段的那张关系图了。
还记得第1阶段,Launcher发送给AMS要启动斗鱼App的哪一个Activity吗?这个信息被AMS存下来了。
那么在第6阶段,AMS从过去的记录中翻出来要启动哪一个Activity,而后经过ATP告诉App。
第7阶段 启动斗鱼首页Activity
毕其功于一役,尽在第7阶段。这是最后一步。
App,这个Binder的另外一端,经过APT接收到AMS的消息,仍然是在H的handleMessage方法的switch语句中处理,只不过,此次消息的类型是LAUNCH_ACTIVITY:
ActivityClientRecord是什么?这是AMS传递过来的要启动的Activity。
仍是这里,咱们仔细看那个getPackageInfoNoCheck方法,这个方法会提取Apk中的全部资源,而后设置给r的packageInfo属性。这个属性的类型颇有名,叫作LoadedApk。各位记住这里,这个地方能够干坏事,也是插件化技术渗入的一个点。
在H的这个分支中,又反过来回调ActivityThread的handleLaunchActivity方法,你要是以为很绕那就对了。其实我一直以为,ActivityThread和H合并成一个类也没问题。
从新看一下这个过程,每次都是APT执行ActivityThread的sendMessage方法,在这个方法中,把消息拼装一下,而后扔个H的swicth语句去分析,来决定要执行ActivityThread的那个方法。每次都是这样,习惯就行了。
handleLaunchActivity方法都作哪些事呢?
1)经过Instrumentation的newActivity方法,建立出来要启动的Activity实例。
2)为这个Activity建立一个上下文Context对象,并与Activity进行关联。
3)经过Instrumentation的callActivityOnCreate方法,执行Activity的onCreate方法,从而启动Activity。看到这里是否是很熟悉很亲切?
至此,App启动完毕。这个流程是通过了不少次握手, App和ASM,频繁的向对方发送消息,而发送消息的机制,是创建在Binder的基础之上的。
下一篇文章,咱们讲Context家族。