跟着Innost理解下ActivityManagerService

SystemServer中AMS的调用轨迹
以一个Crash的应用进程为出发点,分析AMS如何打理该应用进程的身后事
AMS中应用进程的调度、内存管理

1. AMS由SystemServer的ServerThread创建的调用轨迹

· AMS的main函数:创建AMS实例,其中最重要的工作是创建Android运行环境,得到一个ActivityThread和一个Context对象。
· AMS的setSystemProcess函数:该函数注册AMS和meminfo等服务到ServiceManager中。另外,它为SystemServer创建了一个ProcessRecord对象。由于AMS是Java世界的进程管理及调度中心,要做到对Java进程一视同仁,尽管SystemServer贵为系统进程,此时也不得不将其并入AMS的管理范围内。
· AMS的installSystemProviders:为SystemServer加载SettingsProvider。
· AMS的systemReady:做系统启动完毕前最后一些扫尾工作。该函数调用完毕后,HomeActivity将呈现在用户面前。

1.1 ActivityManagerService main分析

1.1.1 AThread分析
1.1.1.1 AThread分析
1.1.1.2 AMS的构造函数分析
创建BSS、USS、mProcessStats (ProcessStats类型)、mProcessStatsThread线程
创建/data/system目录,为 mCompatModePackages (CompatModePackages 类型)和mConfiguration (Configuration类型)等成员变量赋值
1.1.2 ActivityThread.systemMain函数分析
systemMain函数将为SystemServer进程搭建一个和应用进程一样的Android运行环境。用systemMain函数结束后:
· 得到一个ActivityThread对象,它代表应用进程的主线程。
· 得到一个Context对象,它背后所指向的Application环境与framework-res.apk有关。
1.1.2.1 ActivityThread.attach()
1.1.2.1.1 Instrumentation:Base class for implementingapplication instrumentation code. When running with instrumentation turned on,this class will be instantiated for you before any of the application code,allowing you to monitor all of the interaction the system has with the application.An Instrumentation implementation is described to the system through anAndroidManifest.xml’s tag.
1.1.2.1.2 Application:Base class for those who need tomaintain global application state. You can provide your own implementation byspecifying its name in your AndroidManifest.xml’s tag,which will cause that class to be instantiated for you when the process foryour application/package is created.
1.1.2.1.3 Context:Interface to global informationabout an application environment. This is an abstract class whoseimplementation is provided by the Android system. It allows access toapplication-specific resources and classes, as well as up-calls forapplication-level operations such as launching activities, broadcasting andreceiving intents, etc.

1.1.2.2 ActivityThread.getSystemContext()
这里写图片描述

1.1.3 ActivityThread.getSystemContext()
Context hierachy
这里写图片描述

1.1.4 AMS的startRunning()

1.1.5 ActivityManagerService的main函数总结
AMS的main函数的目的有两个:
· 首先也是最容易想到的目的是创建AMS对象。
· 另外一个目的比较隐晦,但是非常重要,那就是创建一个供SystemServer进程使用的Android运行环境。
根据目前所分析的代码,Android运行环境将包括两个成员:ActivityThread和ContextImpl(一般用它的基类Context)。
这里写图片描述

1.2 AMS的setSystemProcess()

· 注册AMS、meminfo、gfxinfo等服务到ServiceManager中。
· 根据PKMS返回的ApplicationInfo初始化Android运行环境,并创建一个代表SystemServer进程的ProcessRecord,从此,SystemServer进程也并入AMS的管理范围内。

1.2.1 ActivityThread.installSystemApplicationInfo()
installSystemApplicationInfo函数的参数为一个ApplicationInfo对象,该对象由AMS通过Context查询PKMS中一个名为“android”的package得来(根据前面介绍的知识,目前只有framework-res.apk声明其package名为“android”)。

mSystemContext在AMS的main函数中已经初始化过了,此处为何再次初始化呢?
Context第一次执行init的目的仅仅是为了创建一个Android运行环境,而该Context并没有和实际的ApplicationInfo绑定。而第二次执行init前,先利用Context和PKMS交互得到一个实际的ApplicationInfo,然后再通过init将此Context和ApplicationInfo绑定。

1.2.2 关于ProcessRecord和IApplicationThread的介绍
AMS如何与应用进程交互?例如AMS启动一个位于其他进程的Activity,由于该Activity运行在另外一进程中,因此AMS势必要和该进程进行跨进程通信。
答案自然是通过Binder进行通信。为此,Android提供了一个IApplicationThread接口,该接口定义了AMS和应用进程之间的交互函数

