APP被杀后Service如何重启
Binder有个讣告机制,Server死后,会向Client发送一份通知,在这里,其实就是APP死掉后,会像ActivityManagerService发送一份讣告通知,AMS后面负责清理APP的场景,并看是否需要回复Service,进一步处理后续流程,ActivityManagerService会调用handleAppDiedLocked处理死去的进程:
<!--函数1--> private final void handleAppDiedLocked(ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1); .., <!--函数2--> private final boolean cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, boolean allowRestart, int index) { ... mServices.killServicesLocked(app, allowRestart);
进一步调用ActiveServcies的killServicesLocked,killServicesLocked负责清理已死进程的Service,如果有必要,还需要根据之前启动时的设置重启Service:
final void killServicesLocked(ProcessRecord app, boolean allowRestart) { <!--先清理bindService,如果仅仅是bind先清理掉--> for (int i = app.connections.size() - 1; i >= 0; i--) { ConnectionRecord r = app.connections.valueAt(i); removeConnectionLocked(r, app, null); } ... ServiceMap smap = getServiceMap(app.userId); <!--处理未正常stop的Service--> for (int i=app.services.size()-1; i>=0; i--) { ServiceRecord sr = app.services.valueAt(i); ... <!-- 超过两次的要避免再次重启Service,但是进程还是会被唤醒 如果是系统应用则无视,仍旧重启--> if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) { bringDownServiceLocked(sr); } else if (!allowRestart || !mAm.isUserRunningLocked(sr.userId, false)) { <!--不准重启的--> bringDownServiceLocked(sr); } else { <!--准备重启--> boolean canceled = scheduleServiceRestartLocked(sr, true); <!--看看是否终止一些极端的情况--> // Should the service remain running? Note that in the // extreme case of so many attempts to deliver a command // that it failed we also will stop it here. <!-重启次数过多的话canceled=true(主要针对重发intent的)--> if (sr.startRequested && (sr.stopIfKilled || canceled)) { if (sr.pendingStarts.size() == 0) { sr.startRequested = false;... if (!sr.hasAutoCreateConnections()) { bringDownServiceLocked(sr); } } } } }
这里有些限制,比如重启两次都失败,那就不再重启Service,但是系统APP不受限制,bindService的那种先不考虑,其他的为被正常stop的都会调用scheduleServiceRestartLocked进行重启登记,不过对于像START_NOT_STICKY这种,登记会再次被取消,sr.stopIfKilled就是在这里被用到。先看下 scheduleServiceRestartLocked,它的返回值也会影响是否需要重启:
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) { boolean canceled = false; ServiceMap smap = getServiceMap(r.userId); if (smap.mServicesByName.get(r.name) != r) { ServiceRecord cur = smap.mServicesByName.get(r.name); Slog.wtf(TAG, "Attempting to schedule restart of " + r + " when found in map: " + cur); return false; } final long now = SystemClock.uptimeMillis(); if ((r.serviceInfo.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) { long minDuration = SERVICE_RESTART_DURATION; long resetTime = SERVICE_RESET_RUN_DURATION; // Any delivered but not yet finished starts should be put back // on the pending list. // 在clean的时候会处理 // 这里仅仅是要处理的需要deliveredStarts intent // remove task的被清理吗 final int N = r.deliveredStarts.size(); // deliveredStarts的耗时需要重新计算 if (N > 0) { for (int i=N-1; i>=0; i--) { ServiceRecord.StartItem si = r.deliveredStarts.get(i); si.removeUriPermissionsLocked(); if (si.intent == null) { // We'll generate this again if needed. } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { // 重启的时候 // 重启的时候,deliveredStarts被pendingStarts替换掉了 // 也就说,这个时候由死转到活 r.pendingStarts.add(0, si); // 当前时间距离上次的deliveredTime,一般耗时比较长 long dur = SystemClock.uptimeMillis() - si.deliveredTime; dur *= 2; if (minDuration < dur) minDuration = dur; if (resetTime < dur) resetTime = dur; } else { canceled = true; } } r.deliveredStarts.clear(); } r.totalRestartCount++; // r.restartDelay第一次重启 if (r.restartDelay == 0) { r.restartCount++; r.restartDelay = minDuration; } else { // If it has been a "reasonably long time" since the service // was started, then reset our restart duration back to // the beginning, so we don't infinitely increase the duration // on a service that just occasionally gets killed (which is // a normal case, due to process being killed to reclaim memory). <!--如果被杀后,运行时间较短又被杀了,那么增加重启延时,否则重置为minDuration,(比如内存不足,经常重杀,那么不能无限重启,增大延时)--> if (now > (r.restartTime+resetTime)) { r.restartCount = 1; r.restartDelay = minDuration; } else { r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR; if (r.restartDelay < minDuration) { r.restartDelay = minDuration; } } } <!--计算下次重启的时间--> r.nextRestartTime = now + r.restartDelay; <!--两个Service启动至少间隔10秒,这里的意义其实不是很大,主要是为了Service启动失败的情况,如果启动成功,其他要启动的Service会被一并直接重新唤起,--> boolean repeat; do { repeat = false; for (int i=mRestartingServices.size()-1; i>=0; i--) { ServiceRecord r2 = mRestartingServices.get(i); if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN) && r.nextRestartTime < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) { r.nextRestartTime = r2.nextRestartTime + SERVICE_MIN_RESTART_TIME_BETWEEN; r.restartDelay = r.nextRestartTime - now; repeat = true; break; } } } while (repeat); } else { <!--系统服务,即可重启--> // Persistent processes are immediately restarted, so there is no // reason to hold of on restarting their services. r.totalRestartCount++; r.restartCount = 0; r.restartDelay = 0; r.nextRestartTime = now; } if (!mRestartingServices.contains(r)) { <!--添加一个Service--> mRestartingServices.add(r); ... } mAm.mHandler.removeCallbacks(r.restarter); // postAtTime mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); <!--校准一下真实的nextRestartTime,dump时候可以看到--> r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; ... return canceled; }
scheduleServiceRestartLocked主要作用是计算重启延时,并发送重启的消息到Handler对应的MessageQueue,对于需要发送Intent的Service,他们之前的Intent被暂存在delivered, 在恢复阶段,原来的deliveredStarts会被清理,转换到pendingStart列表中,后面重新启动时候会根据pendingStart重发Intent给Service,调用其onStartCommand。不过对于这种Service,其启动恢复的时间跟其运行时间有关系,距离startService时间越长,其需要恢复的延时时间就越多,后面会单独解释。
另外,如果Service重启的时间间隔过短,则说明被杀的太迅速,很可能是系统资源不足,这个时候就逐步拉大重启时间。其次,什么时候Cancle重新启动呢?只有deliveredStarts非空(START_DELIVER_INTENT),并且回调onStartCommand失败的次数>=2,或者成功的次数>=6的时候,比如:对于START_DELIVER_INTENT,如果被杀超过6次,AMS会清理该Service,不会再重启了。另外如果重启的Service可有很多个,为了避免重启时间太接近,多个Service预置的重启间隔最少是10S,不过,并不是说Service真的需要间隔10s才能重启,而是说,如果前一个Service重启失败或者太慢,要至少10s后才重启下一个,如果第一个Service就重启成功,同时进程也启动成功,那么所有的Service都会被立刻唤起,而不需要等到真正的10秒延时间隔。
可以看到,虽然pendingStart中Service重启的间隔是至少相隔10秒,但是一个Service启动成功后,所有的Service都被唤起了,虽然还没有到之前预置的启动时机。这是为什么?因为,如果在进程未启动的时候启动Service,那么需要先启动进程,之后attach Application ,在attatch的时候,除了启动自己Service,还要将其余等待唤醒的Service一并唤起,源码如下:
boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException { boolean didSomething = false; ... // 只要是一个起来了,就立刻重新启动所有Service,进程已经活了,就无须等待 if (mRestartingServices.size() > 0) { ServiceRecord sr = null; for (int i=0; i<mRestartingServices.size(); i++) { sr = mRestartingServices.get(i); if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid || !processName.equals(sr.processName))) { continue; } <!--清除旧的,--> mAm.mHandler.removeCallbacks(sr.restarter); <!--添加新的--> mAm.mHandler.post(sr.restarter); } } return didSomething; }
可以看到,attachApplicationLocked的时候,会将之前旧的含有10秒延迟间隔的restarter清理掉,并重新添加无需延时的重启命令,这样那些需要重启的Service就不用等到之前设定的延时就可以重新启动了。还有什么情况,需要考虑呢,看下面的:
<!-重启次数过多的话canceled=true(主要针对重发intent的)--> if (sr.startRequested && (sr.stopIfKilled || canceled)) { if (sr.pendingStarts.size() == 0) { sr.startRequested = false;... if (!sr.hasAutoCreateConnections()) { bringDownServiceLocked(sr); } } } }
- 对于START_STICKY,scheduleServiceRestartLocked返回值一定是false,delay的时间是1S,并且由于其stopIfKilled是false,所以一定会被快速重启,不会走bringDownServiceLocked流程
- 对于STAR_NO_STICKY,scheduleServiceRestartLocked返回值是flase,但是stopIfKilled是true,另外其pendingStarts列表为空,如果没有被其他存活的Activity绑定,那么需要走bringDownServiceLocked流程,也就是说,不会被重启。
处理完上述逻辑后,ServiceRestarter就会被插入到MessegeQueue等待执行,之后调用performServiceRestartLocked-> bringUpServiceLocked-> realStartServiceLocked进一步处理Service的重启。
private class ServiceRestarter implements Runnable { private ServiceRecord mService; void setService(ServiceRecord service) { mService = service; } public void run() { synchronized(mAm) { performServiceRestartLocked(mService); } } } final void performServiceRestartLocked(ServiceRecord r) { // 如果被置空了,也是不用重启 if (!mRestartingServices.contains(r)) { return; } try { bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true); } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. }
}
之前第一次启动的时候看过了,这里再来看复习一下,主要看不同的点,其实主要是针对START_STICKY的处理:
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... <!--恢复:这里应该主要是给start_sticky用的,恢复的时候触发调用onStartCommand--> if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), null, null)); } <!--处理onstartComand--> sendServiceArgsLocked(r, execInFg, true); ...
对于START_STICKY需要重启,之前说过了,但是怎么标记需要重新调用onStartCommand呢?上面的realStartServiceLocked会主动添加一个ServiceRecord.StartItem到pendingStarts,因为这个时候,对于START_STICKY满足如下条件。
r.startRequested && r.callStart && r.pendingStarts.size() == 0
不过,这个Item没有Intent,也就说,回调onStartCommand的时候,没有Intent传递给APP端,接下来的sendServiceArgsLocked跟之前的逻辑没太大区别,不再分析,下面再说下为什么START_REDELIVER_INTENT比较耗时。
被杀重启时候,为什么 START_REDELIVER_INTENT通常比START_STICK延时更多
之前说过,在onStartCommand返回值是START_REDELIVER_INTENT的时候,其重启恢复的延时时间跟Service的启动时间有关系。具体算法是:从start到now的时间*2,距离启动时间越长,restart的延时越多。
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) { boolean canceled = false; ... final int N = r.deliveredStarts.size(); // deliveredStarts的耗时需要重新计算 if (N > 0) { ... if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { r.pendingStarts.add(0, si); // 当前时间距离上次的deliveredTime,一般耗时比较长 long dur = SystemClock.uptimeMillis() - si.deliveredTime; dur *= 2; if (minDuration < dur) minDuration = dur; if (resetTime < dur) resetTime = dur; }
如果设置了START_REDELIVER_INTENT,这里的deliveredStarts就一定非空,因为它持有startService的Intent列表,在这种情况下,重启延时是需要重新计算的,一般是是2*(距离上次sendServiceArgsLocked的时间(比如由startService触发))
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
比如距离上次startService的是3分,那么它就在6分钟后重启,如果是半小时,那么它就在一小时后启动,
而对于START_STICK,它的启动延时基本上是系统设置的Service最小重启延时单位,一般是一秒:
static final int SERVICE_RESTART_DURATION = 1*1000;
所以如果你需要快速重启Service,那么就使用START_STICK,不过START_STICK不会传递之前Intent信息,上面分析都是假设进程被意外杀死,那么用户主动从最近的任务列表删除的时候,也会重启,有什么不同吗?
从最近任务列表删除,如何处理Service的重启
左滑删除有时候会导致进程被杀死,这个时候,未被stop的Service也是可能需要重新启动的,这个时候跟之前的有什么不同吗?在这种情况下Service的onTaskRemoved会被回调。
@Override public void onTaskRemoved(Intent rootIntent) { super.onTaskRemoved(rootIntent); }
左滑删除TASK会调用AMS的cleanUpRemovedTaskLocked,这个函数会先处理Service的,并回调其onTaskRemoved,之后杀进程,杀进程之后的逻辑同样走binder讣告机制,跟之前的恢复没什么区别,这里主要看看onTaskRemoved,如果不需要重启,可以在这里做下处理:
private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) { ... // Find any running services associated with this app and stop if needed. <!--先处理Service,如果有必要清理Service--> mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent())); if (!killProcess) { return; } <!--找到跟改Task相关的进程,并决定是否需要kill--> ArrayList<ProcessRecord> procsToKill = new ArrayList<>(); ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap(); for (int i = 0; i < pmap.size(); i++) { SparseArray<ProcessRecord> uids = pmap.valueAt(i); for (int j = 0; j < uids.size(); j++) { ProcessRecord proc = uids.valueAt(j); ...<!--满足条件的等待被杀 (不是Home,)--> procsToKill.add(proc); } } // 如果可以即可杀,就立刻杀,否则等待下一次评估oomadj的时候杀,不过,总归是要杀的 for (int i = 0; i < procsToKill.size(); i++) { ProcessRecord pr = procsToKill.get(i); if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE && pr.curReceiver == null) { pr.kill("remove task", true); } else { pr.waitingToKill = "remove task"; } } }
ActiveServices的cleanUpRemovedTaskLocked
void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) { ArrayList<ServiceRecord> services = new ArrayList<>(); ArrayMap<ComponentName, ServiceRecord> alls = getServices(tr.userId); for (int i = alls.size() - 1; i >= 0; i--) { ServiceRecord sr = alls.valueAt(i); if (sr.packageName.equals(component.getPackageName())) { services.add(sr); } } // Take care of any running services associated with the app. for (int i = services.size() - 1; i >= 0; i--) { ServiceRecord sr = services.get(i); // 如果是通过startRequested启动 if (sr.startRequested) { if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { stopServiceLocked(sr); } else { <!--作为remove的一部分,这里pendingStarts的add主要是为了回到onStartCommand,而且这个时候,进程还没死呢,否则通知个屁啊--> sr.pendingStarts.add(new ServiceRecord.StartItem(sr, taskremover =true, sr.makeNextStartId(), baseIntent, null)); if (sr.app != null && sr.app.thread != null) { try { sendServiceArgsLocked(sr, true, false); } ... }
其实从最近任务列表删除最近任务的时候,处理很简单,如果Service设置了ServiceInfo.FLAG_STOP_WITH_TASK,那么左滑删除后,Service不用重启,也不会处理 onTaskRemoved,直接干掉,否则,是需要往pendingStarts填充ServiceRecord.StartItem,这样在sendServiceArgsLocked才能发送onTaskRemoved请求,为了跟启动onStartCommand分开,ServiceRecord.StartItem的taskremover被设置成true,这样在回调ActiviyThread的handleServiceArgs就会走onTaskRemoved分支如下:
private void handleServiceArgs(ServiceArgsData data) { Service s = mServices.get(data.token); if (s != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); data.args.prepareToEnterProcess(); } int res; // 如果没有 taskRemoved,如果taskRemoved 则回调onTaskRemoved if (!data.taskRemoved) { res = s.onStartCommand(data.args, data.flags, data.startId); } else { s.onTaskRemoved(data.args); res = Service.START_TASK_REMOVED_COMPLETE; } ....
因此,从最近任务列表删除,可以看做是仅仅多了个一个onTaskRemoved在这个会调中,用户可以自己处理一些事情,比如中断一些Service处理的事情,保存现场等。
Android O 之后的变更
似乎Android O之后START_STICK的Service会重新唤起进程,但是onStartCommand不会被调用,这样似乎更合情理,不是startService启动的Service不会调用onStartCommand。
总结
- 通过startService启动,但是却没有通过stopService结束的Service并不一定触发重新启动,需要设置相应的onStartCommand返回值,比如START_REDELIVER_INTENT、比START_STICK
- START_REDELIVER_INTENT并不是重发最后一个Intent,看源码是所有Intent
- START_REDELIVER_INTENT同START_STICK重启的延时不一样,START_STICK一般固定1s,而START_REDELIVER_INTENT较长,基本是距离startService的2倍。
- 可以用来做保活,但是不推荐,而且国内也不怎么好用(MIUI、华为等都对AMS做了定制,限制较多)