Android AMS 完全剖析2 —— Activity 管理之启动过程情景分析1

本文基于 android-10.0.0_r41 版本讲解

英文缩写说明:

  • AMS:ActivityManagerService
  • ATMS:ActivityTaskManagerService

Android 在 Java 层弱化了进程的概念,建立了四大组件及其配套框架。这套框架中最核心的组件就是 AMS,在 Android10 中,AMS 的部分功能迁移到了 ATMS。接下来我们通过分析四大组件的启动过程来了解 AMS/ATMS 的内部实现。我们首先分析 Activity 的启动过程,Activity 主要由 ATMS 管理。

1. ATMS 整体框架分析

首先明确 Activity 的启动过程涉及到两个进程,App 进程和 SystemServer 进程,是一个客户端服务端模型。SystemServer 中注册了一个 Java Binder 服务 ATMS,其主要作用是作为服务端向 App 提供管理 Activity 的接口:

java 复制代码
startActivity
finishActivity
activityResumed
activityPaused
activityStopped
activityDestroyed
// ......

App 进程作为客户端通过 Binder RPC 调用到这些方法,实现 Activity 的管理:

ATMS 通过 AIDL 实现,相关类的类图如下:

在 App 进程启动的过程中,会启动一个匿名 Java Binder 服务 ApplicationThread,ATMS 可以通过调用 ApplicationThread 提供的接口,告知 App 进程更新 Activity 的状态:

java 复制代码
bindApplication
scheduleTransaction
scheduleLowMemory
scheduleSleeping
//......

此时,App 进程是服务端,SystemServer 是客户单。也就是说 App 和 SystemServer 互为客户端服务端。

ApplicationThread 同样基于 AIDL 实现,相关类的类图如下:

2. 客户端流程

ATMS 内部代码非常繁琐,涉及多种情景的处理,直接分析代码非常容易迷失。这里给出三个相对简单的具体场景:

  • 情景一:从 Launcher 页面点击 App 图标启动一个全新的 App
  • 情景二:在应用内从 Activity A 跳转到 Activity B
  • 情景三:启动 App 后,按 Home 键回到 Launcher ,再点击 App 图标

接下来,我们以情景一为例,分析 Activity 的启动过程:

Activity 启动是一个 RPC 过程,涉及到 App 进程和 SystemServer 进程,本节我们主要关心 App 进程(客户端)中的流程。

当用户在 Launcher 页面点击应用图标后,会执行到 startActivitySafely 方法:

java 复制代码
    // packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
            @Nullable String sourceContainer) {

        // ......

        boolean success = super.startActivitySafely(v, intent, item, sourceContainer);

        // ......
        return success;
    }

接着调用父类 BaseDraggingActivity 的另一个重载 startActivitySafely

java 复制代码
    // packages/apps/Launcher3/src/com/android/launcher3/BaseDraggingActivity.java
    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
            @Nullable String sourceContainer) {
        
       //......

        // 关注点1
        // 根据 View 的位置构建一个 ActivityOptions 对象,然后把 ActivityOptions 转换为 bundle 对象并返回
        // ActivityOptions 主要用于转场动画
        Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;

        // 关注点2
        //拿到 UserHandle
        UserHandle user = item == null ? null : item.user;

        // 关注点3
        // 设置 FLAG_ACTIVITY_NEW_TASK,保证 Activity 在新任务栈中运行
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        // intent 中的 mSourcebounds 保存了点击的图标的具体位置
        if (v != null) {
            intent.setSourceBounds(getViewBounds(v));
        }

        try {
            // 关注点 4
            // 判断是否弹出
            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                    && !((WorkspaceItemInfo) item).isPromise();
            if (isShortcut) {  // 应用快捷方式,不走这个分支
                // Shortcuts need some special checks due to legacy reasons.
                startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
            } else if (user == null || user.equals(Process.myUserHandle())) { 
                // 走这个分支
                // Could be launching some bookkeeping activity
                startActivity(intent, optsBundle);  // 调用父类 Activity 的 startActivity 方法
                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
                        Process.myUserHandle(), sourceContainer);
            } else {  // 不走这个分支
                LauncherAppsCompat.getInstance(this).startActivityForProfile(
                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
                        sourceContainer);
            }

            getUserEventDispatcher().logAppLaunch(v, intent);
            getStatsLogManager().logAppLaunch(v, intent);
            return true;
        } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
        }
        return false;
    }

关注点 1 处,根据 View 的位置构建一个 ActivityOptions 对象,然后把 ActivityOptions 转换为 bundle 对象并返回。ActivityOptions 对象用于 Activity 的转场动画,不清楚的同学可以参考使用ActivityOptions实现Activity转场动画

关注点 2 处,通过 ItemInfo 拿到目标 App 对应的 UserHandle 对象,不清楚 UserHandle 的同学可以参考Android的uid与UserHandle

关注点 3 处,设置 FLAG_ACTIVITY_NEW_TASK,保证 Activity 在新任务栈中运行

