简单分析 Activity 的启动流程(一)
这篇主要分析 startActivity 这个方法
源码的版本
Android 27
V4 27.1.1
我都是粘的里面比较关键的源码,还希望配合源码阅读
第一步先找到源码的切入点
我们启动 Activity 一般都是调用 startActivity()
这个方法 Activity、Context、Fragment 中都有我们分别看一下这几种调用的具体源码
一、Activity 的 startActivity()/startActivityForResult()
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
startActivity 是直接调用了 startActivityForResult() 如果不需要请求结果的话 requestCode 直接传 -1 就可以了
在 startActivityForResult 中发现了很关键的代码
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
}
- mParent 代表 ActivityGroup,ActivityGroup 已经在 API 13 中废弃了,官方推荐使用 Fragment 来代替 ActivityGroup ,所以这个
mParent == null 会一直成立 - ApplicationThread() 是 ActivityThread 的内部类,通过后面的分析你会发现他对 Activity 的启动过程起着至关重要的作用
- Instrumentation 看这个类的注释解释这个类的作用是一个仪器测试类,后面分析中你们会发现 Activity 的主要流程都会在这个类中
好了我们继续查看 Instrumentation 的 execStartActivity 方法
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
我们看一下 ActivityManager.getService()
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
再以上代码中我们可以分析出 ActivityManager.getService() 获得的就是一个 IActivityManager 的 Binder 对象,由此推出这里的 Activity 的启动是远程调用的 ActivityManagerService 也就是我们熟知的 AMS,这里就是典型的 Binder 的使用,不熟悉的可以了解下 Binder 机制
如何验证 Context.ACTIVITY_SERVICE 这个 Service 为 ActivityManagerService 呢,看一下 ActivityManagerService 的 setSystemProcess() 就了解了
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
我们再额外说一下 checkStartActivityResult 方法,跟进去可以发现全是报错信息是吧,平常我们常见的也有好多。就比如说这个
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
二、Context 的 startActivity()
因为 abstract 是一个抽象类,我们找到他的实现类 ContextImpl,那么为什么确定是 ContextImpl 呢,我们提前先看一下 ActivityThread.performLaunchActivity() 的方法里面调用的 createBaseContextForActivity() 方法
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
关键代码是 ContextImpl.createActivityContext() 方法
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
return context;
}
这里看到了 activity.attach() 传入的 appContext 实际上就是 ContextImpl 。
我们接下来继续查看 ContextImpl 的 startActivity() 方法
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
又看到了熟悉的代码,还有一个地方不知道你们发没发现,使用 Context 会判断你的 Flags 如果不是 FLAG_ACTIVITY_NEW_TASK 就会报错,不知道各位观众老爷有没有遇见过
三、Fragment 的 startActivity()/startActivityForResult()
查看 Fragment 的 startActivity() 方法看到以下关键代码
mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
这个地方是通过 mHost(FragmentHostCallback)
对象来执行启动流程的,然而这个类也是抽象的,好歹他就一个实现类 HostCallbacks
,查看 HostCallbacks 的 onStartActivityFromFragment() 方法
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
这个类调用的是 FragmentActivity 的 startActivityFromFragment 方法,我们继续往里跟
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options);
return;
}
checkForValidRequestCode(requestCode);
int requestIndex = allocateRequestIndex(fragment);
ActivityCompat.startActivityForResult(
this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
这个地方挺有意思的一点就是他的 requestCode 经过了简单的修改,这估计就是为了 Fragment 也能收到 onActivityResult 所作的处理,感兴趣可以自己看一下这不是我们这篇文章要讲的。
ActivityCompat 是 v4 包的一个兼容类,包括我们平时写代码的时候也可以用,废话不多说我们继续跟
if (Build.VERSION.SDK_INT >= 16) {
activity.startActivityForResult(intent, requestCode, options);
} else {
activity.startActivityForResult(intent, requestCode);
}
又回到了 FragmentActivity 的 startActivityForResult 方法中
super.startActivityForResult(intent, requestCode);
这里他直接调用了 Activity 的启动方法
四、Launcher 启动应用
到这里我们简单的讲了我们开发中所能用到的启动 Activity 的方式,我们在看一个我们没见过的 Launcher 中启动应用
我们找到源码 packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
//添加 FLAG_ACTIVITY_NEW_TASK Flags
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
if (Utilities.ATLEAST_MARSHMALLOW
&& (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
|| item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& !((ShortcutInfo) item).isPromise()) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
//启动 Activity
startActivity(intent, optsBundle);
} else {
LauncherAppsCompat.getInstance(this).startActivityForProfile(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
return true;
} catch (ActivityNotFoundException|SecurityException e) {
}
return false;
}
我们再看一下 他也是直接调用的 Activity 的 startActivity() 方法,后面的就和之前分析的 Activity.startActivity() 的启动一样了