第12章 Activity Manager进程管理

Android通过框架层提供的机制屏蔽了底层进程的概念,从应用层的视角,Android只有Activity、Service、Content Provider、Broadcast Receiver、Intent、Application和Task等组件,这样大大方便了应用程序的开发。但应用程序最终要运行于特定的进程中,因此应用程序组件的生命周期与进程是紧密联系在一起的。

Android的设计理念是尽量利用系统内存以提高多任务调度的效率,在应用程序退出后,即便其组件的生命周期已经运行完毕,系统通常也只是将该应用程序切换到后台,并不会“杀死”应用程序进程,这样便大大提高了下次启动或返回该应用程序的速度。可见应用程序组件的生命周期与进程并不是同步的。

Android中参与进程管理的主要模块是Activity Manager,其依赖于LRU weight、OOM adj和Low Memory Killer共同完成进程的管理。本章分析这部分内容。

12.1 LRU weight机制

LRU(Least Recently Used)是最近最少使用的意思,LRU weight用于衡量最近最少使用的权重,是Android进程管理的核心概念之一。

Android应用程序进程(包括建立了应用程序进程运行环境的system_server进程)启动后,都会在ActivityManagerService的成员变量中保存其ProcessRecord信息。保存ProcessRecord的方式有多种,例如在mPidsSelfLocked和mProcessNames成员变量中,分别以PID和进程名为键保存当前运行的应用程序进程的ProcessRecord信息,这种方式的存储顺序通常与应用程序组件的启动顺序相同,用于调度应用程序组件。此外,mLruProcesses以LRU顺序存储了当前运行的应用程序进程信息,mLruProcesses中第一个元素便是最近最少使用的进程对应的ProcessRecord,用于管理应用程序进程。

以下三种情况发生时可以更新mLruProcesses。

应用程序异常退出:调用handleAppDiedLocked更新mLruProcesses。

显式“杀死”指定进程:调用ActivityManagerService显式“杀死”进程时需要更新mLruProcesses。

启动和调度应用程序四大组件:调用updateLruProcessLocked更新mLruProcesses。

应用程序异常退出后,其进程也会被“杀死”,因此第一种情况和第二种情况都是从mLruProcesses列表中删除该进程信息。这里主要分析启动和调度应用程序四大组件导致mLruProcesses变化的情况。updateLruProcessLocked的入口点如图12-1所示。

第12章 Activity Manager进程管理 - 图1

图 12-1 updateLruProcessLocked的入口

在图12-1中,除了BroadcastQueue.processCurBroadcastLocked方法用于处理广播外,多数方法在第10章和第11章已经分析过,它们都与应用程序组件的调度有关。以ActivityStack.realStartActivityLocked为例,代码如下:


final boolean realStartActivityLocked(ActivityRecord r,

ProcessRecord app, boolean andResume, boolean checkConfig)

throws RemoteException{

……

//mService即ActivityManagerService, app是应用程序进程的ProcessRecord

mService.updateLruProcessLocked(app, true, true);//此处更新LRU列表

try{

……

//调度Activity生命周期的onCreate、onStart、onResume等方法

app.thread.scheduleLaunchActivity(……);

……


realStartActivityLocked是启动应用程序Activity的关键方法之一,realStartActivityLocked是如何调度Activity生命周期的onCreate、onStart、onResume等方法的,在第11章已经详细分析过了。这里分析在调度Activity之前所做的工作。updateLruProcessLocked的代码如下:


public final class ActivityManagerService extends ActivityManagerNative

implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback{

int mLruSeq=0;//LRU序列号,初始为0

……

final void updateLruProcessLocked(ProcessRecord app,

boolean oomAdj, boolean updateActivityTime){

mLruSeq++;//每次更新LRU列表时,LRU序列号自动加①

updateLruProcessInternalLocked(app, oomAdj, updateActivityTime,0);

}

……


updateLruProcessLocked首先将LRU序列号加1,用于标记一次更细LRU列表的操作,然后便将请求转发给updateLruProcessInternalLocked方法,代码如下:


public final class ActivityManagerService extends ActivityManagerNative

implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback{

……

int mLruSeq=0;//LRU序列号

……

final ArrayList<ProcessRecord>mLruProcesses//进程LRU列表

=new ArrayList<ProcessRecord>();

……

private final void updateLruProcessInternalLocked(ProcessRecord app,

boolean oomAdj, boolean updateActivityTime, int bestPos){

//首先判断LRU列表中是否已经存在已有进程信息,如果存在,则删除旧的,后续创建新的

int lrui=mLruProcesses.indexOf(app);

if(lrui>=0)mLruProcesses.remove(lrui);

int i=mLruProcesses.size()-1;

int skipTop=0;

app.lruSeq=mLruSeq;//在进程信息中存储其LRU序列号,这个序列号是唯一的

if(updateActivityTime){//参数为true

app.lastActivityTime=SystemClock.uptimeMillis();//记录当前时间

}

if(app.activities.size()>0){

//如果该进程中有运行的Activity,其lruweight便是当前时间

app.lruWeight=app.lastActivityTime;

}else if(app.pubProviders.size()>0){

/*如果该进程有发布的content provider,其lruweight是当前时间减去

一个偏移值,偏移值由CONTENT_APP_IDLE_OFFSET指定,默认为15s/

app.lruWeight=app.lastActivityTime-

ProcessList.CONTENT_APP_IDLE_OFFSET;

skipTop=ProcessList.MIN_HIDDEN_APPS;

}else{

/*除以上两种情况外,该进程的lruweight都是当前时间减去

CONTENT_APP_IDLE_OFFSET指定的偏移值,默认为120s/

app.lruWeight=app.lastActivityTime-

ProcessList.EMPTY_APP_IDLE_OFFSET;

skipTop=ProcessList.MIN_HIDDEN_APPS;

}

while(i>=0){//从后往前遍历LRU列表

ProcessRecord p=mLruProcesses.get(i);

……//skipTop没有起到作用

/*在LRU列表中找到该进程信息的插入位置,添加到LRU列表中lruweight越

大,在LRU列表中的位置越靠后。bestPos用于指定最佳插入位置,本例为0/

if(p.lruWeight<=app.lruWeight||i<bestPos){

mLruProcesses.add(i+1,app);

break;

}

i—;

}

if(i<0){

/*如果没有找到插入位置,即该进程的lruweight比LRU列表中

其他进程的都小,因此将其放入LRU列表的第一个位置/

mLruProcesses.add(0,app);

}

/*如果该进程正在使用Content Provider或者Service,那么

Content Provider和Service所在进程也需要调整LRU/

if(app.connections.size()>0){

……

updateLruProcessInternalLocked(……);

}

}

}

for(int j=app.conProviders.size()-1;j>=0;j—){

updateLruProcessInternalLocked(……);

}

}

if(oomAdj){//本例为true

updateOomAdjLocked();//更新OOM adj

}

}


updateLruProcessInternalLocked方法的主要工作如下:

为指定的进程计算LRU序列号和LRU weight。

根据lruweight值将指定进程信息插入mLruProcesses表示的进程LRU列表中,lruweight值越大,在列表中的位置越靠后。

如果进程使用了Content Provider或者Service,还需要更新Content Provider或者Service所在进程的lruweight及其在LRU列表中的位置。

根据参数oomAdj的值决定是否同时调整OOM adj的值。

由以上分析可知,updateLruProcessLocked方法调整的只是进程的lruweight和进程在LRU列表中的位置,并不会直接参与进程的管理,但是当其参数oomAdj的值为true时,便可以调用updateOomAdjLocked方法更新OOM adj信息,进而影响进程的管理。