13

从 App 启动过程看10.0 Framework 重构

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAxMTg2MjA2OA%3D%3D&%3Bmid=2649847971&%3Bidx=2&%3Bsn=f8322a6fba73848b45bdb89720d1ae4b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JfMRnqV.gif

作者:Michaelbest1

链接:https://www.jianshu.com/p/5a7035ee99e6

Android每个大版本都会对framework进行一定的重构,10.0也不例外。这次,谷歌把重构的对象瞄准了AMS。看过AMS代码的同学都知道,AcitivityManagerService.java是Android Framework里的一个超大文件。在Android 9.0里,AMS已经膨胀到28K+行。谷歌肯定是觉得不能忍了,于是对AMS进行了一定重构,目前10.0里AMS已经缩减到19K行了。相应的,为了配合AMS的重构,WMS也做了一些重构。本文就尝试从App启动过程作为一个切入点,来看看10.0上Framework都做了哪些相关的重构,并简单分析这些重构的原因。

注意:

  1. 本文假设读者对Android Framework有一定基础,特别是对App的启动过程有一定了解。如果对这个过程不太了解,可以先阅读相关文章,再来看10.0上的重构。

  2. 我会尝试分析每一个重构的原因。如果读者有更好的建议,非常欢迎交流!

重构1:引入ATMS(ActivityTaskManagerService),接管Activity生命周期相关接口的实现。

这是Android 10.0上AMS的最大重构。Android 10.0上,IActivityManager.aidl中的接口大部分都被移动到了IActivityTaskManagerService.aidl里,并在原接口处加上了UnsupportedAppUsage注解。这些接口就包含了我们启动Activity的重要接口startActivity:

// frameworks/base/core/java/android/app/IActivityManager.aidl
interface IActivityManager {
    ...
    @UnsupportedAppUsage
    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);
            ...
}

我们看到新的接口已经移动到了ActivityTaskManager.aidl里:

// frameworks/base/core/java/android/app/IActivityTaskManager.aidl
79/**
80 * System private API for talking with the activity task manager that handles how activities are
81 * managed on screen.
82 *
83 * {@hide}
84 */
85interface IActivityTaskManager {
86    int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
87            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
88            int flags, in ProfilerInfo profilerInfo, in Bundle options);
89    int startActivities(in IApplicationThread caller, in String callingPackage,
90            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
91            in Bundle options, int userId);

这个改动对一般开发者影响不大,因为正如注释所述,IActivityTaskManager的startActivity是系统私有API,系统内部调用的。开发者调用到的是Activity里的startActivity,并不会直接调用到ATMS的startActivity。真正调用ATMS的startActivity的地方就是以前调用AMS的地方:

// frameworks/base/core/java/android/app/Instrumentation.java
1678    @UnsupportedAppUsage
1679    public ActivityResult execStartActivity(
1680            Context who, IBinder contextThread, IBinder token, Activity target,
1681            Intent intent, int requestCode, Bundle options) {
...
1715            int result = ActivityTaskManager.getService()
1716                .startActivity(whoThread, who.getBasePackageName(), intent,
1717                        intent.resolveTypeIfNeeded(who.getContentResolver()),
1718                        token, target != null ? target.mEmbeddedID : null,
1719                        requestCode, 0, null, options);
1720            checkStartActivityResult(result, intent);
...
}

这里1715行,原来调用的就是AMS的startActivity。

到这里,我们已经了解了App端ATMS相关的重构。可以看到App端的影响并不大,只不过把原来部分调用AMS的binder call调整到ATMS里,而且这个对开发者基本透明。

再来看看服务端有哪些改变。我们先来看看现在AMS里startActivity是怎么实现的:

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
3567    @Override
3568    public int startActivity(IApplicationThread caller, String callingPackage,
3569            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
3570            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
3571        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
3572                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
3573    }

这里的mActivityTaskManager正如其名所暗示的那样是ActivityTaskManagerService实例的引用。因此我们可以知道,AMS把Activity生命周期相关的实现都委托到ATMS里了。ATMS并没有持有AMS的引用,仅有一处调用了AMS的一个静态方法。这样的设计可以降低ATMS对AMS的依赖。可以看出谷歌在OO设计方面还是比较讲究的。ATMS里的startActivity实现就和AMS基本是一样的,最终都是调用到startActivityAsUser。这里就不再贴代码了。

重构意义:这个重构的主要目的还是为了把AMS维护Acitvity生命周期相关的职责交给ATMS来承担。我们知道OO设计原则中有一项就是单一职责原则(Single Reponsibility Principle)。AMS的代码既然膨胀到这么大,那么原因很可能就是其承担了过多职责造成的。如果一个类承担的职责过多,就难免会增加其复杂度,降低类的内聚性,进而影响到后续类的可扩展性、可维护性等诸多方面。所以,当一个类承担的职责过多时,我们就有必要把其中一部分职责抽出来,交个其它类来承担。

