本文参考
1. Gityuan博文:startActivity启动过程分析
2. 老罗博文:Activity组件的启动过程
整理文档 网盘地址:https://pan.baidu.com/s/1hrEn2O8
Activity
Activity是Android应用程序的四大组件之一,它负责管理Android应用程序的用户界面。一个应用程序一般会包含若干个Activity组件,每一个Activity组件负责一个用户界面的展现,它们可能运行在同一个进程中 ,也可能运行在不同的进程中。运行在不同进程中的Activity组件通过Binder进程间通信机制来协作完成应用程序的功能。
从应用程序的角度出发,我们可以将Activity组件划分为两种类型:一种是根Activity,另一种是子Activity。根Activity以快捷图标的形式显示在应用程序启动器中,它的启动过程就代表了一个Android应用程序的启动过程。子Activity由根Activity或者其他子Activity启动,它们有可能与启动它们的Activity运行在同一个进程中,也有可能运行在不同的进程中,这取决于它们的配置和启动参数。
Activity组件的启动方式分为显示和隐式两种。对于显示启动的Activity组件来说,我们必须事先知道用来实现它们的类的名称; 而对于隐式启动的Activity组件来说,我们只需要知道它们的组件名称即可,而不需要知道它们是由哪一个类来实现的。无论是显示启动的Activity组件,还是隐式启动的Activity组件,它们的启动过程都是类似的,唯一的区别在于系统是根据类名还是组件名称来找到它们。但是从软件工程的角度来看,隐式启动Activity组件可以减少Android应用程序组件间的依赖。
根Activity组件的启动过程
指令:adb shell dumpsys activity activities
该指令可以查看栈中Activity列表,包含了task等信息。从下面的图一来看,当前只有一个任务栈#0,栈中有两个任务,任务#3是我们直观可见的Launcher,任务#4是长按任务键可见的最近任务列表。这种情况是默认的场景。
图1、默认场景
图2、Launcher启动My Test应用
前言介绍:
My Test应用根Activity为MainActivity,拥有两个子Activity。MainActivity与SubActivity1运行在同一个进程中,而SubActivity2处于不同的进程中。从Launcher点击启动My Test应用,默认启动MainActivity。结合图1、2来看,由于之前没有启动过,此次启动MainActivity会创建一个新的任务栈,即任务栈#1,栈中只运行了一个根Activity。
根Activity:
子Activity:
从上面的图2来看,MainActivity位于任务栈#1中,运行在进程com.example.calf1234.mainprocess/u0a57中,与Launcher组件所处的进程不同。换而言之,启动Main Activity需要创建一个新的进程。本章节将根据events log来分析根Activity的启动过程。
图3、My Test启动events log
从events log来看,大致可推出MainActivity的启动过程:
1、Launcher组件向AMS发送一个启动MainActivity组件的进程间通信请求。
2、AMS(ActivityManagerService)首先将要启动的MainActivity组件的信息保存下来,然后再向Launchr组件发送一个进入中止状态的进程间通信请求。
3、Launcher组件进入到中止状态之后,就会向AMS发送一个已进入中止状态的进程间通信请求,以便AMS可以继续执行启动MainActivity组件的操作。
4、AMS发现用来运行MainActivity组件的应用程序进程不存在,因此,它就会先启动一个新的应用程序进程。
5、新的应用程序进程启动完成之后,就会向AMS发送一个启动完成的进程间通信请求,以便AMS可以继续执行启动MainActivity组件的操作。
6、AMS将step 2保存下来的Main Activity组件的信息发送给step 4创建的应用程序进程,以便它可以将MainActivity组件启动起来。
图4、Activity组件启动流程图
1.Launcher通知AMS启动MainActivity
step5. 给intent设置了FLAG_ACTIVITY_NEW_TASK标志位。此标志位意味着一般从Launcher启动应用都会新建一个调用栈,除非之前有遗留的未被销毁的调用栈。
step6. 默认的requestcode值为-1,即不返回结果。
Step8.
在Instrumentation.java文件中有三个execStartActivity方法,在Activity类中调用此方法2。重要参数:
contextThread:传递进来的为类型ApplicationThead的IBinder对象,实际上代表Launcher组件所运行的应用程序进程。
Token:传递进来的是一个IBinder代理对象,该对象指向了AMS中对应的ActivityRecord对象,即保存有Launcher组件信息。
target:Activity对象,这里指的是Launcher组件。
intent:携带即将要启动的组件的信息,这里指的是MainActivity组件。
与1的差别在于param4,1中为Activity对象,2中则为String对象。
step9. 此步骤,Launcher进程开始通过AMS代理对象与AMS进行Binder进程间通信。换而言之,Launcher组件向AMS发送一个启动MainActivity组件的进程间通信请求。
2.保存MainActivity信息,后通知Launcher中止
step1. 将需奥传递的数据封装成Parcel对象,、通过Binder进程间通信机制与AMS进行交互,附带子命令:START_ACTIVITY_TRANSACTION。
step2. 重点关注caller、intent、resultTo参数。
step5、6. 通过PMS获取最匹配intent的组件,此处代表的是即将要启动的MainActivity组件详细信息。
step7. 在AMS中新建与即将要启动的MainActivity组件相对应的ActivityRecord对象。
step8. 方法一开始进行一些初始化操作,主要还是为intent进一步设置标志位,同时根据标志位做一些特定地操作,比如新建任务栈。
之前设置了FLAG_ACTIVITY_NEW_TASK标志位,这里会新建一个task。
这里对上了一部分的events log。
step11. 在启动MainActivity组件的过程中发现Launcher组件并没有pausing。
step12. 此时此刻向Launchr组件发送一个进入中止状态的进程间通信请求,同时在events log中输出Launcher组件am_pause_activity信息。
3.Launcher中止OK,通知AMS继续启动
step2. 利用Handler沟通ApplicationThread与ActivityThread。
step6. 该方法内通过调用Instrumentation类的callActivityOnPause方法来中止Launcher组件,执行完该代码后,events log中会输出am_on_pause_called信息。
step8. 6804行代码很熟悉,会调用自己继承Activity类重写的方法onPause。
step9. Launcher组件自己中止OK后,通知AMS继续启动MainActivity组件。
4.AMS继续启动MainActivity组件
step4. 继续启动MainActivity组件。
step7. MainActivity组件对应的ProcessRecord和ApplicationThread对象不存在。
step8. 基于step7,发现MainActivity组件所属的进程不存在,故先去创建进程。
5.AMS为MainActivity组件创建新进程
step4. 通知zygote fork一个子进程,返回的ProcessStartResult结果带有pid进程号。
step15. BindApplication时,创建context、Instrumentation、Application类型对象。
6.进程创建OK,继续启动MainActivity组件
step1. 获取即将要启动的MainActivity组件。
此时hr值不为空,故调用realStartActivityLocked方法去启动MainActivity组件。
step3. 通知My Test应用程序进程去启动MainActivity组件。