这里写图片描述
ApplicationThreadNative实现了IApplicationThread接口。AMS通过它可以和应用进程进行交互,例如,AMS启动一个Activity的时候会调用该接口的scheduleLaunchActivity函数。服务端自然在应用进程中,因为AMS需要监听应用进程的死亡通知。
ActivityThread通过成员变量mAppThread指向它的内部类ApplicationThread,而ApplicationThread从ApplicationThreadNative派生。

ProcessRecord除保存和应用进程通信的IApplicationThread对象外,还保存了进程名、不同状态对应的Oom_adj值及一个ApplicationInfo。一个进程虽然可运行多个Application,但是ProcessRecord一般保存该进程中先运行的那个Application的ApplicationInfo。

AMS中有两个成员变量用于保存ProcessRecord,一个是mProcessNames,另一个是mPidsSelfLocked:
这里写图片描述

1.3 AMS的installSystemProviders

还记得Settings数据库吗?SystemServer中很多Service都需要向它查询配置信息。为此,Android提供了一个SettingsProvider来帮助开发者。该Provider在SettingsProvider.apk中,installSystemProviders就会加载该APK并把SettingsProvider放到SystemServer进程中来运行。
此时的SystemServer已经加载了framework-res.apk,现在又要加载另外一个APK文件,这就是多个APK运行在同一进程的典型案例。另外,通过installSystemProviders函数还能见识ContentProvider的安装过程,下面就来分析它。
1.3.1 AMS的 generateApplicationProvidersLocked()
先从PKMS那里查询满足条件的ProviderInfo信息,而后将它们分别保存到AMS和ProcessRecord中对应的数据结构中。
1.3.1.1 PMS中 queryContentProviders() 从PKMS那里查找满足条件的Provider,然后生成AMS使用的ProviderInfo信息.
1.3.1.2 AMS及ProcessRecord中保存的ContentProvider
· AMS保存ProviderInfo的原因是它要管理ContentProvider。
· ProcessRecord保存ProviderInfo的原因是ContentProvider最终要落实到一个进程中。其实也是为了方便AMS管理,例如该进程一旦退出,AMS需要把其中的ContentProvider信息从系统中去除。
AMS及ProcessRecord均使用了一个新的数据结构ContentProviderRecord来管理ContentProvider信息。
这里写图片描述

1.3.2 ActivityThread 的installSystemProviders()
在AMS和ProcessRecord中都保存了Provider信息,但这些仅仅都是一些信息,并不是ContentProvider,因此下面要创建一个ContentProvider实例(即SettingsProvider对象)。该工作由ActivityThread的installSystemProviders来完成. installContentProviders实际上是标准的ContentProvider安装时调用的程序。安装ContentProvider包括两方面的工作:
· 先在ActivityThread中通过installProvider得到一个ContentProvider实例。
· 向AMS发布这个ContentProvider实例。如此这般,一个APK中声明的ContentProvider才能登上历史舞台,发挥其该有的作用。
1.3.2.1 ActivityThread的installProvider()
这里写图片描述
1.3.2.2 ASM的 publishContentProviders()
publicContentProviders函数用于向AMS注册ContentProviders。
这里应解释一下publishContentProviders的工作流程:
· 先根据调用者的pid找到对应的ProcessRecord对象。
· 该ProcessRecord的pubProviders中保存了ContentProviderRecord信息。该信息由前面介绍的AMS的generateApplicationProvidersLocked函数根据Package本身的信息生成。此处将判断要发布的ContentProvider是否由该Package声明。
· 如果判断返回成功,则将该ContentProvider及其对应的authority加到mProvidersByName中。注意,AMS中还有一个mProvidersByClass变量,该变量以ContentProvider的ComponentName为key,即系统提供多种方式找到某一个ContentProvider,一种是通过 authority,另一种方式就是指明ComponentName。
· mLaunchingProviders和最后的notifyAll函数用于通知那些等待ContentProvider所在进程启动的客户端进程。例如,进程A要查询一个数据库,需要通过进程B中的某个ContentProvider 来实施。如果B还未启动,那么AMS就需要先启动B。在这段时间内,A需要等待B启动并注册对应的ContentProvider。B一旦完成注册,就需要告知A退出等待以继续后续的查询工作。

1.4 ASM的systemReady()

