本文依据 android-14.0.0_r15 版本解说

英文缩写说明:

  • AMS:ActivityManagerService
  • ATMS:ActivityTaskManagerService

AndroidJava 层弱化了进程的概念,建立了四大组件结构。这套结构中最核心的组件便是 AMS,在 Android10 及以后,AMS 的部分功用搬迁到了 ATMS。接下来咱们经过剖析四大组件的发动进程来了解 AMS/ATMS 的内部完成。咱们首先剖析 Activity 的发动进程。

1. 全体结构剖析

首先明确 Activity 的发动进程涉及到多个进程:

  • 源 App 进程
  • SystemServer
  • Zygote
  • 方针 App 进程

在剖析代码之前咱们需求了解 App(包括了源 App 与方针 App) 与 SystemServer 之间的 Binder 通信通道。

SystemServer 中注册了一个 Java Binder 服务 ATMS,其首要作用是作为服务端向客户端 App 供给办理 Activity 的接口:

startActivity
finishActivity
activityResumed
activityPaused
activityStopped
activityDestroyed
// ......

App 进程作为客户端经过 Binder RPC 调用到这些办法,完成 Activity 的办理:

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

ATMS 经过 AIDL 完成,相关类的类图如下:

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

在 App 进程发动的进程中,会初始化一个匿名 Java Binder 服务 ApplicationThread,ATMS 能够经过调用 ApplicationThread 的 Binder 客户端方针供给的接口,长途调用到 App 端,更新 Activity 的状态:

bindApplication
scheduleTransaction
scheduleLowMemory
scheduleSleeping
//......

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

此刻,App 进程是服务端,SystemServer 是客户端。也便是说 App 和 SystemServer 互为客户端服务端。

ApplicationThread 同样依据 AIDL 完成,相关类的类图如下:

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

2. 客户端流程

ATMS 内部代码非常繁琐,涉及多种情形的处理,直接剖析代码非常简单迷失。这儿给出三个详细的场景:

  • 情形一:从 Launcher 页面点击 App 图标发动一个全新的 App(冷发动)
  • 情形二:在运用内从 Activity A 跳转到 Activity B(运用内跳转)
  • 情形三:发动 App 后,按 Home 键回到 Launcher ,再点击 App 图标(热发动)

接下来,咱们以情形一为例,剖析 Activity 的发动进程:

Activity 发动涉及到多个进程,本节咱们首要关怀 App 进程(客户端)中的流程。

2.1 Launcher 中的流程

咱们先运用 Android Studio 创建一个空 Activity 项目,然后安装到模拟器上,接着咱们在 Launcher 页面点击该运用图标,接着就会会履行 View 的 onClick 回调:

    // packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java
    private static void onClick(View v) {
        // ......
        Launcher launcher = Launcher.getLauncher(v.getContext());
       // ......
        Object tag = v.getTag();
        if (tag instanceof WorkspaceItemInfo) { // 会走这个if 分支
            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
        } 
        // ......
    }    

获取到图标对应的 WorkspaceItemInfo 和 Launcher 方针后,接着履行 onClickAppShortcut 办法:

    // packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java
    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
        // ......        
        // 继续跳转到另一个办法
        startAppShortcutOrInfoActivity(v, shortcut, launcher);
    }

接着会调用 startAppShortcutOrInfoActivity 办法:

    // packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java
    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
        // ......
        Intent intent;
        if (item instanceof ItemInfoWithIcon
                && (((ItemInfoWithIcon) item).runtimeStatusFlags
                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
          // ......
        } else { // 走这个分支,从 ItemInfo 从获取到 intent
            // 获取到的 Intent 的 flags 为 270532608,也便是二进制的 10200000
            // 也便是说这儿的 flags = FLAG_ACTIVITY_NEW_TASK & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
            intent = item.getIntent();
        }
        // .......
        // 接着调用另一个办法
        launcher.startActivitySafely(v, intent, item);
    }   

先从 ItemInfo 从获取到发动方针 Activity 的 intent。

intent 的内部数据状况如下:

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

接着调用 startActivitySafely 办法:

// packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
            @Nullable String sourceContainer) {
        // ......
       RunnableList result = super.startActivitySafely(v, intent, item);
        // ......
    }

接着调用父类的 startActivitySafely 办法:

// packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
    @Override
    public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
       // ......
        RunnableList result = super.startActivitySafely(v, intent, item);
        if (result != null && v instanceof BubbleTextView) { // 进入
            // This is set to the view that launched the activity that navigated the user away
            // from launcher. Since there is no callback for when the activity has finished
            // launching, enable the press state and keep this reference to reset the press
            // state when we return to launcher.
            BubbleTextView btv = (BubbleTextView) v;
            btv.setStayPressed(true);
            result.add(() -> btv.setStayPressed(false));
        }
        return result;
    }

