0%

第4章:四大组件的工作过程

根Activity的启动过程

根Activity 是应用程序第一个Activity,相比普通的Activity的启动过程,一般用根Activity 的启动过程来指代应用程序的启动过程,更具有参考意义。根Activity 的启动过程比较复杂,这里分为3个部分来讲:Launcher 请求AMS 过程、AMS 到ApplicaitonThread 的调用过程 以及 ActivityThread 启动Activity

Launcher 请求AMS 过程

当我们点击桌面上某个应用的快捷图标时,就会通过Launcher 请求AMS 来启动该应用程序,过程的时序图如下:

Launcher调用AMS启动Activity时序图

点击桌面图标,会调用 Launcher 的startActivitySafely方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//  packages/apps/Launcher3/src/com/android/launcher3/Launcher.java 

public boolean startActivitySafely(View v ,Intent intent, Itemlnfo item){
...
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //l
try{
if (xxxx){
...
}else if(user == null || user.equals(Process.myUserHandle())){
startActivity(intent, optsBundle); //2
}else {
...
}
return true;
} catch (ActivityNotFoundExceptionlSecurityException e) {
...
}
return false;
}

因为是启动新的应用,所以注释1处将根Activity 在新的任务栈启动,应用启动会执行到注释2处的startActivity 方法,最终会在Activity 中调用 startActivityForResult 方法:

1
2
3
4
5
6
7
8
9
//  frameworks/base/core/java/and oid/app/Activity.java

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {//1
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar = minstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToke,this,intent, requestCode , option);
} else {
...
}

注释1的mParent 是Activity 类型,表示当前Activity 的父类(个人理解,这里应该说是当前Activity的前一个Activity),因此mParent == null 成立,最后由 Instrumentation 的execStartActivity方法来执行启动操作。 Instrumentation 主要用于监控应用程序和系统的交互。主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
//  frameworks/base/core/java/android/app/Instrumentation.java

public ActivityResult execStartActivity(Context who , IBinder contextThread, IBinder token , Activity target, Intent intent ,int requestCode ,Bundle options){
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who );
int result = ActivityManager.getService().startActivity(xxx,xxx,xxxx));
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;

由代码可知,首先通过 ActivityManager 获取 AMS 的代理对象,接着调用代理对象的 startActivity 方法。AMS 的代理对象是一个 IActivityManager(该类由AIDL在工具编译时自动生成的)对象,这个对象封装了 IBinder 类型的 AMS 的引用。通过一系列进程间通信,最终调用 AMS 的 startActivity 方法。

AMS 到 ApplicationThread 的调用过程

Launcher 请求进入AMS 后,接着是AMS 到 ApplicationThread 调用流程,时序图如下所示:

Launcher调用AMS启动Activity时序图

在AMS 的startActivity 会使用 startActivityAsUser 实现功能,并获取UserHandle.getCallingUserld() 即 调用者的UserId 作为参数传入。之后,startActivityAsUser 中会判断调用者进程是否被隔离,如果隔离则抛出SecurityException异常;接着,根据UserId 等参数检查调用者权限,如果没权限也抛出 SecurityException 异常。

AMS 中最终调用ActivityStater 的 startActivityLocked 方法,并且如果有 TaskRecord(代表启动的Activity所在的栈),则将其也作为参数传入;startActivityLocked 中会收集所有逻辑来决定如何将Intent 和Flags 转换为Activity(生成用于描述Activity 的 ActivityRecord 对象),并且将Activity 与Task 及 Stack 关联。

TaskRecord 用于描述一个 Activity 任务栈,Activity 任务栈其实是一个假想模型,并不真实存在。