systemReady函数完成了系统就绪的必要工作,然后它将启动Home Activity。至此,Android系统就全部启动了。
1.4.1 systemReady第一阶段的工作
systemReady第一阶段的工作并不轻松,其主要职责是发送并处理与PRE_BOOT_COMPLETED广播相关的事情。目前代码中还没有接收该广播的地方,不过从代码中的注释中可猜测到,该广播接收者的工作似乎和系统升级有关。
1.4.2 systemReady第二阶段的工作
systemReady第二阶段的工作包括:
· 杀死那些竟然在AMS还未启动完毕就先启动的应用进程。注意,这些应用进程一定是APK所在的Java进程,因为只有应用进程才会向AMS注册,而一般Native(例如mediaserver)进程是不会向AMS注册的。
· 从Settings数据库中获取配置信息,目前只取4个配置参数,分别是:”debug_app”(设置需要debug的app的名称)、”wait_for_debugger”(如果为1,则等待调试器,否则正常启动debug_app)、”always_finish_activities”(当一个activity不再有地方使用时,是否立即对它执行destroy)、”font_scale”(用于控制字体放大倍数,这是Android 4.0新增的功能)。以上配置项由Settings数据库的System表提供。
1.4.3 systemReady第三阶段的工作
systemReady第三阶段的工作有3项:
· 调用systemReady设置的回调对象goingCallback的run函数。
· 启动那些声明了persistent的APK。
· 启动桌面。
1.4.3.1 goingCallback的run
run函数比较简单,执行的工作如下:
· 执行startSystemUi,在该函数内部启动SystemUIService,该Service和状态栏有关。
· 调用一些服务的systemReady函数。
· 启动Watchdog。
1.4.3.2 启动Home界面
1.4.3.3 发送ACTION_BOOT_COMPLETED广播

2. AMS中的进程管理

Android平台中很少能接触到进程的概念,取而代之的是有明确定义的四大组件。但是作为运行在Linux用户空间内的一个系统或框架,Android不仅不能脱离进程,反而要大力利用Linux OS提供的进程管理机制和手段,更好地为自己服务。作为Android平台中组件运行管理的核心服务,ActivityManagerService当仁不让地接手了这方面的工作。目前,AMS对进程的管理仅涉及两个方面:
· 调节进程的调度优先级和调度策略。
· 调节进程的OOM值。

2.1 Linux进程管理介绍

2.2 关于Android中的进程管理的介绍

前面介绍了Linux OS中进程管理(包括调度和OOM控制)方面的API,但AMS是如何利用它们的呢?这就涉及AMS中的进程管理规则了。

2.2.1 进程分类

Android将应用进程分为五大类,分别为Forground类、Visible类、Service类、Background类及Empty类。这五大类的划分各有规则。

2.2.1.1 Forground类
该类中的进程重要性最高,属于该类的进程包括下面几种情况:
· 含一个前端Activity(即onResume函数被调用过了,或者说当前正在显示的那个Activity)。
· 含一个Service,并且该Service和一个前端Activity绑定(例如Music应用包括一个前端界面和一个播放Service,当我们一边听歌一边操作Music界面时,该Service即和一个前端Activity绑定)。
· 含一个调用了startForground的Service,或者该进程的Service正在调用其生命周期的函数(onCreate、onStart或onDestroy)。
· 最后一种情况是,该进程中有BroadcastReceiver实例正在执行onReceive函数。

2.2.2.2 Visible类
属于Visible类的进程中没有处于前端的组件,但是用户仍然能看到它们,例如位于一个对话框后的Activity界面。目前该类进程包括两种:
· 该进程包含一个仅onPause被调用的Activity(即它还在前台,只不过部分界面被遮住)。
· 或者包含一个Service,并且该Service和一个Visible(或Forground)的Activity绑定(从字面意义上看,这种情况不太好和Forground进程中第二种情况区分)。

2.2.2.3 Service类、Background类及Empty类
这三类进程都没有可见的部分,具体情况如下。
· Service进程:该类进程包含一个Service。此Service通过startService启动,并且不属于前面两类进程。这种进程一般在后台默默地干活,例如前面介绍的MediaScannerService。
· Background进程:该类进程包含当前不可见的Activity(即它们的onStop被调用过)。系统保存这些进程到一个LRU(最近最少使用)列表。当系统需要回收内存时,该列表中那些最近最少使用的进程将被杀死。
· Empty进程:这类进程中不包含任何组件。为什么会出现这种不包括任何组件的进程呢?其实很简单,假设该进程仅创建了一个Activity,它完成工作后主动调用finish函数销毁(destroy)自己,之后该进程就会成为Empty进程。系统保留Empty进程的原因是当又重新需要它们时(例如用户在别的进程中通过startActivity启动了它们),可以省去fork进程、创建Android运行环境等一系列漫长而艰苦的工作。

2.2.2 Process类API介绍

