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函数进行处理。这部分代码不再详述,读者可自行阅读。