女儿拿着小天才电话手表问我App启动流程(上)
前言
首先,new一个女儿,
var mDdaughter = new 女儿("6岁",“漂亮可爱”,“健康乖巧”,“最喜欢玩小天才电话手表和她的爸爸”)
好了,女儿有了,有一天,女儿问我:
“爸爸爸爸,你说我玩的这个小天才电话手表
怎么这么厉害,随便点一下这个小图片,这个应用就冒出来了,就可以听儿歌了。好神奇啊。”
我心里一惊:
小天才电话手表的系统就是Android
,所以这不就是。。面试官常考的应用启动流程嘛!
女儿也要来面试我了吗!😭
好了,既然女儿问了,那就答吧。
但是,对付这个小小的0经验面试官,我该咋说呢?
解答小小面试官
女儿,你可以把手表里面想象成一个幼儿园
,里面有一个老师,一个班长,一个班干部,以及一大堆小朋友。
一个老师
:Z老师(Zygote进程)一个班长
:小A(ActivityManagerService)一个班干部
:小L(Launcher桌面应用)一大堆小朋友
:所有应用,包括音乐小朋友,聊天小朋友,日历小朋友等等。
应用启动过程就像一个小朋友被叫醒
一样,开机之后呢,Z老师会依次叫醒班长和班干部
(SystemServer#ActivityManagerService,Launcher),小L醒了之后就会去了解手表里有哪些小朋友,长什么样(icon,name),家庭信息(包名,androidmanifest)等等,然后一个个把小朋友的照片(icon)
贴到自己的身上。比如有音乐小朋友,聊天小朋友,日历小朋友,其实也就是你手表上这个桌面啦。
这时候你要点开一个音乐小朋友呢(startActivity),小L就会通知班长小A(Binder)
,小A知道了之后,让小L自己休息下(Paused),然后就去找Z老师
了。Z老师就负责叫音乐小朋友起床了(fork进程,启动ActivityThread),音乐小朋友起来后就又找小A带她去洗脸刷牙
(启动ApplicationThread,Activity),都弄完了就可以进行各种表演了,唱歌啊,跳舞啊。
不是很明白啊?我们一起聊个天你就懂了,假如我是Launcher
:
女儿似懂非懂的给我点了一个赞👍,爸爸你真棒。
十五年后
mDdaughter.grow(15)
mDdaughter.study("Android")
过了十五年,女儿已经21岁了,正在学习Android
,考虑要不要女从父业。
这天,她一脸疑惑的来找我: “爸,这个app启动到底是怎么个流程啊,我看了好久还是不大明白,要不你再跟我详细讲一遍吧?” “好嘞,别担心,我这次详细跟你说说”
解答Android程序媛
还记得我小时候跟你说过的故事吗,Android系统
就像一个幼儿园,有一个大朋友叫Launcher
,身上会贴很多其他小朋友的名片。这个Launcher
就是我们的桌面了,它通过PackageManagerService
获知了系统里所有应用的信息,并展示了出来,当然它本身也是一个应用。
通过点击一个应用图标,也就是触发了点击事件,最后会执行到startActivity
方法。这里也就和启动Activity
步骤重合上了。
那么这个startActivity
干了啥?是怎么通过重重关卡唤醒这个应用的?
首先,介绍下系统中那些重要的成员,他们在app启动流程中都担任了重要的角色.
系统成员介绍
init进程
,Android系统启动后,Zygote并不是第一个进程,而是linux的根进程init进程,然后init进程才会启动Zygote进程。Zygote进程
,所有android进程的父进程,当然也包括SystemServer进程SystemServer进程
,正如名字一样,系统服务进程,负责系统中大大小小的事物,为此也是启动了三员大将(ActivityManagerService,PackageManagerService,WindowManagerService)以及binder线程池。ActivityManagerService
,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,对于一些进程的启动,都会通过Binder通信机制传递给AMS,再处理给Zygote。PackageManagerService
,主要负责应用包的一些操作,比如安装,卸载,解析AndroidManifest.xml,扫描文件信息等等。WindowManagerService
,主要负责窗口相关的一些服务,比如窗口的启动,添加,删除等。Launcher
,桌面应用,也是属于应用,也有自己的Activity,一开机就会默认启动,通过设置Intent.CATEGORY_HOME的Category隐式启动。
搞清楚这些成员,就跟随我一起看看怎么过五关斩六将,最终启动了一个App。
第一关:跨进程通信,告诉系统我的需求
首先,要告诉系统,我Launcher
要启动一个应用了,调用Activity.startActivityForResult
方法,最终会转到mInstrumentation.execStartActivity
方法。 由于Launcher自己处在一个单独的进程,所以它需要跨进程告诉系统服务我要启动App的需求。 找到要通知的Service,名叫ActivityTaskManagerService
,然后使用AIDL,通过Binder与他进行通信。
这里的简单说下ActivityTaskManagerService
(简称ATMS)。原来这些通信工作都是属于ActivityManagerService,现在分了一部分工作给到ATMS,主要包括四大组件的调度工作。也是由SystemServer进程直接启动的,相关源码可见ActivityManagerService.Lifecycle.startService
方法,感兴趣朋友可以自己看看。
接着说跨进程通信,相关代码如下:
//Instrumentation.java
int result = ActivityTaskManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
//ActivityTaskManager.java
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
new Singleton<IActivityTaskManager>() {
@Override
protected IActivityTaskManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
}
};
//ActivityTaskManagerService.java
public class ActivityTaskManagerService extends IActivityTaskManager.Stub
public static final class Lifecycle extends SystemService {
private final ActivityTaskManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityTaskManagerService(context);
}
@Override
public void onStart() {
publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
mService.start();
}
}
startActivity
我们都很熟悉,平时启动Activity都会使用,启动应用也是从这个方法开始的,也会同样带上intent
信息,表示要启动的是哪个Activity。
另外要注意的一点是,startActivity之后有个checkStartActivityResult
方法,这个方法是用作检查启动Activity的结果。当启动Activity失败的时候,就会通过这个方法抛出异常,比如有我们常见的问题:未在AndroidManifest.xml注册。
public static void checkStartActivityResult(int res, Object intent) {
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to start activity "
+ intent);
case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException(
"PendingIntent is not an activity");
//...
}
}
第二关:通知Launcher可以休息了
ATMS收到要启动的消息后,就会通知上一个应用,也就是Launcher
可以休息会了,进入Paused状态。
//ActivityStack.java
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
//...
ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
//...
boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
pausing |= startPausingLocked(userLeaving, false, next, false);
}
//...
if (next.attachedToProcess()) {
//应用已经启动
try {
//...
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
getDisplay().mDisplayContent.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
//...
} catch (Exception e) {
//...
mStackSupervisor.startSpecificActivityLocked(next, true, false);
return true;
}
//...
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
next.completeResumeLocked();
} catch (Exception e) {
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
"resume-exception", true);
return true;
}
} else {
//冷启动流程
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
}
这里有两个类没有见过:
ActivityStack
,是Activity的栈管理,相当于我们平时项目里面自己写的Activity管理类,用于管理Activity的状态啊,如栈出栈顺序等等。ActivityRecord
,代表具体的某一个Activity,存放了该Activity的各种信息。
startPausingLocked
方法就是让上一个应用,这里也就是Launcher进入Paused状态。 然后就会判断应用是否启动,如果已经启动了,就会走ResumeActivityItem
的方法,看这个名字,结合应用已经启动的前提,是不是已经猜到了它是干吗的?没错,这个就是用来控制Activity的onResume生命周期方法的,不仅是onResume还有onStart
方法,具体可见ActivityThread的handleResumeActivity
方法源码。
如果应用没启动就会接着走到startSpecificActivityLocked
方法,接着看。
第三关:是否已启动进程,否则创建进程
Launcher进入Paused之后,ActivityTaskManagerService
就会判断要打开的这个应用进程是否已经启动,如果已经启动,则直接启动Activity即可,这也就是应用内的启动Activity流程。如果进程没有启动,则需要创建进程。
这里有两个问题:
怎么判断应用进程是否存在呢?如果一个应用已经启动了,会在ATMS里面保存一个
WindowProcessController
信息,这个信息包括processName和uid,uid则是应用程序的id,可以通过applicationInfo.uid获取。processName则是进程名,一般为程序包名。所以判断是否存在应用进程,则是根据processName和uid去判断是否有对应的WindowProcessController,并且WindowProcessController
里面的线程不为空。代码如下:
//ActivityStackSupervisor.java
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// Is this activity's application already running?
final WindowProcessController wpc =
mService.getProcessController(r.processName, r.info.applicationInfo.uid);
boolean knownToBeDead = false;
if (wpc != null && wpc.hasThread()) {
//应用进程存在
try {
realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
}
}
}
//WindowProcessController.java
IApplicationThread getThread() {
return mThread;
}
boolean hasThread() {
return mThread != null;
}
还有个问题就是怎么创建进程?还记得Z老师吗?对,就是Zygote进程。之前说了他是所有进程的父进程,所以就要通知
Zygote
去fork一个新的进程,服务于这个应用。
//ZygoteProcess.java
private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
ZygoteState zygoteState, String msgStr)
throws ZygoteStartFailedEx, IOException {
try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
final BufferedWriter usapWriter =
new BufferedWriter(
new OutputStreamWriter(usapSessionSocket.getOutputStream()),
Zygote.SOCKET_BUFFER_SIZE);
final DataInputStream usapReader =
new DataInputStream(usapSessionSocket.getInputStream());
usapWriter.write(msgStr);
usapWriter.flush();
Process.ProcessStartResult result = new Process.ProcessStartResult();
result.pid = usapReader.readInt();
// USAPs can't be used to spawn processes that need wrappers.
result.usingWrapper = false;
if (result.pid >= 0) {
return result;
} else {
throw new ZygoteStartFailedEx("USAP specialization failed");
}
}
}
可以看到,这里其实是通过socket
和Zygote进行通信,BufferedWriter
用于读取和接收消息。这里将要新建进程的消息传递给Zygote,由Zygote进行fork进程,并返回新进程的pid。
可能又会有人问了?fork是啥?为啥这里又变成socket
进行IPC通信,而不是Bindler
了?
首先,
fork()
是一个方法,是类Unix操作系统上创建进程的主要方法。用于创建子进程(等同于当前进程的副本)。那为什么fork的时候不用Binder而用socket了呢?主要是因为fork
不允许存在多线程
,Binder通讯偏偏就是多线程。
问题总是在不断产生,总有好奇的朋友会接着问,为什么fork
不允许存在多线程?