Android AMS 自述
本文摘要
本文采用自述的方式带大家认识ActivityManagerService
是啥,它有哪些功能,它的启动过程,它的模块有哪些以及模块的作用是啥。(文中代码基于Android13
)
本文大纲
我是一个binder服务
大家好,我是ActivityManagerService
,我存在于systemserver
进程,大家可以看到我的名字中带有Service
这个单词,没错我是一个服务是一个binder
服务,在systemserver
进程中存在着很多像我一样的binder
服务。
既然我是一个服务,那我肯定会提供很多的功能供我的使用方来使用,请大家上眼看下图:(下图只是罗列了一部分功能)
如上图,可以看到我提供的功能的使用方主要是App
,它们与我不在同一进程,因此它们想要使用我的功能是需要通过ActivityManager
和ActivityTaskManager
进行binder
调用来使用,这也就是为啥我被称为binder
服务,而使用方自然被称为binder client
。
从上图还可以看出凡是与Service
、ContentProvider
、BroadcastReceiver
三大组件有关系的事情都归我ActivityManagerService
负责,而Activity
相关的事情都归ActivityTaskManagerService
负责 (以下简称ATMS
),其实在原先的Android版本中,Activity
相关的事情是归我ActivityManagerService
负责的,但是存在一个很大的问题,就是ActivityManagerService
中的代码量越来越多,进而导致高耦合、难以维护等问题产生,为了解决这些问题故把Activity
相关的事情放入了ATMS
内。别看ATMS
也是一个binder
服务,但它还是归我ActivityManagerService
管理的。
我虽然是一个服务,但是我更愿意称自己为集中处理中心,正如上图四大组件的启动、销毁及生命周期方法的变化这些事情统统都需要经过我进行处理。比如某App
要启动一个Activity
,那启动该Activity
的事件会通知到ATMS
,ATMS
会进行一些检查,最终“批准”是否能启动该Activity
;比如某个Service
需要销毁了,那我会把销毁的事件通过binder
通信通知相应的App
;比如某个BroadcastReceiver
需要接收广播了,则我会把广播内容通过binder
通信发送给相应App
。
你要是以为我的工作只是管理四大组件,那可是就小瞧我了,我可是systemserver
进程中最忙碌、包含功能最多的服务,我还负责进程管理、App错误/崩溃管理、性能分析等事情。
为啥进程管理这么重要的事情交给我呢?其主要原因是每个App
进程的产生源头都是由于四大组件的启动,也就是说只有启动四大组件才会导致App
进程的产生,并且四大组件的生命周期变化都会导致App
进程状态发生变化,比如一个App
的最后一个Activity
由resume
状态变为stop
状态,那该App
就变为了后台进程。由于以上原因,我就把进程管理功能也据为己有了。
进程管理就是管理App
进程,比如App
死亡了,那进程管理就需要把该App
对应的进程信息进行销毁处理;比如App
由前台退到后台了,那进程管理就需要把App
对应进程的进程状态置为后台状态,并且为进程计算一个oom_score
(一个分数值),该分数值和该进程id会交给lmkd进程,lmkd进程会在低内存的时候根据oom_score
杀掉对应进程以释放内存。当然进程管理可不止上面这点内容,后面文章会有详细介绍。
App
错误/崩溃管理就是在App
由于crash
/ANR
(Application not response
)/自杀等原因导致App
死亡,该模块会负责处理App
死亡的相关事情,比如收集崩溃的堆栈信息,记录App
死亡的信息等。性能分析可以帮助罗列出每个App
占用的内存大小等这些数据,来帮助进行性能分析。
大家对我拥有的能力有一定了解后,会不会有一种感觉,觉得我的名字ActivityManagerService
是完全没有把我具有的能力表达出来的,还以为我只与Activity
有关系。确实是这样的,我也一直在为这个“不合格的名字”而烦恼。但烦恼有啥用啊,已经是既定事实了,那只能接受了。
以上就是关于我的一个介绍,因为我在整个Android
系统中非常的有名气,就像大家对名人/伟人的成长史有非常浓厚的兴趣一样,大家肯定对于我的“成长史”,也就是我的的启动过程也非常的感兴趣,那我就毫不保留的分享给大家。
我的启动过程
在介绍我的启动过程之前,我觉得非常有必要给大家简单介绍一个背景知识SystemServe
r启动(SystemServer
是一个类),为啥要介绍SystemServer
启动呢?首先因为我的启动过程是SystemServer
启动过程中的一部分,我启动成功与否会关系到SystemServer
启动成功与否;其次SystemServer
启动时会发送各种阶段值,而和我ActivityManagerService
有关的阶段值就有俩。
背景知识
systemserver
进程被zygote
进程fork
成功后,SystemServer
启动就是重头戏了,而SystemServer
的启动过程其实就是各种服务的启动过程,只有SystemServer
启动成功后,Android
设备才可以被用户使用。下图展示了SystemServer
启动过程的主要方法:(下面的方法都属于SystemServer
类)
因为systemserver
进程包含的服务非常多,那该如何保证这些服务顺利启动呢,SystemServer
把所有的服务分类为bootstrap service
、core service
、other service
、apex service
这四种类型。先在startBootstrapServices
方法中启动bootstrap
类型的服务,其次在startCoreServices
方法中启动core
类型的服务,再次在startOtherServices
方法中启动other
类型的服务,最后在startApexServices
启动apex
类型的服务。因此SystemServer
启动过程就是依照上面的顺序启动各种服务,而我ActivityManagerService
很荣幸属于bootstrap
类型的服务,因此是最早被初始化,为啥是初始化呢,因为ActivityManagerService
的真正启动过程还在后面流程中。
阶段值 (phase)
对所有的服务进行分类,只是解决了它们启动先后的问题,而服务与服务之间是存在依赖关系的,比如InputManagerService
、WindowManagerService
等是需要知道ActivityManagerService
是否启动完成了,当得知ActivityManagerService
启动完成后,才可以使用它提供的功能比如注册广播接收器、发送广播等。而解决这个问题的办法就是发送阶段值 (phase
),下图展示了SystemServer
启动过程中发送的阶段值:
如上图,在SystemServer
的启动过程中会分发不同的阶段值,不同的阶段值所代表的含义各不相同,服务收到这些阶段值就可以知道当前是到了哪个启动阶段了,同时根据自己的需求来做不同的事情。
PHASE_WAIT_FOR_DEFAULT_DISPLAY代表当前阶段正在等待获取显示屏幕的硬件信息,这时候SystemServer
的启动过程就暂时处于暂停状态,若屏幕硬件信息获取失败则停止启动;否则继续启动。
PHASE_ACTIVITY_MANAGER_READY代表ActivityManagerService
已经准备好了,其他的服务可以使用ActivityManagerService
提供的服务了,比如发送广播。
PHASE_BOOT_COMPLETED代表SystemServer
启动完成了,所有的服务都已经准备好“进入工作状态”了,Android设备可以被用户使用了 (当然这只是解释几个常用的阶段值)。
想了解更多SystemServer
的内容,可以看此文章
小结
ActivityManagerService
的启动过程对于SystemServer
的启动来说非常重要,ActivityManagerService
在StartBootstrapServices
方法中被初始化,在StartOtherServices
方法做收尾工作。
而PHASE_ACTIVITY_MANAGER_READY
、PHASE_BOOT_COMPLETED
这两个阶段值是与ActivityManagerService
有关系的。PHASE_ACTIVITY_MANAGER_READY
自不用说代表我ActivityManagerService
已经准备好了。而PHASE_BOOT_COMPLETED
阶段值是由我发送的,我再重复一遍===PHASE_BOOT_COMPLETED这个值可是由我来发送的===。
启动步骤
好了背景知识介绍完毕后,那来看下我的启动分为哪些步骤,请大家看下图
如上图,我的启动可以分为出生、设置系统进程、准备、等待boot anim完成这四个步骤,出生这个步骤发生于startBootstrapServices
方法的最前面,而设置系统进程这个步骤发生于startBootstrapServices
方法的中间部分,准备这个步骤发生于startOtherServices
方法的结尾部分,而等待boot anim
则是最后一个步骤。可以看到ActivityManagerService
的启动贯穿了Systemserver
启动流程的全过程。那就按这四个步骤来介绍下我的启动吧。
出生
别看我是一个服务,但我也像其他类一样也是一个类,我的“出生”是指创建一个ActivityManagerService
实例,而我的“出生地”是在SystemServer
类的startBootstrapServices
的方法中,既然创建ActivityManagerService
实例,那肯定需要调用它的构造方法,在ActivityManagerService
的构造方法中对很多的属性做了初始化的操作,以下列出了对主要属性做的一些初始化操作:
- mProcessList属性,它是
ProcessList
类型,它负责进程管理的工作,会构造该类型的实例,并且调用它的init
方法进行初始化。 - mAppProfiler属性,它是
AppProfiler
类型,它负责性能分析的工作,会创建该类型的实例。 - mOomAdjuster属性,它是
OomAdjuster
类型,它也负责进程管理的工作,同样也会创建该类型的实例。 - mFgBroadcastQueue、mBgBroadcastQueue、mBgOffloadBroadcastQueue、mFgOffloadBroadcastQueue都是
BroadcastQueue
类型的,它们对BroadcastReceiver
进行管理,同样也会创建这些类型的实例。 - mServices属性,它是
ActiveServices
类型,它对Service
进行管理,同样也会创建该类型的实例。 - mCpHelper属性,它是
ContentProviderHelper
类型,它对ContentProvider
进行管理,同样也会创建该类型的实例。 - mAppErrors属性,它是
AppErrors
类型,它对App
的死亡进行管理,同样也会创建该类型的实例。
上面只是列出了主要的属性初始化,在ActivityManagerService
构造方法中还对其他的属性进行了初始化,自此我ActivityManagerService
就“出生”了,在内存中占有了一席之地。设置系统进程
我虽然“出生”了,但是我所在的systemserver
进程很多信息还没有设置,它就犹如一个“黑户”的存在。大家都知道我有一个功能是进程管理,而进程管理有一个规定就是凡是App进程甚至包含systemserver
进程在被创建后,都需要使用ProcessRecord
对象把进程信息记录下来,这样进程管理才可以更好的对进程进行管理。
并且lmkd进程也同样有一个规定,就是需要进程管理把进程的pid
、oom_score
交给它,这样它才可以查杀进程,而这里的进程是所有的App进程
和systemserver进程
,因此进程管理也需要把systemserver进程
的pid
和它的oom_score
传递给lmkd进程
。
而上面这些事情就是设置系统进程这一步所要做的,当然还做了别的一些事情。因为我具有进程管理的功能,因此设置系统进程的事情就落在了我的头上。设置系统进程同样也发生于startBootstrapServices
方法,那该步到底都做了哪些事情呢,那就听我细细道来。添加到ServiceManager服务
大家都知道我是一个binder
服务,并且我是一个具名binder
服务,为了让使用方能使用到我提供的这些能力,我需要做的一件事情就是把我自己添加到ServiceManager
服务中,ServiceManager
服务是位于servicemanager
进程的服务,它的作用就是管理所有的具名binder
服务。当然除了把我自己添加到ServiceManager
服务外,还把其他的binder
服务也加到了ServiceManager
服务中,请看下面代码:fold 1
2
3
4
5
6
7
8
9
10
11//ActivityManagerService
public void setSystemProcess() {
省略代码······
//Context.ACTIVITY_SERVICE的值是activity,下面方法把ActivityManagerService加入到ServiceManager服务中
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
//下面代码把其他具名binder服务加入到ServiceManager服务中
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH);
省略代码······
}创建ProcessRecord
创建ProcessRecord
对象把systemserver
进程信息保存下来。大家都知道每个App
进程都有对应的包名,而systemserver
进程也不例外,我把它的包名定义为android
,下面代码做了以上这些事情:fold 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//ActivityManagerService
public void setSystemProcess() {
省略代码······
//创建包名为 android 的ApplicationInfo对象
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
//创建ProcessRecord对象
ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
false,
0,
false,
0,
null,
new HostingRecord(HostingRecord.HOSTING_TYPE_SYSTEM));
app.setPersistent(true); //设置为persistent
app.setPid(MY_PID);
app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);//设置最大adj值,SYSTEM_ADJ的值为-900
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
app.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_SYSTEM);
addPidLocked(app);
//把app保存到ProcessLisst对象中
updateLruProcessLocked(app, false, null);
}
省略代码······
}传递信息给lmkd进程
lmkd进程规定在查杀进程的时候是按照oom_score
从高到低进行查杀,也就是oom_score
越高越先被杀掉。而我所在systemserver
进程那可是非常非常重要的进程,肯定是不能随随便便让lmkd
杀掉的,因此我把systemserver
进程的oom_score
设置为一个固定的值-900,这样lmkd就不会杀掉它了。如下是相应代码:fold 1
2
3
4
5
6
7//ActivityManagerService
public void setSystemProcess() {
省略代码······
//下面方法,会更新进程的oom_score及进程状态等信息,并且会把进程的新的oom_score及pid发送给lmkd进程
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
省略代码······
}小结
经过此步骤,我被添加到ServiceManager
服务中,这样我的使用方比如各种App
就可以使用我的功能了。我所在的systemserver
进程被ProcessRecord
对象记录下来,并且它不再“无名无姓”了,它的包名就是android
。这样进程管理就可以对systemserver
进程进行管理了。同时systemserver
进程的pid
和oom_score:-900
信息也传递给lmkd
进程,便于lmkd
更好的查杀进程。
经过此步骤后,SystemServer
后面的启动流程中各种服务就可以从进程管理根据包名android
拿到systemserver
进程对应的ProcessRecord
对象,进而做相应的处理了。准备
SystemServer
的启动流程依然在继续进行,经过startBootstrapServices
、startCoreServices
方法后,进入到startOtherServices
方法的尾声时,这时候SystemServer
的启动算是接近了尾声。而SystemServer
会调用我ActivityManagerService
的systemReady
方法,告知我系统准备好了,但是准备好并不代表Android设备可以被用户使用了,而要达到完全可以工作状态,那剩下的事情还需要我来处理。自此我的启动进入了准备这一步。
而准备这一步又被我划分为准备前、准备好、准备后这三小步,那就依次来介绍下它们吧。准备前
在“出生”那一步的时候,ActivityManagerService
实例被创建并且它包含的很多属性也被初始化,比如四大组件的管理类。虽然它们被初始化了,但是系统准备好这个激动人心的消息它们还一无所知,而准备前这一步,我会把系统准备好这个消息通知给它们,当然还做一些别的工作,请看下面代码:fold 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//ActivityManagerService
public void systemReady(final Runnable goingCallback, TimingsTraceAndSlog t) {
省略代码······
synchronized(this) {
省略代码······
mLocalDeviceIdleController =
LocalServices.getService(DeviceIdleInternal.class);
//告知相应模块,系统已经准备好了
mActivityTaskManager.onSystemReady();
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
mAppOpsService.systemReady();
mProcessList.onSystemReady();
mAppRestrictionController.onSystemReady();
mSystemReady = true;
}
省略代码······
retrieveSettings();//检索一些数据从settings了或者其他地方,并且用这些数据来初始化一些属性
省略代码······
}准备好
准备前的事情做完后,我就进入准备好这一步,因为我及所有的属性都准备好了,可以很好的工作了,因此我会把PHASE_ACTIVITY_MANAGER_READY
这个阶段值发送出去,告知各种服务,我ActivityManagerService
已经准备好了啊,你们可以使用我的提供的服务了啊。
如下是相关代码:fold 1
2
3
4
5
6
7
8
9private void startOtherServices( { TimingsTraceAndSlog t)
省略代码······
mActivityManagerService.systemReady(() -> {
省略代码······
//发送PHASE_ACTIVITY_MANAGER_READY阶段值
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
省略代码······
}准备后
我把我准备好的消息告知所有服务后,我还得继续回来把剩余的事情做完,其中最重要的一件事情是启动launcher
,只有launcher
启动起来后Android设备才可以被用户使用。
如下是相关代码:fold 1
2
3
4
5
6
7//ActivityManagerService
public void systemReady(final Runnable goingCallback, TimingsTraceAndSlog t) {
省略代码······
mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady"); //启动launcher
省略代码······
}等待boot anim完成
首先我来介绍下boot anim
,它是在Android系统启动过程中显示的一个启动动画,这个启动动画消失后才可以代表Android设备完全可以被用户使用了,而该动画消失的一个前提是launcher
已经启动完成了。而我在收到launcher
启动完成的消息后会发送PHASE_BOOT_COMPLETED
阶段值,该值一发出去后,就代表Android设备可以被用户使用了,所有的服务都打起精神来进入工作状态了。
如下代码:fold 1
2
3
4
5
6
7
8ActivityManagerService
final void finishBooting() {
省略代码······
// Let system services know.
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_BOOT_COMPLETED);
省略代码······
}小结
我的启动贯穿了SystemServer
启动的整个流程,如下是启动过程所做的事情: - 在
SystemServer
最先启动的时候,会创建ActivityManagerService
的实例,并且初始化它包含的各种属性 - 我还承担了设置系统进程的任务,我会创建
ProcessRecord
对象把SystemServer
进程信息记录下来,并且为SystemServer
进程提供android这样的包名,这样SystemServer
进程就可以被记录并且被进程管理进行管理了,同时我还会把SystemServer
进程的pid
和oom_score:-900
的信息传递给lmkd
进程 - 在准备阶段我会把
PHASE_ACTIVITY_MANAGER_READY
这个阶段值发送给所有服务,并且还会启动launcher
,启动launcher
可是一个系统能不能被使用的一个重要环节 - 当收到
launcher
启动完成的消息后,boot anim
会消失,这时候我会发送PHASE_BOOT_COMPLETED
阶段值,该值可是代表Android设备可以被用户使用了。
既然Android系统已经启动完成了,SystemServer
进程中的服务以及其他的各种服务都已经进入“工作状态”了,那接下来我给大家介绍下我的“小伙伴”吧,正是由于它们的帮助才能保证我ActivityManagerService
的工作能够顺利进行。我的“小伙伴”
大家大可不必把我想象成超人,能胜任这么多复杂的工作,完全是由我和我的“小伙伴”们一起完成的,先来通过下图来认识下我的主要“小伙伴”
如上图,我的主要“小伙伴”有Activity管理模块、Service管理模块、BroadcastReceiver管理模块、ContentProvider管理模块、进程管理模块、App错误管理模块、App性能分析模块,当然我还有别的不是很重要的模块没有展示出来,那就依次把我的“小伙伴”介绍给大家吧。Activity管理模块
Activity
管理模块的主要作用是管理整个Android系统中的所有Activity
,比如Activity
的启动,Activity
的pause
,Activity
的stop
等可都是要经过该模块,而每个被启动的Activity
是使用一个ActivityRecord
对象保存的,我把该模块所做的事情全权委托给了ActivityTaskManagerService
类,该类也是一个binder
服务。关于该模块就暂时介绍到这,后面文章会详细介绍它。Service管理模块
Service
管理模块的主要作用是管理所有的Service
,Service
的启动、销毁等等都是该模块负责,而每个被启动的Service
是使用一个ServiceRecord
对象保存的,我同样把该模块所做的事情交给了ActiveServices
类,该类可不是一个binder
服务。BroadcastReceiver管理模块
BroadcastReceiver
管理模块的主要作用是管理所有的BroadcastReceiver
,而每个被启动的BroadcastReceiver
是使用一个BroadcastRecord
对象保存的,我同样把该模块所做的事情交给了BroadcastQueue
,而关于BroadcastQueue
的分类可是有好几种分发,会在后面专题中详细介绍。ContentProvider管理模块
ContentProvider
管理模块的主要作用是管理所有的ContentProvider
,而每个被启动的ContentProvider
同样使用一个ContentProviderRecord
对象保存的,我同样把该模块所做的事情交给了ContentProviderHelper
类。同样会在后面有专题详细介绍该模块。进程管理模块
上面的四个模块所做的事情是管理四大组件,而我除了管理四大组件外我还有非常重要的一个功能就是进程管理,而进程管理主要是进程管理模块在负责,进程管理模块所做的事情主要由ProcessList
和OomAdjuster
两个类完成,那就来看下进程管理模块都做了哪些事情吧。
首先在App
进程开始孵化(fork
)之前,ProcessList
会使用ProcessRecord
对象来保存App
进程,ProcessList
把孵化(fork)App
进程的请求发送给zygote
进程,zygote
进程孵化成功App
进程后,ProcessList
会收到这个消息,进而把孵化成功的App
进程的pid (进程id)
等信息保存到ProcessRecord
对象中。因此ProcessList
中保存了所有的App
进程。
进程管理模块所做的事情可不是仅仅把App
进程保存起来这么简单,不知道大家是否记得lmkd
进程 (不记得可以看lmkd进程杀手这篇文章),lmkd
进程的主要作用是根据内存的使用情况来杀掉分数最高的进程,而具有运行Java/Kotlin
代码的进程的分数是由OomAdjuster
来完成的,OomAdjuster
会对所有的进程都计算出一个分数 (oom_score
),比如当前正在显示的Activity
对应的App
的分数值是0,并且把分数、进程id、uid这些信息发送给lmkd进程。OomAdjuster
在计算每个进程的分数时,也同时会计算出每个进程对应的进程状态 (procState
),比如正在显示的Activity
对应的进程状态值是PROCESS_STATE_TOP
(它的值是2),和进程分数一样,进程状态值越大越容易被杀掉以及分配的资源越少。
当然除了依靠lmkd
进程杀进程外,OomAdjuster
也会在合适的时机根据一些条件来把进程状态为PROCESS_STATE_CACHED_EMPTY
或者PROCESS_STATE_CACHED_ACTIVITY
或者PROCESS_STATE_CACHED_ACTIVITY_CLIENT
的进程杀掉,这个杀进程的过程可是不需要lmkd
进程参与的。
对于一些处于后台又没有BroadcastReceiver
或者Service
或者ContentProvider
运行的进程,该模块会选择把它冻结或者杀死,负责冻结功能的类是CachedAppOptimizer
。
这里只是先简单带大家认识进程管理模块,同样也会在后面专题中着重介绍它。App错误管理模块
App
进程同生物的生命一样也会存在生、死,当App
进程死掉的时候,不管是由于崩溃或者ANR (Application not response)
或者自身原因的死掉,都需要App
错误管理模块来处理App
进程的“后世”,若App
死掉该模块会把App
进程当时死掉的堆栈状态保存下来,如果是由于ANR导致的死亡,也会把ANR
的各种信息保存下来。负责该模块的主要类是AppErrors
。App
进程在死掉的时候,也会把App
进程死掉的相关重要信息保存在文件中,而做这个工作的类是AppExitInfoTracker
,当然除了做这些工作之外,还有其他的工作内容就暂时不细说了。App性能分析模块
App
性能分析模块的主要作用是可以统计每个App
进程所暂用的CPU资源、内存的使用情况等数据,负责该模块的主要类是AppProfiler
,关于该模块会在后面专题详细介绍。小结
通过上面的介绍大家应该对我ActivityManagerService
和我的主要模块有了一个清晰的了解,当然对以上模块的介绍只是先简单带大家认识它们,后面会有各自专题详细的介绍它们。总结
本文带大家认识了ActivityManagerService
,及它的启动过程以及它的各个模块及模块作用。因为ActivityManagerService
的功能实在是太多了,一两篇文章是根本不可能把它的“魅力”全部都给展示出来的,因此后面的文章会针对每个模块进行详细的介绍,比如进程管理、Activity
管理、OomAdj
、Service
管理、ANR
及ANR
分析等内容。