Android Activity启动三部曲(二)

背景

  在上一篇Android Activity启动过程(一)中,我们的探究过程截止到了通过 Binder 机制向 ActivityManagerService(以下简称AMS) 发出 startActivity 请求,本节,我们将接着讨论 AMS 对 start 请求的处理过程。
  使用源码:Nougat - 7.0.0_r1

StartActivity(二)

基础结构

首先,我们从管理的角度,看一看AMS管理 Activity 的基础结构。

AMS中的数据结构

这里,我们需要清除几个概念:

  • ActivityStackSupervisor:该类的实例化对象存在于 AMS 中。它负责管理所有的 ActivityStack。
  • ActivityStack:于 StackId 一一对应,SstackId是有限的,包括如下几种,这也就意味了ActivityStack的个数就只有以下几种。每个ActivityStack对应着不同的使用模式。比如DOCKED_STACK_ID 对应着分屏模式,通常我们使用的模式是FULLSCREEN_WORKSPACE_STACK_ID 。
   /** First static stack ID. */
    public static final int FIRST_STATIC_STACK_ID = 0;

    /** Home activity stack ID. */
    public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;

    /** ID of stack where fullscreen activities are normally launched into. */
    public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;

    /** ID of stack where freeform/resized activities are normally launched into. */
    public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;

    /** ID of stack that occupies a dedicated region of the screen. */
    public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;

    /** ID of stack that always on top (always visible) when it exist. */
    public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;

    /** Last static stack stack ID. */
    public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;

    /** Start of ID range used by stacks that are created dynamically. */
    public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
  • mTaskHistory:它是一个ArrayList结构的,实际上AMS内部维护了很多ArrayList的集合分别对各任务进行管理。比如在mTaskHistory中,记录了该模式下所有的TaskRecord。当某一个Activity被创建,将进入相应的TaskRecord,当该Activity被销毁,则从TaskRecord中remove,同时从TaskRecord栈顶再取出一个Activity显示。
  • TaskRecord:从上面的描述中,其实我们已经看到了TaskRecord的主要作用,即作为一个任务的集合。一个任务中可以包含很多的Activity。
  • ActivityRecord:是AMS中对一个Activity的描述,可以说,它代表了一个实际的Activity。而在TaskRecord中,实际上存储的就是这个ActivityRecord。
    下面,我们通过ActivityStack的产生构建过程,进一步认识一下它们之间的关系:
ActivityStack的构建过程
执行过程

在AMS执行过程关键步骤的描述如下图所示:

AMS中的准备工作

上述过程中,主要完成了一下工作:

  • 验证要启动的Activity的合法性;
  • 生成Activity所对应的 ActivityRecord,同时根据Activity的启动方式的不同,将ActivityRecord放入合适的 TaskRecord中。即建立好 ActivityRecord---TaskRecord---ActivityStack之间的关系。其实不仅仅是这些关系,为了方便管理,还有一些其他关系需要被建立;
  • 将 ActivityRecord 置顶在 TaskRecord中,将TaskRecord置顶在mTaskHistory中;这样就为显示该Activity做好了足够的准备;
  • 最后通过IBinder发送消息到调用 startActivity 的源头(比如Launcher),告诉它可以进行pause状态了。

  做好以后工作,ok,下面就回到了源头所在的进程中,接着执行,回到源Activity进入pause,这样做的主要目的,第一,让源Activity 执行它的回调方法 onPause;第二,保存一些必要的数据,使得在此回到这个Activity时,能够回到现有的状态。这个执行过程如下:

源Activity进入pause状态

  首先,我们来看看源Activity主要做了哪些处理:

 ...
 if (userLeaving) {
                performUserLeavingActivity(r);
 }
 ...
 //执行onPause回调
 performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
 ...
 //保存必要数据
 QueuedWork.waitToFinish();

QueuedWork.waitToFinish()源码如下:

    /**
     * Finishes or waits for async operations to complete.
     * (e.g. SharedPreferences$Editor#startCommit writes)
     *
     * Is called from the Activity base class's onPause(), after
     * BroadcastReceiver's onReceive, after Service command handling,
     * etc.  (so async work is never lost)
     */
    public static void waitToFinish() {
        Runnable toFinish;
        while ((toFinish = sPendingWorkFinishers.poll()) != null) {
            toFinish.run();
        }
    }

  从上图可以看到,此时角色发生了反转。AMS作为客户端,源Activity进程作为服务提供者。CS结构中C和S的角色总是相对的。当ActivityThread完成 pause 工作之后,告知AMS进行下一步工作。
  现在,让我们回到AMS中,接着看。接下来的主要步骤如下:

准备启动新进程

  主要工作主要包括,处理来自源进程中 Activity 已经 pause 的消息;从 focused stack找到 top activity,检测到该activity没有所属的进程,因此回到 AMS 中,启动新进程。
  所有已经启动的process将以ProcessRecord的形式存储在AMS中的ProcessMap属性中,获取一个已启动ProcessRecord过程如下:

  public E get(String name, int uid) {
        SparseArray uids = mMap.get(name);
        if (uids == null) return null;
        return uids.get(uid);
    }

  由此可见,所有的Activity都运行在一个process中,而启动的process是有 uid 和进程名唯一确定的。uid是Activity的用户id,安装的时候由PackageManagerService确定,进程名是在manifest.xml中自己对Activity配置的,默认为包名。决定是否需要创建新进程的调用过程是在startSpecificActivityLocked进行的。因此进程是在需要的时候才被创建的,而是否需要的标准,就是根据这个uid和进程名称是否能找到相应的进程。
  创建新进程的过程在startProcessLocked中完成:

   Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);

Process.start方法如下:

   /**
     * Start a new process.
     * 
     * 

If processes are enabled, a new process is created and the * static main() function of a processClass is executed there. * The process will continue running after this function returns. * *

If processes are not enabled, a new thread in the caller's * process is created and main() of processClass called there. * *

The niceName parameter, if not an empty string, is a custom name to * give to the process instead of using processClass. This allows you to * make easily identifyable processes even if you are using the same base * processClass to start them. * * @param processClass The class to use as the process's main entry * point. * @param niceName A more readable name to use for the process. * @param uid The user-id under which the process will run. * @param gid The group-id under which the process will run. * @param gids Additional group-ids associated with the process. * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. * @param abi non-null the ABI this app should be started with. * @param instructionSet null-ok the instruction set to use. * @param appDataDir null-ok the data directory of the app. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. * @throws RuntimeException on fatal start failure * * {@hide} */ public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String abi, String instructionSet, String appDataDir, String[] zygoteArgs) { try { //这里,启动了由“受精卵”启动了新的进程 return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } }

小结

  本节主要描述了Activity启动在AMS中所需要做的工作。
  我们看到,其中有大量的数据结构,维持着Activity正确的启动、不同状态之间的切换。同时,对于数据结构+算法的认知,又更加进了一步。
  下一节,接着完成最后的剩下的调用执行过程。
  第一次阅读Activity启动的源码,同时查了很多资料。这是一个不断剥茧抽丝的过程,也许会有疏漏,还请指正。

你可能感兴趣的:(Android Activity启动三部曲(二))