最终调用到 ActivityStackSupervisor 的 startSpecificActivityLocked 方法时,会判断要启动的Activity 所在的应用程序进程是否已经运行,已经运行则调用 realStartActivityLocked 方法,并传入代表应用程序进程的 ProcessRecord。之后会调用 ApplicationThread 的 scheduleLaunchActivity 方法。当前代码逻辑运行在AMS所在进程(即SystemServer进程)中,通过 ApplicationThread 进程间通信,将程序执行到应用程序进程,ApplicationThread是AMS 进程与应用程序进程的通信桥梁,如下图所示:

AMS与应用程序进程通信

ActivityThread 启动Activity 的过程

由前面的知识可知,目前的代码逻辑已经运行到应用程序进程中,先查看下ActivityThread 启动Activity 的时序图:

ActivityThread启动Activity过程的时序图

ApplicationThread 是 ActivityThread 的内部类,前面讲过应用程序进程创建完成后,会运行代表主线程的实例 ActivityThread 。接着上一节的内容查看 ApplicationThread.scheduleLaunchActivity 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
//  frameworks/base/core/java/android/app/ActivityThread.java

public final void scheduleLaunchActivity(xxx,xx,xxx) {//参数太多,这里省略了参数
updateProcessState(procState ,false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident =ident;
r.intent = intent;
r.referrer = referrer;
...
updatePendingCoηfiguration (curConfig);
sendMessage(H.LAUNCH_ACTIVITY ,r);
}

在把启动Activity 必要的数据封装成 ActivityClientRecord 后,通过 sendMessage 方法将封装的数据以 H.LAUNCH_ACTIVITY 类型发送了出去,这里可以大胆地猜测sendMessage方法是通过handler的 sendMessage 执行的,果不其然,sendMessage 有多个重载方法,最终调用到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
//  frameworks/base/core/java/android/app/ActivityThread.java

private void sendMessage(int what ,Object obj ,int argl ,int arg2 ,boolean async){
Message msg = Message obta ();
msg.what = what;
msg.ob]= obj;
msg.argl = argl;
msg.arg2 = arg2;
if (async) {
msg .setAsynchronous(true);
}
mH.sendMessage(msg);

这里的mH指的是 ActivityThread 的内部类 H,前面讲过,这个H是集成Handler,是应用进程中主线程的消息管理类,因为ApplicationThread 是一个Binder,它的调用逻辑都运行在Binder 线程池中,所以这里需要使用H将代码的逻辑切换到主线程中。这样一来,我们只需要看 H 的handleMessage 方法即可知道具体的执行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
private class H extends Handler {
...
public void handleMessage (Message msg ) {
switch (msg.what) {
case LAUNCH_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;//1
r.packageinfo = getPackageinfoNoCheck(r.activityinfo.applicationinfo, r.compatlnfo); //2
handleLaunchActivity(r,null ,"LAUNCH ACTIVITY"); //3
Trace.traceEnd (Trace . TRACE TAG ACTIVITY MANAGER);
break;
...
}

在注释1处将传过来的 msg 的成员变量 obj 还原成 ActivityClientRecord,注释2获得LoadApk 类型的对象。应用程序进程要启动Activity时需要将该Activity 所属的APK 加载进来,而LoadApk 就是用来描述已经加载的APK 文件的。注释3处调用 handleLaunchActivity 方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//  frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r ,Intent customintent, String reason) {
...
WindowManagerGlobal.initialize ();
//启动 Activity
Activity a = performLaunchActivity(r, customintent);//1
if(a != null){
...
//将 Activity 的状态置为 Resume
handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);//2
}else{
...
//停止 Activity 启动
ActivityManager getServ ce () .finishActivity(r . token , Activity . RESULT CANCELED , null , Activity . DONT_FINISH_TASK_WITH_ACTIVITY) ;
}
}

注释1处performLaunchActivity 方法启动了 Activity,注释2处将Activity的状态设置为 Resume ,如果该Activity 为null,则会通知AMS 停止启动Activity。我们来看看 performLaunchActivity 方法做了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//  frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r , Intent customintent) {
//获取 Activityinfo
Activityinfo ainfo = r.activityinfo;//l
if (r.packageinfo == null) {
//获取 APK 文件的描述类 LoadedApk
r.packageinfo = getPackageinfo(ainfo.applicationinfo ,r.compatinfo,Context.CONTEXT_INCLUDE_CODE);//2
}
ComponentName component= r.intent.getComponent();//3
...
//创建要启动 Activity 的上下文环境
Contextlmpl appContext = createBaseContextForActivity(r) ; //4
Activity activity = null;
try {
java.lang .ClassLoader cl= appContext.getClassLoader();
//用类加载器来创建该 Activity 的实例
activity = mInstrumentation newActivity(cl ,component.getClassName() ,r.intent) ;//5
...
} catch (Exception e) {
...
}

try {
//创建 Application
Application app = r.packageinfo.makeApplication(false ,minstrumentation); //6
...
if (activity != null) {
//初始化Activity,参数太多,省略
activity.attach(....) ;
...
if (r.isPersistable()) {
minstrumentation.callActivityOnCreate(activity,r.state ,r.persistentState); //8
} else {
minstrumentat on callActivityOnCreate(activ ty r .state) ;
}
...
}
r.paused = true ;
mActivities.put(r.token,r);
} catch (SuperNotCalledException e) {
throw e ;
}catch (Exception e) {
...
}

return activity;

注释1处获取 Activityinfo,用于存储AndroidManifest 以及 代码中设置的Activity 和 Receiver 节点的信息,比如Activity 的theme 和launchMode 。注释3中获取要启动的Activity 的 ComponentName 对象,该对象中保存了该Activity 的包名和类名。注释4中启动了Activity的上下文,注释5根据 Activity 的类名,用类加载器创建该 Activity 的实例。之后,注释6中创建了Application ,并且会调用 Application 的 onCreate方法。注释7中调用 Activity 的attach 方法初始化Activity,并且创建Window 对象(PhoneWindow)与Activity 自身关联。注释8中正式启动Activity,并调用Activity 的onCreate 方法。

至此,根Activity 就启动了,即应用程序启动了。

根Activity 启动过程中涉及的进程

根Activity 启动过程中涉及的4个进程之间关系如下:

根Activity启动过程中涉及的进程关系

首先Launcher 进程向 AMS 请求创建根 Activity ,AMS 会判断根Activity 所需要的应用程序进程是否存在,不存在就请求 Zygote 进程创建应用程序进程;之后,AMS 请求创建根Activity。上图中步骤 2 采用Socket 通信,步骤 1 和4采用Binder 通信。

读完书本,虽然各个点清晰,但是未能完整总结,以下 桌面点击图标 启动流程总结参考自他人博客

  1. 点击桌面图标,Launcher 采用Binder IPC 方式向system_server 发起startActivity 请求。
  2. system_server 进程接收到请求后,向 zygote 进程发送创建进程请求。
  3. zygote 进程fork 出新进程,即App进程。
  4. App 进程通过 Binder IPC 向 system_server 发起 attachApplication 请求。
  5. system_server收到请求做一系列准备后,通过 Binder IPC 向App 进程发送 scheduleLauncherActivity请求。
  6. App 进程的Binder 线程(ApplicationThread)收到请求后,通过Handler 向主线程发送 LAUNCH_ACTIVITY 消息。
  7. 主线程收到Message 后,通过反射机制创建目标Activity ,并回调Activity.onCreate 等方法。
  8. 至此,App启动,开始Activity 生命周期。

这个过程示意图如下所示:

Activity启动流程图

Service 启动过程

Service 的启动过程和根Activity 的启动过程有部分相似知识点。Service 的启动过程可以分为两个部分讲解:分别是ContextImpl 到ActivityManageService 的调用过程,以及 ActivityThread 启动 Service。

ContextImpl 到 AMS 的调用过程

首先上时序图:

ContextImpl到AMS的调用过程

调用startService方法启动service,这个方法在 ContextWrapper 中实现

谢谢你的鼓励