Android应用程序启动流程(二)
一、前言
在上一篇文章中,我们分析了应用程序进程启动的流程。那么,在应用程序进程已启动的情况下,应用程序又是如何启动的呢?在这篇文章中,我们将从点击应用快捷图标开始,分析应用程序是如何启动的,即根Activity是如何启动的。
二、根Activity启动流程
2.1 Launcher 请求 AMS 过程
还记得Android系统启动流程中,最后一个阶段是什么嘛?没错,是Launcher应用程序的启动,也就是手机的桌面。它是Zygote进程孵化的第一个APP。在启动过程中,它会请求PackageManagerService返回系统中已经安装的应用程序的信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,用户可以通过点击图标来启动相应的应用程序。
当我们点击应用图标时,Launcher的onClick方法被调用,该方法如下
1 | |
如果点击的时应用快捷图标,会调用onClickAppShortcut方法,进而调用startAppShortcutOrInfoActivity方法,获取视图对象的Intent,然后调用startActivitySafely方法,设置Intent的Flag为FLAG_ACTIVITY_NEW_TASK,指示Android系统通过创建一个新的任务栈来启动Activity,之后会调用startActivity方法,该方法如下
1 | |
在startActivity方法中,无论哪个分支,最终都会调用三个参数的startActivityForResult方法,第二个参数requestCode = -1表示Launcher不需要知道Activity启动的结果,第三个参数则代表Activity启动的方式。
在三个参数的startActivityForResult方法中,mParent是Activity类型的,表示当前Activity的父类。很显然,我们要启动的应用程序的第一个Activity(也就是根Activity)还没有创建出来,所以调用mInstrumentation的execStartActivity方法,mInstrumentation主要用来监控应用程序和系统的交互。execStartActivity方法如下
1 | |
先通过活动监视器检查正在启动的Activity是否受监视或拦截,如果没有,则调用ActivityManager的getService方法获得AMS的代理对象,接着调用startActivity方法。
首先看getService方法做了什么,该方法如下
1 | |
getService方法调用了IActivityManagerSingleton的get方法,而IActivityManagerSingleton是一个Singleton类,这个类中get方法调用了create方法。它在创建的时候重写了create方法,通过getService方法获取名为 “activity” 的Service引用,也就是IBinder类型的AMS引用,接着将它转换成IActivityManager类型的对象(采用的是AIDL,IActivityManager.java类是由AIDL工具编译IActivityManager.aidl时生成的)。AIDL的作用是实现跨进程通信(IPC),在这里是建立服务端AMS和客户端Launcher之间的通信。
回到Instrumentation.java,接下来是调用startActivity方法。
2.2 AMS 到 ApplicationThread 的调用过程
Launcher请求AMS后,代码逻辑进入到AMS中, AMS的 startActivity方法如下
1 | |
与 startActivity方法相比, startActivityAsUser方法的参数多了一个UserHandle.getCallingUserId(),此方法用于获取调用者的UserId。接着在startActivityAsUser方法中,检查调用者进程是否被隔离,以及调用者权限,最后调用startActivityMayWait方法,该方法如下
1 | |
在startActivityMayWait方法中,我们主要关注startActivityLocked方法,该方法先判断了一下启动理由,然后调用startActivity方法(怎么这么多同名方法啊!)。该方法中,调用了mService.getRecordForAppLocked(caller)获得代表Launcher进程的callerApp对象,它是ProcessRecord类型的,ProcessRecord用于描述一个应用程序进程。参数caller是一路传过来的,指向的是Launcher所在的应用程序进程的AppliactionThread对象。
同样地,ActivityRecord用于描述一个Activity,记录其所用信息。ActivityRecord方法创建了用于描述将要启动的Activity信息的对象r,并将r赋值给outActivity[0],两者作为startActivity方法的参数传递下去。
1 | |
startActivity方法调用了startActivityUnchecked方法,该方法主要是处理与Activity栈管理相关的逻辑。在 2.1 中的 startActivitySafely方法,里面设置Intent的Flag为FLAG_ACTIVITY_NEW_TASK。所以这里会调用setTaskFromReuseOrCreateNewTask方法,创建一个新的TaskRecord用于描述Activity栈。之后会调用resumeFocusedStackTopActivityLocked方法,该方法如下
1 | |
走到这一步,新Activity栈已经创建好了,但是Activity还没创建好,所以调用后一个resumeTopActivityUncheckedLocked方法,该方法如下
1 | |
在resumeTopActivityUncheckedLocked方法中,根据mStackSupervisor.inResumeTopActivity标志来防止递归调用resumeTopActivityInnerLocked方法,之后调用resumeTopActivityInnerLocked方法,该方法代码巨多,主要关注startSpecificActivityLocked方法,该方法如下
1 | |
在startSpecificActivityLocked方法中,先通过getProcessRecordLocked方法获得即将启动的Activity的所在的应用程序进程,接着判断得到的应用程序进程是否已经运行了,如果没有运行,则调用startProcessLocked方法,沿着此方法继续下去,就是应用程序进程启动流程了。如果运行了,就调用realStartActivityLocked方法,该方法如下
1 | |
这里的app指传入的要启动的Activity所在的应用程序进程, app.thread指IApplicationThread,它的实现是ActivityThread的内部类AppliactionThread,这个类又是继承IApplicationThread.Stub的。
realStartActivityLocked方法主要是调用scheduleLaunchActivity方法,将启动Activity的请求发送给应用程序所在的进程。当前代码逻辑运行在AMS所在的进程(SystemServer进程)中,通过ApplicationThread来与应用程序进程进行Binder通信,即ApplicationThread是AMS所在的进程和应用程序进程的通信桥梁。
2.3 ActivityThread启动Activity的过程
在上一篇文章里讲过,应用程序进程创建后会运行代表主线程的实例ActivityThread,它管理者当前应用程序进程的主线程。 scheduleLaunchActivity方法就是在AppliactionThread类中的,该类又是ActivityThread的内部类。所以代码逻辑终于进入到应用程序进程中。scheduleLaunchActivity该方法如下
1 | |
scheduleLaunchActivity方法将传入的参数封装成ActivityClientRecord对象,sendMessage方法向H类发送类型为LAUNCH_ACTIVITY的消息,并将ActivityClientRecord传递过去。sendMessage方法有多个重载方法,调用到的如下所示
1 | |
mH是H类,它是ActivityThread的内部类并继承自Handler,是应用程序进程中主线程的消息管理类。因为ApplicationThread是一个Binder,它的调用逻辑运行在Binder线程池中,所以这里需要用H类将代码的逻辑切换到主线程中。
sendMessage方法将消息添加到消息队列中,消息处理程序会自动从队列中取出消息并调用对应的handleMessage方法进行处理,该方法在H类中,如下所示
1 | |
handleMessage对 LAUNCH_ACTIVITY消息的处理为:将参数obj(也就是在scheduleLaunchActivity方法中创建的ActivityClientRecord)转成ActivityClientRecord对象,通过getPackageInfoNoCheck的方法获得LoadedApk类型的对象并赋值给r.packageInfo。
为什么要获取LoadedApk呢?因为应用程序进程要启动Activity时,需要将该Activity所属的APK加载进来,而LoadedApk就是用来描述已加载的APK文件的。
接下来调用handleLaunchActivity方法,该方法如下
1 | |
取消GC操作,根据profilerInfo决定是否启动性能分析器,然后更新配置,初始化窗口管理器,调用performLaunchActivity方法启动Activity,如果启动成功,则进行相应的处理,包括保存配置信息、报告Activity尺寸配置、将Activity的状态设置为Resume等。如果Activity启动失败,则通知ActivityManager停止当前Activity。
接下来看performLaunchActivity方法,该方法如下
1 | |
先做一些准备工作,比如获取Activity的信息、获取APK文件描述信息、获取组件名称(ComponentName)、获取上下文环境(Context)。然后调用mInstrumentation的newActivity方法,通过类加载器创建Activity实例。
接着是调用makeApplication方法创建应用程序对象(Application)的实例。在 Android 中,每个应用程序都有一个全局的 Application 对象,该对象在整个应用程序的生命周期中存在,并且可以用于存储和共享应用程序的全局状态和数据。
之后是对刚创建的Activity实例进行初始化工作,最后调用callActivityOnCreate方法启动Activity。该方法如下
1 | |
关键是调用了Activity的performCreate方法,该方法如下
1 | |
在performCreate方法中会调用Activity的实际的onCreate方法,其具体的实现是由开发者来重写。到此,根Activity就启动了,即应用程序启动了。
三、总结
整个流程下来非常长,感觉有点杂乱,这里简单梳理一下整个应用程序启动的流程(假设应用程序进程没有运行):
- 点击桌面的应用图标,
Launcher进程使用Binder与SystemServer进程的AMS进行跨进程通信(IPC),发起startActivity请求。 SystemServer进程的AMS接收到请求后,通过zygoteSendArgsAndGetResult与zygote进程进行Socket通信,发送创建进程的请求。Zygote进程接收请求后,通过forkAndSpecialize方法创建应用程序进程,然后进行一系列初始化工作。- 启动了应用程序进程后,
AMS通过ApplicationThread来与应用程序进程进行Binder通信,发送scheduleLaunchActivity请求。 - 应用程序进程的
Binder线程(ApplicationThread)接收到请求后,通过sendMessage方法与主线程ActivityThread进行Handler通信,发送LAUNCH_ACTIVITY的消息。 - 主线程
ActivityThread接收到消息后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。到此,应用程序便正式启动,开始进入Activity生命周期。
参考:
Android深入四大组件(一)应用程序启动过程(前篇) | BATcoder - 刘望舒 (liuwangshu.cn)
Android深入四大组件(一)应用程序启动过程(后篇) | BATcoder - 刘望舒 (liuwangshu.cn)