App启动流程【2】Activity 到 ActivityTaskManagerService 的调用流程

在上一章节中,最后分析到了 Activity 的 startActivity 方法,从此方法开始继续向下分析:

public void startActivity(Intent intent, @Nullable Bundle options) {
    // ... 这里省略了处理 Activity 恢复数据相关的内容
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // 请注意,我们希望通过此调用与可能已覆盖该方法的应用程序兼容。
        startActivityForResult(intent, -1);
    }
}

Activtiy 的 startActivity 方法的注释:

启动一个新的 Activity 。

您不会收到任何与 Activity 有关的信息。

此实现覆盖了基础版本,提供了有关 Activity 执行启动的相关信息。由于这些附加信息,不需要 Intent.FLAG_ACTIVITY_NEW_TASK 。

如果未指定,新的 Activity 将添加到调用者的任务栈中。如果没有找到给定 Intent 所指定的 Activity ,此方法将抛出 android.content.ActivityNotFoundException 。

Anyway,Activtiy 的 startActivity 最终还是通过 startActivityForResult 方法进行的:

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options)
 
{
        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);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

这个方法中,根据 mParent 是否存在,形成了两种逻辑:

  • mParent 不存在,通过 Instrumentation 的 execStartActivity 方法获取了一个 ActivityResult ,然后再通过 ActivityThread 的 sendActivityResult 方法将 ActivityResult 发送出去。
  • mParent 存在,调用 startActivityFromChild 方法启动 Activity 。

由此可以产生三个疑问:

  1. mParent 是什么,哪来的?
  2. Instrumentation 的 execStartActivity 做了什么?
  3. startActivityFromChild 做了什么?

下面将一一探索这个三个问题的答案。

parent 的来源

parent 属性是一个 Activity 类型的对象,它有两个赋值的使用:

  1. setParent 方法
  2. attach 方法中的参数

setParent 没有被调用;而 attach 方法有两个调用来源:

  1. ActivityThread#performLaunchActivity

    Activity 启动的核心实现

  2. Instrumentation#newActivity

    执行 Activity 对象的实例化,此方法适用于单元测试,例如 android.test.ActivityUnitTestCase。

Instrumentation#execStartActivity

该方法用于执行应用程序发出的 startActivity 调用。默认的实现负责更新 Activity 的 Instrumentation.ActivityMonitor 对象,并将此调用分配给系统的 Activity Manager ;你可以通过覆盖这个类以监视应用程序启动 Activity ,并修改它在启动时的行为。

此方法返回一个 Instrumentation.ActivityResult 对象,您可以在应用程序调用此方法时,拦截该方法避免执行启动 Activity 的操作,但仍应该返回一个应用程序期待的返回值。为此,请重写此方法来捕获 start activity 的调用,以便它返回一个新的 ActivityResult 。其中包含您希望应用程序看到的结果,并且不要调用超类。请注意,仅当 requestCode >= 0 时,应用程序才会期待结果。

如果没有找到运行给定 Intent 的 Activity,此方法将抛出 ActivityNotFoundException。

从这个方法的注释中我们可以提取出几个关键信息:

  1. 该方法用于执行应用程序发出的 startActivity 调用。
  2. 可以覆盖这个类以监视应用程序启动 Activity 。

来看看这个方法的默认实现:

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
    // ... 这里省略 mActivityMonitors 更新等逻辑
    try {
        // intent 信息准备
        intent.migrateExtraStreamToClipData(who);
        intent.prepareToLeaveProcess(who);
        // AMS 调用 startActivity
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getOpPackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0null, options);
        // 检查结果
        checkStartActivityResult(result, intent); 
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

在这个方法中,最关键的一行代码就是 ActivityTaskManager.getService().startActivity(...) 。ActivityTaskManager 对应的系统服务是 ActivityTaskManagerService 。

Activity#startActivityFromChild

startActivityFromChild 方法已标记为弃用,建议使用 androidx.fragment.app.FragmentActivity#startActivityFromFragment(androidx.fragment.app.Fragment,Intent,int)

这个方法是 Activity 的子 Activity 调用 startActivity 或 startActivityForResult 时,会调用到此方法。

public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
        options = transferSpringboardActivityOptions(options);
    Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, child, intent, requestCode, options);
    if (ar != null) {
        mMainThread.sendActivityResult(mToken, child.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
    }
    cancelInputsAndStartExitTransition(options);
}

这个方法也是调用了 mInstrumentation.execStartActivity(...)


原文始发于微信公众号(八千里路山与海):App启动流程【2】Activity 到 ActivityTaskManagerService 的调用流程

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/85056.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!