接着调用父类的 startActivitySafely 办法

    // packages/apps/Launcher3/src/com/android/launcher3/views/ActivityContext.java
        default RunnableList startActivitySafely(
            View v, Intent intent, @Nullable ItemInfo item) {
       // ......
        Context context = (Context) this;
        // ......
        // 关注点1
        // getActivityLaunchOptions 办法依据 View 的方位构建一个 ActivityOptionsWrapper 方针回来,ActivityOptionsWrapper 内部有一个成员 ActivityOptions
        // ActivityOptions 首要用于转场动画
        // 关于 ActivityOptions 的运用,能够参阅下面两篇文章
        // https://blog.csdn.net/JohanMan/article/details/76726638
        // https://blog.csdn.net/chuyouyinghe/article/details/109515766
        ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
                : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
                        ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
        // 关注点2
        //拿到 UserHandle,UserHandle 用于描述当时用户 id,详细能够参阅前文的 ID
        UserHandle user = item == null ? null : item.user; // UserHandle{0}
        Bundle optsBundle = options.toBundle();
        /// 关注点3
        // 设置 FLAG_ACTIVITY_NEW_TASK,确保 Activity 在新任务栈中运行,实践已经有这个 flag 了
        // 值为 0x10000000
        // Prepare intent
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (v != null) {
            // ViewBounds 便是一个 Rect 方针,用于指定点击图标的方位
            intent.setSourceBounds(Utilities.getViewBounds(v));
        }
        try {
            // 关注点 4
            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                    && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
                    || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                    && !((WorkspaceItemInfo) item).isPromise();
            if (isShortcut) { // // 运用快捷方式,不走这个分支
                // 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
                // 走这儿
                context.startActivity(intent, optsBundle);
            } else {
                context.getSystemService(LauncherApps.class).startMainActivity(
                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
            }
            if (item != null) {
                InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                logAppLaunch(getStatsLogManager(), item, instanceId);
            }
            return options.onEndCallback;
        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
            Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
        }
        return null;
    }

关注点 1 处,getActivityLaunchOptions 办法依据 View 的方位构建一个 ActivityOptionsWrapper 方针回来,ActivityOptionsWrapper 内部有一个成员 ActivityOptions。ActivityOptions 首要用于转场动画,不清楚的同学能够参阅

关注点 2 处,经过 ItemInfo 拿到方针 App 对应的 UserHandle 方针,不清楚 UserHandle 的同学能够参阅Android的uid与UserHandle

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

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

接着调用 Activity 的 startActivity 办法:

    // frameworks/base/core/java/android/app/Activity.java
    public void startActivity(Intent intent, @Nullable Bundle options) {
        getAutofillClientController().onStartActivity(intent, mIntent);
        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 办法:

// packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
    @Override
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (requestCode != -1) {
            mPendingActivityRequestCode = requestCode;
            StartActivityParams params = new StartActivityParams(this, requestCode);
            params.intent = intent;
            params.options = options;
            startActivity(ProxyActivityStarter.getLaunchIntent(this, params));
        } else { // 走这个分支
            super.startActivityForResult(intent, requestCode, options);
        }
    }

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

    // packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
    @Override
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        if (requestCode != -1) { // 不进入
            mPendingActivityRequestCode = requestCode;
        }
        super.startActivityForResult(intent, requestCode, options);
    }

接着调用父类 Activity 的 startActivityForResult 办法

    // 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 办法。

到此,Launcher 部分就完毕,代码很繁琐,但总结一下,Launcher 阶段就干了一件事:预备下一阶段 Activity 发动的参数

2.2 Instrumentation 阶段

Intrumentation 方针用来监控运用程序和体系之间的交互操作。由于发动 Activity 是运用程序与体系之间的交互操作,因此调用它的成员函数 execStartActivity 来替代履行发动 Activity 的操作,以便它能够监控这个交互进程。首要有以下一些接口:

newActivity(…)
newApplication(…)
callApplicationOnCreate(…)
callActivityOnCreate(…)
callActivityOnNewIntent(…)
callActivityOnXXX(…)
execStartActivity(…)

咱们首要关注上面运用到的 execStartActivity 办法:

    // 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) {
        // App 这边的匿名服务,ATMS 向 App 建议调用的通道
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) { // 不进入
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        // ......
        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;
    }

execStartActivity 办法的参数由 Launcher 阶段预备,详细的,这些参数是:

  • who:类型是 Context,实践便是源 Activity 方针 QuickstepLauncher
  • contextThread:IBinder 方针,实践类型是 ApplicationThread,是一个 Binder 服务端方针
  • token:IBinder 方针,是一个 Binder 署理端方针,对应的 Binder 服务端方针是 ActivityRecord 方针中的 Token。在服务端首要起索引找到对应 ActivityRecord 方针的作用
  • target:建议端 Activity 方针,这儿是 QuickstepLauncher
  • intent:发动方针 Activity 的 Intent 方针
  • requestCode:发动方针 Activity 的恳求码,这儿是 -1
  • options:发动方针 Activity 的附加参数,内部首要内容如下:

Android14 Activity 发动进程详解 1 —— App 进程建议恳求

这儿处理了传入的参数后,调用 ActivityTaskManager.getService() 取得 ActivityTaskManager 服务的客服端署理类方针:

    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 文件中定义如下:

    // frameworks/base/core/java/android/app/IActivityTaskManager.aidl
    int startActivity(in IApplicationThread caller, in String callingPackage,
            in String callingFeatureId, 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,是一个 Binder 署理端方针,对应的 Binder 服务端方针是建议端 Activity 对应的的 ActivityRecord 方针中的 Token 方针,其Binder 服务端方针在 AMS 中。
  • String resultWho:当时建议端 Activity.mEmbeddedID,可能为 null
  • int requestCode:发动意图端 Activity 的恳求码,此刻的取值为 -1
  • int flags:此刻取值为 0,用于指定 Activity 的发动形式
  • ProfilerInfo profilerInfo:这儿传入的是 null
  • Bundle options:发动意图端 Activity 的附加参数

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

总结

  • App 与 SystemServer 之间存在两个 Binder 通信通道,ATMS 与 ApplicationThread
  • Activity 的发动的 Launcher 阶段首要作业是预备 Instrumentation 阶段建议长途调用的参数
  • Instrumentation 阶段向 SystemServer 建议长途调用,发动方针 Activity

参阅资料