重构2:ActivityStarter从am移动到wm

我们知道ActivityStarter是启动Activity时的一个重要类,它封装了启动Activity这个行为,包括这个行为需要的一些状态、属性等等。Android 10.0把这个类从am移动到了wm。简单对比了一下两个版本,这个类的改动并不大。从重要成员变量的角度看,10.0只是多了mRootActivityContainer和mRestrictedBgActivity两个。前者将会在重构3中讲解,后者是和10.0上引入的后台启动Activity限制相关的,具体可以参考谷歌官方网文档: 

https://developer.android.com/guide/components/activities/background-starts

重构意义:这个重构没有什么太多可说的。我估计谷歌只是觉得ActivityStarter和ActivityStack, ActivityStackSupervisor等类的耦合更多,而这些类都在wm里,所以就把这个类也移动到wm里了。我们就不要给它强行加戏了。

重构3:引入RootActivityContainer

在启动Activity时,我们需要拿到当前的focusedStack。9.0上,可以直接通过ActivityStarter对象的mFocusedStack成员变量拿到。而10.0上,则是通过RootActivityContainer对象拿到的:

// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
966 final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();

这里mRootActivityContainer就是RootActivityContainer类的实例。RootActivityContainer是10.0新引入的类,我们可以先通过注释初步认识一下它的职责:

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
130/**
131 * Root node for activity containers.
132 * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The
133 * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.
134 */
135public class RootActivityContainer extends ConfigurationContainer
136        implements DisplayManager.DisplayListener 

通过注释我们看到,这个类是暂时用来分担ActivityStackSupervisor的部分职责的,主要目的是使ActivityContainer的结构和WindowContainer的结构保持一致。

重构意义:目前除了和WindowContainer保持一致外,尚未发现其它意义。留待后续继续学习。

重构4:startProcess核心逻辑移动到ProcessList里

ProcessList类在9.0上就有了。但9.0及之前,startProcess的核心逻辑都直接写在AMS里。10.0上,这些逻辑都被移动到了ProcessList里。这也是合理的。因为根据OO中的SRP原则,进程启动相关逻辑应当放到进程管理相关的类里,于是ProcessList就接管了这一任务。

重构意义:维持SRP的OO设计原则。

重构5:增加AppZygote类用于维护App的Zygote进程

ProcessList里增加了mAppZygotes和mAppZygoteProcesses两个集合分别用于维护App的Zygote进程和App的进程组:

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
379    /**
380     * The currently running application zygotes.
381     */
382    final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
383
384    /**
385     * The processes that are forked off an application zygote.
386     */
387    final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
388            new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();

于是在ProcessList.startProcess里,如果是App的zygote进程,就会走不一样的逻辑:

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
1813    private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
1814            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
1815            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
1816            long startTime) {
1817        try {
1818            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
1819                    app.processName);
1820            checkSlow(startTime, "startProcess: asking zygote to start proc");
1821            final Process.ProcessStartResult startResult;
1822            if (hostingRecord.usesWebviewZygote()) {
1823                startResult = startWebView(entryPoint,
1824                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
1825                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
1826                        app.info.dataDir, null, app.info.packageName,
1827                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
1828            } else if (hostingRecord.usesAppZygote()) {
1829                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
1830
1831                startResult = appZygote.getProcess().start(entryPoint,
1832                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
1833                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
1834                        app.info.dataDir, null, app.info.packageName,
1835                        /*useUsapPool=*/ false,
1836                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
1837            } else {
1838                startResult = Process.start(entryPoint,
1839                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
1840                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
1841                        app.info.dataDir, invokeWith, app.info.packageName,
1842                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
1843            }

在9.0之前的版本里,是没有1828行这个分支的。

但是目前没有发现维护AppZygotes的原因。从ProcessList的源码来看,只是在进程创建和销毁的时候分别把appZygote实例从集合中加入和移除,未发现其它使用这个集合的地方。需要后续调查。

重构意义:目前尚未发现这个重构的意义。留待后续学习。

总结:

本文从App启动过程讲解了Android 10.0上Framework的一些重构。从这些重构来看,主要目的还是为了细分职责,简化那些过于臃肿的模块,尽量做到高内聚低耦合。后续希望能继续深入研究Framework,体会OO设计原则、设计模式等在Android里的应用。

----------  END  ----------

重磅!后厂技术官-技术交流群已成立

扫码可添加后厂技术官助手, 可申请加入后厂技术官大群和细分方向群,细分方向已涵盖: Java、Python、机器学习、大前端 等群。

一定要备注: 开发方向+地点+学校/公司+昵称 (如Java开发+北京+美团+小北) ,根据格式备注,可更快被通过且邀请进群

2yQZfeq.png!web

▲长按加群

点个在看少个 bug :point_down:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK