乐于分享
好东西不私藏

App 启动流程

App 启动流程

当我们在 Android 手机桌面上点击图标运行一个 APP 之后到底发生了什么事情,为什么会启动一个应用并展示 UI 界面,接下来让我们了解一下当在桌面上点击图标运行一个 APP 的大概过程,我们先通过流程图的方式简单理解一下:
从上图中我们就可以了解到 APP 启动的大概流程,Launcher 进程也就是我们的桌面进程,从图中可以看到我们的 APP 进程是通过 Zygote 进程 fork 得到的,并启动 ActivityThread的 main 函数,接下来我们从这条线开始往下分析 WMS 在整个 APP 启动流程中扮演的角色。
由于源码过多,所以在我们讲解源码之前,我们可以先想一想,想要启动一个 APP 进程,我们需要什么数据以及需要做什么处理?
  1. 需要从 AMS 中拿到资源文件。
  2. 实例化 Application 对象并注册生命周期回调
  3. 执行 launch 生命周期,在这其中应该会实例化 Activity 对象并执行 onCreate() 方法
  4. 执行 onCreate() 方法,构建 PhoneWindow、DecorView、mContentWindow 之间的关系并加载所有的 xml 文件,将其转换为 View 并相互联系。
  5. 执行 resume() 生命周期,先执行 resume 回调,并创建 ViewRootImpl 管理所有的 View 对象,最后执行 requestLayout() 进行实际的绘制工作,最终会展示 UI 画面供我们操作。
接下来我们来根据这些步骤来逐一分析一下源码:
1. 通过 Binder 通信从 AMS 中拿到 Activity 资源文件。
别的我们不关心,只需要了解一下 attach() 这个初始化方法做了什么。
可以看到 attach() 方法就干了一件事情,先拿到关于 AMS 的 Proxy 对象,然后调用 AMS 中的 attachApplication() 方法, 告知 AMS(APP 进程已就绪);AMS 收到后,会将 Activity 的启动参数通过 Binder 下发给 APP 进程。
2. 实例化 Application 对象并注册生命周期回调
在 attachApplicationLocked() 方法中主要的目的就是通过 Binder 调用 ActivityThread 中的 bindApplication() 方法,并在最下面调用 ActivityStackSupervisor 的 attachApplicationLocked() 方法,我们分别走进去看一下。
在 bindApplication() 方法中,主要目的就是将从 AMS 中拿到的 Activity 数据进行组装并通过 Handler 发送出去,接下来我们肯定就会知道 Handler 会依据自己的事件机制来执行对应的回调方法。
这个方法内部主要会反射构建 Application 对象并执行 onCreate() 方法,接下来来看一下ActivityStackSupervisor 的 attachApplicationLocked() 方法。
这段代码的主要作用就是在 AMS 这边添加 Activity 的生命周期回调机制并执行事务,最终会执行到 LaunchActivityItem的 execute() 方法中执行 launch 生命周期。
3. 执行 launch 生命周期
可以看到在 LaunchActivityItem的 execute() 方法中最终会调用到 ClientTransactionHandler 这个抽象类的 handlelaunchActivity 方法,最终会走到子类也就是 ActivityThread 的 handlelaunchActivity 中。
这个方法会根据拿到的 Activity 数据来实例化 Activity 出来,我们来看一下 performLaunchActivity() 方法。
可以看到最终会进行反射构建 Activity 对象并执行 onCreate() 方法,接下来就会走到我们自己写的 onCreate() 方法中,launch 生命周期也就结束了。
4. 执行 onCreate() 方法。
可以看到首先会调用 setContentView 方法,这个方法首先会通过 getWindow()方法拿到 PhoneWindow 对象并执行其 setContentView 方法。
这个方法中有两个重要的点,第一个就是通过 installDecor() 方法初始化 DecorView 对象和 mContentParent 对象,第二点就是通过 LayoutInflater.inflate() 方法来递归创建 View 对象并建立联系。我们来逐一分析一下:
这个方法中会通过 generateDecor() 方法来创建 DecorView 对象和 mContentParent 对象。
创建 DecorView 对象没有什么特殊的点,重点看一下 mContentParent 对象是怎么创建出来的。
我们只看最核心的代码,首先先看第一点,通过 onResourcesLoaded 方法将系统布局加载到 DecorView 中,DecorView 作为父容器。第二点就是从 DecorView 中找到 id 为 content 的 ViewGroup 作为 mContentParent,我们后续设置的 Activity 就是在 mContentParent 基础上构建的。我们来看一下 onResourcesLoaded 怎么加载的。
可以看到第一次进来会创建基础布局生成对应的 View 对象。将 DecorView 和 mContentParent 对象创建完成之后 indtallDecor()方法就结束了,接下来我们来重点看一下 LayoutInflater.inflate() 方法干了什么,注意这一次会将我们自己创建的 resId 和 mContentParent 作为参数传入。那根据上面的 inflate() 方法,最终会走到 rInflateChildren 方法中。
可以看到最终会递归生成所有的 View 对象,并建立 View 之间的父子关系,最终的结果就是我们所有自定义的 View 树会挂载到 mContentParent下面,而 mContentParent 又在 DecorView 下面。到此时,我们的 onCreate() 方法也就结束了。
此时我们的 launch 生命周期也就彻底结束了,但是我们整个 APP 的启动流程还没有走完。
从这段代码我们就可以知道接下来会走到 resume 的生命周期。
5. 执行 resume() 生命周期
首先会执行我们自定义的 onResume 方法。
最终在这个方法中会创建 ViewRootImpl,并将我们传入的 DecorView 交给 ViewRootImpl 进行管理。
这个方法首先会进行 DecorView 和 ViewRootImpl 双向绑定,最终就会走到我们的 requestLayout() 方法进行最终的 UI 绘制阶段,这个过程由于太过复杂,我们会放到一篇单独的文章进行讲解,此时到这个阶段我们的 APP 启动过程就讲完了。
我觉得这个过程中最重要的就是我们需要建立起一种立体的关系图,通过关系图来理解每个对象的作用。
这是一个简单的层级关系图,最外层的就是 PhoneWindow,内部存在 DecorView 对象作为 View 的根 View, DecorView 分为两部分,一部分作为 Title Bar,另一部分就是 mContentParent,我们后续实现的子 View 就是在这基础上构建的,最终我们的根 View 也就是 DecorView 会被 ViewRootImpl 进行管理。
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » App 启动流程

评论 抢沙发

6 + 2 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