6.7.3 AppDeathRecipient binderDied分析

1.binderDied函数分析

binderDied函数的代码如下:

[—>ActvityManagerService.java:AppDeathRecipient binderDied]


public void binderDied(){

//注意,该函数也是通过Binder线程调用的,所以此处要加锁

synchronized(ActivityManagerService.this){

appDiedLocked(mApp, mPid, mAppThread);

}

}


最终的处理函数是appDiedLocked,其中所传递的3个参数保存了对应死亡进程的信息。下面来看appDiedLocked的代码:

[—>ActvityManagerService.java:appDiedLocked]


final void appDiedLocked(ProcessRecord app, int pid,

IApplicationThread thread){

……

if(app.pid==pid&&app.thread!=null&&

app.thread.asBinder()==thread.asBinder()){

//判断是否需要执行内存调整操作,如果App instrumentationClass不为空,则代表该进程

//是正常启动的,否则有可能是因为进行JUnit测试而启动的,这时就无须进行后面的内存调整工作了

boolean doLowMem=app.instrumentationClass==null;

//下面这个函数非常重要

handleAppDiedLocked(app, false, true);

if(doLowMem){

boolean haveBg=false;

//如果系统中还存在oom_adj大于HIDDEN_APP_MIN_ADJ的进程

for(int i=mLruProcesses.size()-1;i>=0;i—){

ProcessRecord rec=mLruProcesses.get(i);

if(rec.thread!=null&&rec.setAdj>=

ProcessList.HIDDEN_APP_MIN_ADJ){

haveBg=true;//还有后台进程

break;

}

}//for循环结束

//如果没有后台进程

if(!haveBg){

long now=SystemClock.uptimeMillis();

for(int i=mLruProcesses.size()-1;i>=0;i—){

……//将这些进程按一定规则加到mProcessesToGc中,尽量保证

//heavy/important/visible/foreground的进程位于mProcessesToGc数组

//的前端

}//for循环结束

/*

发送GC_BACKGROUND_PROCESSES_MSG消息给mHandler,该消息的处理过程就是

调用应用进程的scheduleLowMemory或processInBackground函数。其中,

scheduleLowMemory将触发onLowMemory被调用,而processInBackground将

触发应用进程进行一次垃圾回收

读者可自行阅读该消息的处理函数performAppGcsIfAppropriateLocked

*/

scheduleAppGcsLocked();

}//if(!haveBg)判断结束

}//if(doLowMem)判断结束

}


以上代码中有一个关键函数handleAppDiedLocked,下面来看它的处理过程。

2.handleAppDiedLocked函数分析

该函数的代码如下:

[—>ActivityManagerService.java:handleAppDiedLocked]


private final void handleAppDiedLocked(ProcessRecord app,

boolean restarting, boolean allowRestart){

//在本例中,传入的参数为restarting=false, allowRestart=true

cleanUpApplicationRecordLocked(app, restarting, allowRestart,-1);

if(!restarting){

mLruProcesses.remove(app);

}

……//下面还有一部分代码处理和Activity相关的收尾工作,读者可自行阅读

}


重点看上边代码中的cleanUpApplicationRecordLocked函数,该函数的主要功能就是处理Service、ContentProvider及BroadcastReceiver相关的收尾工作。先来看Service方面的工作。

(1)cleanUpApplicationRecordLocked之处理Service

这部分的代码如下:

[—>ActivityManagerService.java:cleanUpApplicationRecordLocked]


private final void cleanUpApplicationRecordLocked(ProcessRecord app,

boolean restarting, boolean allowRestart, int index){

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

mProcessesToGc.remove(app);

//如果该Crash进程有对应打开的对话框,则关闭它们,这些对话框包括crash、anr和wait等

if(app.crashDialog!=null){

app.crashDialog.dismiss();

app.crashDialog=null;

}

……//处理anrDialog、waitDialog

//清理app的一些参数

app.crashing=false;

app.notResponding=false;

……

//处理该进程中所驻留的Service或它和别的进程中的Service建立的Connection关系

//该函数是AMS Service处理流程中很重要的一环,读者要仔细阅读

killServicesLocked(app, allowRestart);


cleanUpApplicationRecordLocked函数首先处理几个对话框(dialog),然后调用killServicesLocked函数做相关处理。作为Service流程的一部分,读者需要深入研究。

(2)cleanUpApplicationRecordLocked之处理ContentProvider

再来看cleanUpApplicationRecordLocked下一阶段的工作,这一阶段的工作主要和Content-Provider有关。

[—>ActivityManagerService.java:cleanUpApplicationRecordLocked]


boolean restart=false;

int NL=mLaunchingProviders.size();

if(!app.pubProviders.isEmpty()){

//得到该进程中发布的ContentProvider信息

Iterator<ContentProviderRecord>it=

app.pubProviders.values().iterator();

while(it.hasNext()){

ContentProviderRecord cpr=it.next();

cpr.provider=null;

cpr.proc=null;

int i=0;

if(!app.bad&&allowRestart){

for(;i<NL;i++){

/*

如果有客户端进程在等待这个已经死亡的ContentProvider,则系统会

尝试重新启动它,即设置restart变量为true

*/

if(mLaunchingProviders.get(i)==cpr){

restart=true;

break;

}

}//for循环结束

}else i=NL;

if(i>=NL){

/*

如果没有客户端进程等待这个ContentProvider,则调用下面这个函数处理它,我们

在卷I的第10章曾提过一个问题,即ContentProvider进程被杀死

后,系统该如何处理那些使用了该ContentProvider的客户端进程。例如,Music和

MediaProvider之间有交互,如果杀死了MediaProvider, Music会怎样呢?

答案是系统会杀死Music,证据就在removeDyingProviderLocked函数

中,读者可自行阅读其内部处理流程

*/

removeDyingProviderLocked(app, cpr);

NL=mLaunchingProviders.size();

}

}//while(it.hasNext())循环结束

app.pubProviders.clear();

}

//下面这个函数的功能是检查本进程中的ContentProvider是否存在于

//mLaunchingProviders中,如果存在,则表明有客户端在等待,故需考虑是否重启本进程或者

//杀死客户端(当死亡进程变成bad process的时,需要杀死客户端)

if(checkAppInLaunchingProvidersLocked(app, false))restart=true;

……


从以上的描述中可知,ContentProvider所在进程和其客户端进程实际上有着非常紧密而隐晦(之所以说其隐晦,是因为SDK中没有任何说明)的关系。在目前软件开发追求模块间尽量保持松耦合关系的大趋势下,Android中的ContentProvider和其客户端这种紧耦合的设计思路似乎不够明智。不过,这种设计是否是不得已而为之呢?读者不妨探讨一下,如果有更合适的解决方案,期待能一起分享。

(3)cleanUpApplicationRecordLocked之处理BroadcastReceiver这部分的代码如下:

[—>ActivityManagerService.java:cleanUpApplicationRecordLocked]


skipCurrentReceiverLocked(app);

//从AMS中去除接收者

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

Iterator<ReceiverList>it=app.receivers.iterator();

while(it.hasNext()){

removeReceiverLocked(it.next());

}

app.receivers.clear();

}

if(mBackupTarget!=null&&app.pid==mBackupTarget.app.pid){

//处理Backup信息

}

mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid,

app.info.uid, null).sendToTarget();

//注意该变量名为restarting,前面设置为restart.

if(restarting)return;

if(!app.persistent){

mProcessNames.remove(app.processName, app.info.uid);

if(mHeavyWeightProcess==app){

……//处理HeavyWeightProcess的情况}

}else if(!app.removed){

if(mPersistentStartingProcesses.indexOf(app)<0){

mPersistentStartingProcesses.add(app);

restart=true;

}

}

mProcessesOnHold.remove(app);

if(app==mHomeProcess)mHomeProcess=null;

if(restart){//如果需要重启,则调用startProcessLocked处理它

mProcessNames.put(app.processName, app.info.uid, app);

startProcessLocked(app,"restart",app.processName);

}else if(app.pid>0&&app.pid!=MY_PID){

synchronized(mPidsSelfLocked){

mPidsSelfLocked.remove(app.pid);

mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

}

app.setPid(0);

}

}


在这段代码中,除了处理BrodcastReceiver方面的工作外,还包括其他方面的收尾工作。最后,如果要重启该应用,则需调用startProcessLocked函数进行处理。这部分代码不再详述,读者可自行阅读。