关注点 4 处,计算出一个 boolean 变量 isShortcut,这个变量决定是否显示应用快捷方式,当前情景下 isShortcut 为 false,进入到 else if 分支,在这个分支中接着会调用到父类 Activity 的成员方法 startActivity,其实现如下:

java 复制代码
    // frameworks/base/core/java/android/app/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);
        }
    }

这里 options 不为空,走第一个 if 分支,调用到子类 launcher 对象的 startActivityForResult 方法:

java 复制代码
    // /packages/apps/Launcher3/src/com/android/launcher3/launcher.java
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (requestCode != -1) {
            mPendingActivityRequestCode = requestCode;
        }
        // 走这个分支
        if (requestCode == -1
                || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
            super.startActivityForResult(intent, requestCode, options);
        }
    }

requestCode 的值为 -1,这里走第二个 if 分支,接着调用到父类 Activity 的 startActivityForResult 方法:

java 复制代码
    // frameworks/base/core/java/android/app/Activity.java

    // requestcode -1
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {  // 一般走这个分支
            
            // 单独处理 options,特定情况下使用当前 Activity 的 options 作为启动目标 Activity 的 options
            options = transferSpringboardActivityOptions(options);

            // 调用 Instrumentation.execStartActivity 方法启动 Activity
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

            // 处理启动 Activity 的返回值
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }

            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            // 退出即将来临的输入事件,开始 Activity 的动画
            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else { // 子 Activity 中会执行此分支
            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 顾名思义,表示是当前 Activity 的父 Activity,那么在什么样的场景下会存在一个 Activity 中包含 Activity 的情况呢,很容易就想到是 TabActivity、DialogActivity,现在基本已经被 Fragment 替代了,这种情况很少。

所以大部分情况下走第一个 if 分支。在 if 分支中会调用到 Instrumentation 对象的 execStartActivity 方法。

Intrumentation 是 application 和 activity 的辅助工具类,主要有以下一些接口:

java 复制代码
newActivity(...)
newApplication(...)
callApplicationOnCreate(...)
callActivityOnCreate(...)
callActivityOnNewIntent(...)
callActivityOnXXX(...)
execStartActivity(...)

我们主要关注上面使用到的 execStartActivity 方法:

java 复制代码
    // frameworks/base/core/java/android/app/Instrumentation.java
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        // 参数简单处理
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }

        // activity 监控相关,暂时不用管
        // ......


        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            // 获取 ATMS 服务的客户端代理类
            // 通过客户端代理类发起远程过程调用
            int result = ActivityTaskManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            // 检查 Activity 启动是否成功
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

这里处理了传入的参数后,调用 ActivityTaskManager.getService() 获得 ActivityTaskManager 服务的客服端代理类对象:

java 复制代码
    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };

这里是一个单例模式,通过 ServiceManager.getService 获得系统服务,然后通过 IActivityTaskManager.Stub.asInterface 将其转换为具体的代理端对象。通过这个代理端对象我们就可以发起 RPC 调用了。

接下来发起远程调用 startActivity,该远程调用在 aidl 文件中定义如下:

java 复制代码
    // frameworks/base/core/java/android/app/IActivityTaskManager.aidl
    int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
            int flags, in ProfilerInfo profilerInfo, in Bundle options);

方法的参数:

  • IApplicationThread caller:当前应用(Launcher)的 ActivityThread 对象的 ApplicationThread 类型成员
  • String callingPackage:当前 Activity 所在包名,这里的值为 intent.resolveTypeIfNeeded(who.getContentResolver()
  • Intent intent:启动目标 Activity 的 Intent,其中携带目标 Acitivity 隐式或者显示启动需要的参数
  • String resolvedType:intent.resolveTypeIfNeeded 方法的返回值,表示 intent 的 MIME 数据类型
  • IBinder resultTo:类型为 IBinder ,指向发起端 Activity 的 ActivityRecord 对象中的 Token 对象,其Binder 服务端对象在 AMS 中,这个参数比较复杂,后面单独一节来讲这个参数。
  • String resultWho:当前发起端 Activity.mEmbeddedID,可能为 null
  • int requestCode:启动目的端 Activity 的请求码,此时的取值为 -1
  • int flags:此时取值为 0,用于指定 Activity 的启动模式
  • ProfilerInfo profilerInfo:这里传入的是 null
  • Bundle options:启动目的端 Activity 的附加参数

ATMS Binder 服务的结构如下:

接下来,启动过程就会进入到服务端 SystemServer的 ATMS Binder 服务中去了。

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,主要研究方向是 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

相关推荐
Meteors.42 分钟前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton1 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw5 小时前
安卓图片性能优化技巧
android
风往哪边走6 小时前
自定义底部筛选弹框
android
Yyyy4826 小时前
MyCAT基础概念
android
Android轮子哥7 小时前
尝试解决 Android 适配最后一公里
android
雨白8 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走9 小时前
自定义仿日历组件弹框
android
没有了遇见9 小时前
Android 外接 U 盘开发实战:从权限到文件复制
android
Monkey-旭10 小时前
Android 文件存储机制全解析
android·文件存储·kolin