简单分析 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);
        }
  1. mParent 代表 ActivityGroup,ActivityGroup 已经在 API 13 中废弃了,官方推荐使用 Fragment 来代替 ActivityGroup ,所以这个

mParent == null 会一直成立

  1. ApplicationThread() 是 ActivityThread 的内部类,通过后面的分析你会发现他对 Activity 的启动过程起着至关重要的作用
  2. 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() 的启动一样了

这篇文章上次修改于 390 天前,可能其部分内容已经发生变化,如有疑问可询问作者。