Android平台中进程调度和OOM控制的API,它们统一被封装在Process.java中
Process类还为不同调度优先级定义一些非常直观的名字以避免在代码中直接使用整型,例如为最低的调度优先级19定义了整型变量THREAD_PRIORITY_LOWEST。除此之外,Process还提供了fork子进程等相关的函数。
注意Process.java中的大多数函数是由JNI层实现的,其中Android在调度策略设置这一功能上还有一些特殊的地方,感兴趣的读者不妨阅读system/core/libcutils/sched_policy.c文件。

2.2.3 关于ProcessList类和ProcessRecord类的介绍

2.2.3.1 ProcessList类的介绍
ProcessList类有两个主要功能:
· 定义一些成员变量,这些成员变量描述了不同状态下进程的oom_adj值。
· 在Android 4.0之后,LMK的配置参数由ProcessList综合考虑手机总内存大小和屏幕尺寸后再行设置(在Android 2.3中,LMK的配置参数在init.rc中由init进程设置,并且没有考虑屏幕尺寸的影响)。读者可自行阅读其updateOomLevels函数,此处不再赘述。

2.2.3.2 ProcessRecord中相关成员变量的介绍
ProcessRecord定义了较多成员变量用于进程管理。笔者不打算深究其中的细节。这里仅把其中的主要变量及一些注释列举出来。下文会分析到它们的作用。

2.3 AMS进程管理函数分析

在AMS中,和进程管理有关的函数只要有两个,分别是updateLruProcessLocked和updateOomAdjLocked。这两个函数的调用点有多处,本节以attachApplication为切入点,尝试对它们进行分析。
注意AMS一共定义了3个updateOomAdjLocked函数,此处将其归为一类。
先回顾一下attachApplication函数被调用的情况:AMS新创建一个应用进程,该进程启动后最重要的就是调用AMS的attachApplication。

2.3.1 updateLruProcessLocked函数分析

updateLruProcessLocked的主要工作是根据app的lruWeight值调整它在数组中的位置。lruWeight值越大,其在数组中的位置就越靠后。如果该app和某些Service(仅考虑通过bindService建立关系的那些Service)或ContentProvider有交互关系,那么这些Service或ContentProvider所在的进程也需要调节lruWeight值。

2.3.2 updateOomAdjLocked函数分析

2.3.2.1 updateOomAdjLocked分析之一
updateOomAdjLocked第一阶段的工作看起来很简单,但是其中也包含一些较难理解的内容。
· 处理hidden adj,划分9个级别。
· 根据mLruProcesses中进程个数计算每个级别平均会存在多少进程。在这个计算过程中出现了一个魔数4令人极度费解。
· 然后利用一个循环从mLruProcesses末端开始对每个进程执行另一个updateOomAdjLocked函数。关于这个函数的内容,我们放到下一节再讨论。
· 判断处于Hidden状态的进程数是否超过限制,如果超过限制,则会杀死一些进程。

2.3.2.2 updateOomAdjLocked分析之二
可获得两个信息:
· Android 4.0增加了新的接口类ComponentCallbacks2,其中只定义了一个函数onTrimMemory。从以上描述中可知,它主要通知应用进程进行一定的内存释放。
· Android 4.0 Settings新增了一个开放人员选项,通过它可控制AMS对后台Activity的操作。
这里和读者探讨一下ComponentCallbacks2接口的意义。此接口的目的是通知应用程序根据情况做一些内存释放,但笔者觉得,这种设计方案的优劣尚有待考证,主要是出于以下下几种考虑:
第一,不是所有应用程序都会实现该函数。原因有很多,主要原因是,该接口只是SDK 14才有的,之前的版本没有这个接口。另外,应用程序都会尽可能抢占资源(在不超过允许范围内)以保证运行速度,不应该考虑其他程序的事情。
第二个重要原因是无法区分在不同的level下到底要释放什么样的内存。代码中的注释也是含糊其辞。到底什么样的资源可以在TRIM_MEMORY_BACKGROUND级别下释放,什么样的资源不可以在TRIM_MEMORY_BACKGROUND级别下释放?
既然系统加了这些接口,读者不妨参考源码中的使用案例来开发自己的程序。
建议真诚希望Google能给出一个明确的文档,说明这几个函数该怎么使用。

2.3.3 第二个updateOomAdjLocked分析

主要完成两项工作:
· 调用computeOomAdjLocked计算获得某个进程的oom_adj和调度策略。
· 调整进程的调度策略和oom_adj。
建议思考一个问题:为何AMS只设置进程的调度策略,而不设置进程的调度优先级?

2.3.4 computeOomAdjLocked分析

这段代码较长,其核心思想是综合考虑各种情况以计算进程的oom_adj和调度策略。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值