Activity 的启动流程源码剖析(一)

简介: 简单分析 Activity 的启动流程(一)这篇主要分析 startActivity 这个方法源码的版本Android 27V4 27.1.1我都是粘的里面比较关键的源码,还希望配合源码阅读第一步先找到源码的切入点我们启动 Activit...

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

img_e89fe89627c41ab8492884c09cb90b5d.jpe
欢迎关注我的公众号
目录
相关文章
flowable 启动流程的三种方式
flowable 启动流程的三种方式
319 0
|
Java 调度 Android开发
android体系课-系统启动流程-之SystemServer启动过程源码分析
笔者刚开始学习Android的时候也和大部分同学一样,只会使用一些应用层面的知识,对于一些比较常见的开源框架如<mark>RxJava</mark>,<mark>OkHttp</mark>,<mark>Retrofit</mark>,以及后来谷歌推出的<mark>协程</mark>等,都只在使用层面,对于他们<mark>内部原理</mark>,基本没有去了解觉得够用就可以了,又比如Activity,Service等四大组件的使用原理,系统开机过程,Launcher启动过程等知之甚少,知其然而不知其所以然,结果就是出现某些问题,不知道从哪里找原因,只能依赖万能的百度,但是百度看多了,你会发现自己
|
缓存 Java 程序员
庖丁解牛 Activity 启动流程
庖丁解牛 Activity 启动流程
庖丁解牛 Activity 启动流程
|
Java
深入理解 NioEventLoop启动流程
深入理解 NioEventLoop启动流程
159 0
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )(二)
【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )(二)
217 0
【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )(二)
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )
241 0
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 一 )(二)
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 一 )(二)
289 0
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 一 )(一)
【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 一 )(一)
158 0
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( Activity -> AMS、主线程阶段 )
【Android 启动过程】Activity 启动源码分析 ( Activity -> AMS、主线程阶段 )
212 0
|
Java Android开发
【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )(一)
【Android 启动过程】Activity 启动源码分析 ( AMS -> ActivityThread、AMS 线程阶段 二 )(一)
229 0