Android Framework AMS(04)startActivity分析-1(am启动到ActivityThread启动)

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要解读AMS通过startActivity启动Activity的整个流程的第一阶段:从am启动到ActivityThread启动。

第二阶段文章链接为:

Android Framework AMS(05)startActivity分析-2(ActivityThread启动到Activity拉起)

AMS的startActivity方法目的是确保Activity 的正确启动和正确显示。分析Activity的启动流程方法有很多,我们选择从 adb shell am start 命令开始分析 Activity 的启动流程,那么为什么要选择从这个视角来分析呢?

这是因为这个命令提供了一个标准化和可控的方式来启动 Activity,它模拟了从系统层面发起的启动请求,这种方式可以绕过应用内部的逻辑,直接请求 AMS 进行处理。可以让开发者从系统层面更好地理解 Android 系统的工作原理,以及 AMS 在其中扮演的角色。这种方式有助于揭示 Activity 启动过程中的系统行为,包括进程创建、任务栈管理、Activity 生命周期管理等。

接下来开始我们的分析。

1 从am命令到AMS的startActivty调用

这里我们以启动setting界面为例。在命令行中,我们开始执行:

$adb shell am start -a android.intent.action.MAIN -n com.android.settings/.Settings

这里的命令解释如下:

  • adb shell: 启动一个 Android shell 命令行。
  • am: 命令行工具,用于与 Activity Manager 服务交互。
  • start: 指示 am 工具启动一个新的 Activity。
  • -a android.intent.action.MAIN: 指定启动的 Activity 应该处理 MAIN 动作,这是启动 Activity 的常用动作。
  • -n com.android.settings/.Settings: 指定包名和 Activity 名称,这里 com.android.settings 是设置应用的包名,Settings 是设置应用的主 Activity。

这个命令会打开设备的设置主界面(注意:不同的设备可能有细微的差别,特别是在定制过的 Android 系统上,Activity 名称或者包名可能有所不同,如果上述命令不起作用,你可能需要查看设备的特定设置应用的包名和 Activity 名称)

接下来我们看看am命令是如何编译和运行的?

1.1 am命令的编译和运行

am命令对应的Am.java代码,我们先来看Am.java是如何被编译成jar文件,以及如何被使用的。相关文件均在AOSP的frameworks/base/cmds/am目录下。am命令对应的Android.mk文件内容如下所示:

# Copyright 2008 The Android Open Source Project
#
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := am
include $(BUILD_JAVA_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := am
LOCAL_SRC_FILES := am
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)

这里编译Am.java后生成一个am.jar的文件。这里我们再看am命令,编辑器打开后,如下所示:

#!/system/bin/sh
#
# Script to start "am" on the device, which has a very rudimentary
# shell.
#
base=/system

#CLASSPATH 用于指定 Java 程序运行时查找类定义的路径。这里,它被设置为 
#/system/framework/am.jar,意味着 am.jar 文件包含了执行 am 命令所需的类。
export CLASSPATH=$base/framework/am.jar

exec app_process $base/bin com.android.commands.am.Am "$@"

这里详细解读下最后一句脚本的含义:

  • exec:这个 shell 内置命令用于执行指定的命令,并且替换当前 shell 进程。
  • app_process:这是一个用于启动 Android 应用程序和系统服务的底层 C++ 程序。
  • $base/bin:这是 app_process 的路径,指向 /system/bin 目录。
  • com.android.commands.am.Am:这是启动的 Java 主类,定义在 am.jar 中。
  • "$@":这是传递给脚本的所有参数,它们将被传递给 com.android.commands.am.Am。

总的来说,这个脚本设置了必要的环境变量,并使用 app_process 来启动 am 命令行工具。am 命令行工具实际上是通过 Java 编写的,并且被打包在 am.jar 文件中。这个脚本使得用户可以通过 shell 界面以命令行的方式与 Activity Manager 服务进行交互。

接下来我们来分析 Am.java文件的内容。

1.2 Am.java解读

Am.java 是 Android 系统源代码中的一个文件,当调用 am start时,进入到Am.java的main函数中,代码内容如下:

//Am 
   public static void main(String[] args) {
        (new Am()).run(args);
    }

Am类是继承BaseCommand,因此这里的run实际上是调用父类的run方法,BaseCommand的run方法实现如下:

//BaseCommand
    public void run(String[] args) {
        if (args.length < 1) {
            onShowUsage(System.out);
            return;
        }

        mArgs = args;
        mNextArg = 0;
        mCurArgData = null;

        try {
            //这里调用的是子类Am的onRun方法
            onRun();
        } catch (IllegalArgumentException e) {
            onShowUsage(System.err);
            System.err.println();
            System.err.println("Error: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace(System.err);
            System.exit(1);
        }
    }

接着,再回到Am中的OnRun方法实现,代码如下所示:

//Am 
   @Override
    public void onRun() throws Exception {
        mAm = ActivityManagerNative.getDefault();
        //...

        String op = nextArgRequired();

        if (op.equals("start")) {
            runStart();
        } else if (op.equals("startservice")) {
            runStartService();
        } else if (op.equals("stopservice")) {
            runStopService();
        } else if (op.equals("force-stop")) {
            runForceStop();
		//...
        } else {
            showError("Error: unknown command '" + op + "'");
        }
    }

这里开始,真正的处理命令参数了,关于am start,我们这里只需要关注runStart方法即可,对应的代码实现如下所示:

//Am
    private void runStart() throws Exception {
		//创建一个 Intent 对象,用于指定要启动的 Activity
        Intent intent = makeIntent(UserHandle.USER_CURRENT);
		//...
		
        String mimeType = intent.getType();
        if (mimeType == null && intent.getData() != null
                && "content".equals(intent.getData().getScheme())) {
            mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
        }

        do {
            //...
            System.out.println("Starting: " + intent);
			//Activity 将在新的任务栈中启动
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

			//性能分析相关
            ParcelFileDescriptor fd = null;
            ProfilerInfo profilerInfo = null;
            if (mProfileFile != null) {
                try {
                    fd = ParcelFileDescriptor.open(
                            new File(mProfileFile),
                            ParcelFileDescriptor.MODE_CREATE |
                            ParcelFileDescriptor.MODE_TRUNCATE |
                            ParcelFileDescriptor.MODE_READ_WRITE);
                } catch (FileNotFoundException e) {
                    System.err.println("Error: Unable to open file: " + mProfileFile);
                    return;
                }
                profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
            }

            IActivityManager.WaitResult result = null;
            int res;
            final long startTime = SystemClock.uptimeMillis();
			//启动activity,关键代码,同时记录启动前后的时间,用于性能分析
            if (mWaitOption) {
                result = mAm.startActivityAndWait(null, null, intent, mimeType,
                            null, null, 0, mStartFlags, profilerInfo, null, mUserId);
                res = result.result;
            } else {
                res = mAm.startActivityAsUser(null, null, intent, mimeType,
                        null, null, 0, mStartFlags, profilerInfo, null, mUserId);
            }
            final long endTime = SystemClock.uptimeMillis();
            PrintStream out = mWaitOption ? System.out : System.err;
            boolean launched = false;
			//activity启动后的结果处理
            switch (res) {
                case ActivityManager.START_SUCCESS:
                    launched = true;
                    break;
				//...
                default:
                    out.println(
                            "Error: Activity not started, unknown error code " + res);
                    break;
            }
            //...
			//重复启动Activity处理
            mRepeat--;
            if (mRepeat > 1) {
                mAm.unhandledBack();
            }
        } while (mRepeat > 1);
    }

这个方法展示了 adb shell am start 命令背后的逻辑,包括 Intent 的创建、性能分析、Activity 的启动以及结果处理。

1.3 Am.java 额外的逻辑解读

1.3.1 mWaitOption解读

mWaitOption 表示是否应该等待目标 Activity 启动完成后再继续执行。当 mWaitOption 设置为 true 时,am start 命令会等待 ActivityManager 启动指定的 Activity 并返回结果。如果 Activity 成功启动,命令会输出启动结果;如果启动失败,命令会输出错误信息和失败原因。在代码中,mWaitOption 可能通过命令行参数设置,例如,如果用户输入了

$adb shell am start -W

命令,其中的 -W 参数就会使得 mWaitOption 设置为 true。

1.3.2 mRepeat解读

在 Am.java 的 runStart() 方法中,处理重复启动的代码片段提取如下:

do {
    //...
    mRepeat--;
    if (mRepeat > 1) {
        mAm.unhandledBack();
    }
} while (mRepeat > 1);

mRepeat 的初始值是由用户通过命令行参数(如 --repeat 或 -r)指定的,表示 Activity 需要被启动的次数。在循环体内部,每次启动 Activity 后,mRepeat 的值会递减(mRepeat --)。如果 mRepeat 大于 1,意味着还需要再次启动 Activity。

在每次成功启动 Activity 并且设置了重复启动之后,if 语句检查 mRepeat 的值。如果还需要再次启动(mRepeat 大于 1),它会调用 mAm.unhandledBack() 方法。这个方法的作用是发送一个“返回”事件(相当于用户按下设备上的返回键)到系统,使得当前 Activity 退到后台,并且将其所在的任务栈上一个 Activity 带到前台。

假设用户执行了如下命令:

#这里通过-r参数设置mRepeat的值
$adb shell am start -r 3 com.example/myActivity

这个命令会启动 com.example/myActivity Activity 三次。以下是预期的行为:

  1. 第一次启动 Activity。
  2. 发送返回操作,使得 Activity 退到后台。
  3. 第二次启动 Activity。
  4. 再次发送返回操作。
  5. 第三次启动 Activity。

在第三次启动之后,mRepeat的值会递减到 1,循环结束。

总的来说,mRepeat相关的这段代码实现了通过命令行控制 Activity 重复启动和模拟用户返回操作的功能,有助于进行更复杂的测试和性能评估。

接下来开始进入到AMS中的调用。

2 AMS的startActivityXXX方法分析

2.1 进入到AMS的startActivityMayWait方法

从1.2的分析中,关键代码如下:

//Am
    //runStart
            if (mWaitOption) {
                result = mAm.startActivityAndWait(null, null, intent, mimeType,
                            null, null, 0, mStartFlags, profilerInfo, null, mUserId);
                res = result.result;
            } else {
                res = mAm.startActivityAsUser(null, null, intent, mimeType,
                        null, null, 0, mStartFlags, profilerInfo, null, mUserId);
            }

这里开始进入到AMS的代理接口调用,最终是调用到了AMS的接口startActivityAndWait 和 startActivityAsUser方法。解读如下:

//ActivityManagerService
	//...
   @Override
	public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
			Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
			int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
		// 确保调用者不是隔离进程
		enforceNotIsolatedCaller("startActivityAndWait");

		// 处理用户ID,确保调用者有权启动目标用户ID的Activity
		userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
				false, ALLOW_FULL_ONLY, "startActivityAndWait", null);

		// 创建WaitResult对象,用于存储启动结果
		WaitResult res = new WaitResult();

		// 请求启动Activity并等待结果
		// 调用ActivityStackSupervisor的相关方法来实际启动Activity
		mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
				null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
				options, userId, null, null);

		// 返回启动结果
		return res;
	}
	//...
    @Override
	public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
			Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
			int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
		// 确保调用者不是隔离进程
		enforceNotIsolatedCaller("startActivity");

		// 处理用户ID,确保调用者有权启动目标用户ID的Activity
		userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
				false, ALLOW_FULL_ONLY, "startActivity", null);

		// 请求启动Activity但不等待结果,这里传递的res实际上是null
		// 调用ActivityStackSupervisor的相关方法来实际启动Activity
		return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
				resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
				profilerInfo, null, null, options, userId, null, null);
	}

两个方法都调用了 ActivityStackSupervisor 的 startActivityMayWait 方法来实际启动 Activity。其中:

  • startActivityAndWait 方法返回一个 WaitResult 对象,包含启动结果和性能数据。
  • startActivityAsUser 方法立即返回一个整数结果,表示启动操作的结果。

最后总结下,这两个方法是 AMS 中处理 Activity 启动请求的核心方法,分别用于同步和异步启动 Activity。最后也都调用了ActivityStackSupervisor的方法startActivityMayWait。该方法较长,这里分成3个阶段进行解读。3个阶段代码的定位分别是:

  1. 为启动Activity做相关函数的参数初始化。
  2. 启动Activity,也就是最关键操作。
  3. 启动Activity后的返回值处理。

接下来分别进行解读。

2.1.1 startActivityMayWait第一阶段代码分析

ActivityStackSupervisor的方法startActivityMayWait第一阶段代码解读如

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值