第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-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信息,进而影响进程的管理。