Android Launcher 启动Activity工作流程

前言

通过Binder之应用层总结与分析可以了解到进程间通讯的一个大致情况,像今天要提到的Activity启动过程,也是以Binder为通讯方式。系统对这个工作过程做了很多封装,使得启动一个Activity变得十分简单。这看似简单的背后,实际上是Activity与ActivityManagerService之间多次通讯的结果。
阅读该篇文章建议配合源码一起食用,味道更佳。

Launcher

手机桌面APP叫Launcher,每一个应用的icon都罗列在Launcher上,点击icon触发onItemClick事件,下面例如我们要启动某个TT App,首先我们要在AndroidManifest.xml清单文件定义默认启动的Activity信息。

1
2
3
4
5
6
7
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

然后Launcher获取到该信息之后,启动TT APP

1
2
3
4
5
6
7
8
9
10
//该应用的包名
String pkg = info.activityInfo.packageName;
//应用的主activity类
String cls = info.activityInfo.name;

ComponentName componet = new ComponentName(pkg, cls);

Intent i = new Intent();
i.setComponent(componet);
startActivity(i);

启动Activity这一工作不管是相同应用的2个不同Activity的启动,或者是不同进程不同应用的Activity启动,都是由Activity大管家ActivityManagerService(简称AMS)全权管理,而他们之间的通讯就要用到Binder,通过Binder与AMS多次通讯,才能启动该App。

整体流程

通过对Android操作系统的学习可以提高我们对操作系统在技术实现上的理解,这对于加强开发人员的内功是很有帮助的。
但是由于Android内部实现多数都比较复杂,在研究内部实现上应该更加侧重对整体流程的把握,而不能深入到代码细节不能自拔

  1. Launcher通知AMS启动淘宝APP的MainActivity,也就是清单文件设置启动的Activity。
  2. AMS记录要启动的Activity信息,并且通知Launcher进入pause状态。
  3. Launcher进入pause状态后,通知AMS已经paused了,可以启动淘宝了。
  4. TTapp未开启过,所以AMS启动新的进程,并且在新进程中创建ActivityThread对象,执行其中的main函数方法。
  5. TTapp主线程启动完毕后通知AMS,并传入applicationThread以便通讯。
  6. AMS通知淘宝绑定Application并启动MainActivity。
  7. TTapp启动MainActivitiy,并且创建和关联Context,最后调用onCreate方法。

startActivityForResult

我们从Activity的startActivity方法开始分析。 startActivity方法有好几种重载方式,但它们最终都会调用startActivityForResult方法。

1
2
3
4
5
6
7
8
9
10
11
//Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

在startActivityForResult方法内,会调用Instrumentation的execStartActivity方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Activity.java
@Override
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}

Instrumentation

Instrumentation从字面上来看是仪器盘的意思,具体到程序中是管理activity的一个工具类,包括创建和启动Activity,activity的生命周期方法都是由Instrumentation这个仪器来控制,一个进程中只用一个Instrumentation实例。

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
48
49
//Instrumentation.java
/**
* Base class for implementing application instrumentation code. When running
* with instrumentation turned on, this class will be instantiated for you
* before any of the application code, allowing you to monitor all of the
* interaction the system has with the application. An Instrumentation
* implementation is described to the system through an AndroidManifest.xml's
* &lt;instrumentation&gt; tag.
*/
public class Instrumentation {
/**
*
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
* is being started.
* @param token Internal token identifying to the system who is starting
* the activity; may be null.
* @param target Which activity is performing the start (and thus receiving
* any result);
* may be null if this call is no`t being made form an activity.
* @param intent The actual Intent to start.
* @param requestCode Identifier for this request's result; less than zero
* if the caller is not expecting a result.
* @param options Addition options.
*
*/
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;

......

try {
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);

//检查启动Activity的结果(抛出异常,例如清单文件未注册Activity)
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
....

我们截取了比较关键的代码片段来分析Instrumentation的execStartActivity方法,方法参数注释中也有对该方法的几个参数进行简单描述。下面我们来分析一下比较重要的2个参数,contextThread和token。

IBinder contextThread

在上一个方法中传入为mMainThread.getApplicationThread()
我们可以看到这是一个IBinder对象,说明它的作用就是用于进程间通讯的Binder对象。

mMainThread实际上是ActivityThread对象。ActivityThread,就是主线程,也就是UI线程,它是在App启动时创建的,它代表了App应用程序。
啥?ActivityThread代表了App应用程序,那Application类岂不是被架空了?其实,Application对我们App开发人员来说也许很重要,但是在Android系统中还真的没那么重要,他就是个上下文。Activity不是有个Context上下文吗?Application就是整个ActivityThread的上下文。

我们找到ActivityThread文件,其实这个getApplicationThread方法获取的是内部类ApplicationThread对象,而且ApplicationThread继承IApplicationThread.Stub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ActivityThread.java
public final class ActivityThread {//没有继承或者实现其他类。
...
final ApplicationThread mAppThread = new ApplicationThread();
...
public ApplicationThread getApplicationThread()
{
return mAppThread;
}
.....
//ActivityThread的内部类ApplicationThread
private class ApplicationThread extends IApplicationThread.Stub {
......
}

}

上面的介绍中我们也说过,Activity的启动实际上是多次进程间通讯的成果,看到这里我们就可以得出结论:ActivityThread通过内部类ApplicationThread来进行进程间通讯.

IBinder token

追溯到参数起源,这个token对象,是在Activity的attach方法中传入的,也就是Activity的创建与关联时候(下面的内容会提到)传入的Activity信息.
这也是个Binder对象,它代表了Launcher这个Activity,这里也通过Instrumentation传给AMS,AMS查询后,就知道是谁向AMS发起请求了。

1
2
3
4
5
6
7
8
9
10
11
12
//Activity.java
private IBinder mToken;
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
...
mToken = token;
}

contextThread和token这两个参数是伏笔,传递给AMS,以后AMS想反过来通知Launcher,就能通过这两个参数,找到Launcher。

startActivity

在Instrumentation中,启动Activity真正的实现是由ActivityManager.getService()的startActivity方法来完成。

1
2
3
4
5
6
7
8
9
10
11
12
//
public ActivityResult execStartActivityAsCaller(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity,
int userId) {
...
int result = ActivityManager.getService()
.startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options, ignoreTargetSecurity, userId);
checkStartActivityResult(result, intent);

在ActivityManager.java中getService()实现如下:

1
2
3
4
5
6
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}

参考资料:

Android Launcher 启动 Activity 的工作过程