(七)App启动流程第2篇

书接上文,App启动一共有七个阶段,上篇文章篇幅所限,我们只看了第一阶段,接下来讲剩余的六个阶段,仍然是拿斗鱼App举例子。

简单回顾一下第一阶段的流程,就是Launcher向AMS发送一个跨进程通信,通过AMN/AMP,告诉AMS,我要启动斗鱼App。

画一个图,描述一下启动App所经历的7个阶段:

第2阶段 AMS处理Launcher传过来的信息

这个阶段主要是Binder的Server端在做事情。因为我们是没有机会修改Binder的Server端逻辑的,所以这个阶段看起来非常枯燥,我尽量说的简单些。

  1. 首先Binder,也就是AMN/AMP,和AMS通信,肯定每次是做不同的事情,就比如说这次Launcher要启动斗鱼App,那么会发送类型为START_ACTIVITY——TRANSACTION的请求给AMS,同时会告诉AMS要启动哪个Activity。
  2. AMS说,好,我知道了,然后它会干一件很有趣的事情,就是检查斗鱼App中的Manifest文件,是否存在要启动的Activity。如果不存在,就抛出Activity not found的错误,各位做App的同学对这个异常应该再熟悉不过了,经常写了个Activity而忘记在Manifest中声明了,就报这个错,就是因为AMS在这里做检查。不管是新启动一个App的首页,还是在App内部跳转到另一个Activity,都会做这个检查。
  3. 但是Launcher还活着啊,所以接下来AMS会通知Launcher,哥们儿没你什么事了,你“停薪留职”吧。那么AMS是通过什么途径告诉Launcher的呢?

前面讲过,Binder的双方进行通信是平等的,谁发消息,谁就是Client,接收的一方就是Server。Client这边会调用Server的代理对象。

对于从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方法。这个方法干两件事:

  • ActivityThread里面有一个mActivities集合,保存当前App也就是Launcher中所有打开的Activity,把它找出来,让它休眠。
  • 通过AMP通知AMS,我真的休眠了。

你可能会找不到H和APT这两个类文件,那是因为它们都是ActivityThread的内嵌类。

至此,Launcher的工作完成了。你可以看到在这个过程中,各个类都起到了什么作用。芸芸众生,粉墨登场:

  • APT
  • ActivityThread
  • H

第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家族。

写给Android App开发人员看的Android底层知识(3)的更多相关文章

  1. 写给Android App开发人员看的Android底层知识(1)

    这个系列的文章一共8篇,我酝酿了很多年,参考了很多资源,查看了很多源码,直到今天把它写出来,也是战战兢兢,生怕什么地方写错了,贻笑大方. (一)引言 早在我还是Android菜鸟的时候,有很多技术我都 ...

  2. 写给Android App开发人员看的Android底层知识(2)

    (五)AMS 如果站在四大组件的角度来看,AMS就是Binder中的Server. AMS全称是ActivityManagerService,看字面意思是管理Activity的,但其实四大组件都归它管 ...

  3. 写给Android App开发人员看的Android底层知识(6)

    (十一)BroadcastReceiver BroadcastReceiver,也就是广播,简称Receiver. 很多App开发人员表示,从来没用过Receiver.其实吧,对于音乐播放类App,用 ...

  4. 写给Android App开发人员看的Android底层知识(5)

    (十)Service Service有两套流程,一套是启动流程,另一套是绑定流程.我们做App开发的同学都应该知道. 1)在新进程启动Service 我们先看Service启动过程,假设要启动的Ser ...

  5. 写给Android App开发人员看的Android底层知识(7)

    (十二)ContentProvider (1)ContentProvider是什么? ContentProvider,简称CP. 做App开发的同学,尤其是电商类App,对CP并不熟悉,对这个概念的最 ...

  6. 写给Android App开发人员看的Android底层知识(8)

    (十)PMS及App安装过程 PMS,全称PackageManagerService,是用来获取Apk包的信息的. 在前面分析四大组件与AMS通信的时候,我们介绍过,AMS总是会使用PMS加载包的信息 ...

  7. 写给Android App开发人员看的Android底层知识(4)

    (八)App内部的页面跳转 在介绍完App的启动流程后,我们发现,其实就是启动一个App的首页. 接下来我们看App内部页面的跳转. 从ActivityA跳转到ActivityB,其实可以把Activ ...

  8. 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 作者: 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Android APP开发入门教程,从SDK下载.开发环境搭建.代码编写.APP打包等步骤 ...

  9. 谋哥:App开发人员的苦逼不值得怜悯!!

    [谋哥每天一干货,第四十篇]         为什么取这个标题呢?由于昨天一些本来"支持"谋哥的人看到谋哥搞收费VIP群,认为谋哥赚苦逼开发人员的钱非常不道德,且说谋哥我写的东西都 ...

随机推荐

  1. 提高 webpack 构建 Vue 项目的速度

    前言 最近有人给我的 Vue2 后台管理系统解决方案 提了 issue ,说执行 npm run build 构建项目的时候极其慢,然后就引起我的注意了.在项目中,引入了比较多的第三方库,导致项目大, ...

  2. Git安装与上传代码至Github

    转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6642887.html 这篇文章应该是全网最新,最全,最靠谱的Github安装到上传代码的流程. 1.Git ...

  3. Entity Framework Code First在Oracle下的伪实现

    为什么要说是伪实现,因为还做不到类似MsSql中那样完全的功能.Oralce中的数据库还是要我们自己手动去创建的.这里,我们舍掉了Model First中的EDMX文件,自己在代码里面写模型与映射关系 ...

  4. 利用伪元素和css3实现鼠标移入下划线向两边展开效果

    一.思路: 将伪元素:before和:after定位到元素底部中间,设置宽度从0变成100%达到目的. 二.实现: 1.首先定义一个块状元素(行内元素没有宽高)并修改样式为一个背景色为浅灰色的矩形,设 ...

  5. php 启动过程 - sapi MSHUTDOWN 过程

    php 启动过程 - sapi MSHUTDOWN 过程 概述 当服务器关闭时, 会走到 sapi MSHUTDOWN 过程 注册过程 本次内容是在 php 启动过程 - sapi MINIT 过程 ...

  6. 微信开发(2)---微信小程序开发实战part1

    微信开发现在来说,简单可以概括为两部分,微信公众号和微信小程序. 微信公众号的技术已经非常成熟.分为服务号和订阅号.简单的.可以弄一个个人订阅号,在编辑模式下就可以实现推送图文.自动回复.自定义菜单的 ...

  7. 主机ping通虚拟机,虚拟机ping通主机解决方法(NAT模式)

    有时候需要用虚拟机和宿主机模拟做数据交互,ping不通是件很烦人的事,本文以net模式解决这一问题. 宿主机系统:window7 虚拟机系统:CentOs7 连接方式:NAT模式 主机ping通虚拟机 ...

  8. 2017/4/27-Gradle的配置与Spring的下载

    Gradle的配置与Spring的下载 1.Gradle 1) 介绍 Gradle是一个基于Groovy的构建工具,类似Maven,但是比其更加简单轻便.它可以自动化地进行软件构建.测试.发布.部署. ...

  9. Linux 练习(1)

    1) 新建用户natasha,uid为1000,gid为555,备注信息为"master" useradd -u 1000 -g 555 -c 'master' natasha2) ...

  10. [第一阶段] Python学习

    首先声明一下,我这个学习计划是关于学习Python的. 先说一下起因:我自己接触Python算是很久了,目前仍没学会,很失败,很惭愧.所以这次一方面简单分析一下自学会碰到的问题:另一方便,我想到了一种 ...