美文网首页
Android Activity启动三部曲(二)

Android Activity启动三部曲(二)

作者: 风雪围城 | 来源:发表于2017-10-11 19:16 被阅读0次

    背景

      在上一篇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<E> 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.
         * 
         * <p>If processes are enabled, a new process is created and the
         * static main() function of a <var>processClass</var> is executed there.
         * The process will continue running after this function returns.
         * 
         * <p>If processes are not enabled, a new thread in the caller's
         * process is created and main() of <var>processClass</var> called there.
         * 
         * <p>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
         * <var>processClass</var> 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启动三部曲(二)

          本文链接:https://www.haomeiwen.com/subject/sgvwyxtx.html