本文档适合对Service的启动和停止的fwk流程有一定了解的查看。
基础知识
ServiceRecord
boolean fgRequired; // 是否请求的是前台Service,完成后设置为false。
boolean startRequested; // 是否是start方式启动,在start权限校验后才会赋值为true,Service stop后会赋值为false
boolean isForeground; // 当前Service是否是前台Service,startForeground后赋值为true,stopForeground赋值为false
boolean stopIfKilled; // 跟onStartCommand返回值有关,START_STICKY_COMPATIBILITY/START_STICKY等为false;表示Service被杀后是否停止重启;
boolean callStart; // 决定Service重启后是否执行onStartCommand,START_STICKY_COMPATIBILITY时会赋值为false
int crashCount; // number of times proc has crashed with service running
int restartCount; // 连续执行的重启次数
long restartDelay; // 下一次重启的delay,scheduleServiceRestartLocked 赋值,启动/停止/绑定时会重置为0
long restartTime; //上次调用realStartServiceLocked的时间
long nextRestartTime; // 当前时间+restartDelay
int totalRestartCount; // 不得不重启的次数
final ArrayList deliveredStarts = new ArrayList(); // 在sendServiceArgsLocked中会添加StartItem到这个列表中,执行完serviceDoneExecutingLocked/或stop后会从该列表中移除,返回值为START_REDELIVER_INTENT时不会从列表中移除.
final ArrayList pendingStarts = new ArrayList(); // startServiceInnerLocked时添加进列表,sendServiceArgsLocked 中移除;
StartItem
long deliveredTime; // 在sendServiceArgsLocked中赋值,START_REDELIVER_INTENT返回值时,重启时会根据这个计算延时时间
int deliveryCount; // 调用一次 sendServiceArgsLocked增加一次计数,如果onStartCommand执行失败的次数超过两次,后面就不会为这个intent重发,主要针对START_REDELIVER_INTENT
int doneExecutingCount; //如果返回START_REDELIVER_INTENT时,则该引用计数+1
Service重启时序图
常见问题问答
什么情况下进程被杀后Service能重启?
start方式启动时onStartCommand返回值是START_STICKY_COMPATIBILITY/START_STICKY/START_REDELIVER_INTENT之一
Servive有flag是BIND_AUTO_CREATE的连接
进程被杀时Service还没执行完onStartCommand方法,导致deliverStarts中的额StartItem没有被移除
boolean canStopIfKilled(boolean isStartCanceled) {
// onStartCommand返回值是START_STICKY_COMPATIBILITY/START_STICKY时,stopIfKilled为false;
// onStartCommand返回值是START_REDELIVER_INTENT时,这里的pendingStarts不为空
return startRequested && (stopIfKilled || isStartCanceled) && pendingStarts.isEmpty();
}
// Servive有flag是BIND_AUTO_CREATE的连接
public boolean hasAutoCreateConnections() {
for (int conni=connections.size()-1; conni>=0; conni--) {
ArrayList cr = connections.valueAt(conni);
for (int i=0; i
/** @return {@code true} if the restart is scheduled. */
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
.......
if (allowCancel) {
// start方式启动,返回值是START_STICKY_COMPATIBILITY/START_STICKY/START_REDELIVER_INTENT之一时,shouldStop为false
final boolean shouldStop = r.canStopIfKilled(canceled);
if (shouldStop && !r.hasAutoCreateConnections()) {
// Nothing to restart.
return false;
}
reason = (r.startRequested && !shouldStop) ? "start-requested" : "connection";
} else {
reason = "always";
}
.......
return true;
}
onStartCommand不同返回值有什么不同的效果?
@IntDef(flag = false, prefix = { "START_" }, value = {
START_STICKY_COMPATIBILITY, // 重启后只执行onCreate
START_STICKY, // 重启后执行onCreate、onStartCommand,但intent为null
START_NOT_STICKY, // 不重启
START_REDELIVER_INTENT, //重启后执行onCreate、onStartCommand、intent不为null、onStartCommand可能执行多次,重启delay时间较长。
})
START_NOT_STICKY
进程被杀后Service不会被重启 : 因为这里stopIfKilled赋值为true。
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
boolean enqueueOomAdj) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
// 表明默认重启后需要回调onStartCommand方法
r.callStart = true;
switch (res) {
......
case Service.START_NOT_STICKY: {
// 查找并将当前StartItem从deliveredStarts列表中移除
r.findDeliveredStart(startId, false, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
}
break;
}
......
}.......
}
START_STICKY_COMPATIBILITY
Service被杀死后会重启(执行了onCreate方法),但是onStartCommand方法没有被执行。
system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 1000ms for start-requested
system_process I/ActivityManager: Start proc 7675:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication
com.example.myapplication D/MyService: onCreate
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
boolean enqueueOomAdj) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
// 查找并将当前StartItem从deliveredStarts列表中移除
r.findDeliveredStart(startId, false, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
.......
}
// 这里callStart赋值为false, 决定Service重启后不执行onStartCommand方法。
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
}.......
}
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj) throws RemoteException {
......
// callStart为false,则StartItem不会添加到pendingStarts列表;sendServiceArgsLocked方法中传递到app端的ServiceStartArgs列表会为空,导致不会遍历执行到onStartCommand方法
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}
// 从这个方法中会触发app端回调Service的onStartCommand方法
sendServiceArgsLocked(r, execInFg, true);
.......
}
START_STICKY
进程被杀后Service会被重启,执行onCreate和onStartCommand,重启后的Intent为null。
具体代码同START_STICKY_COMPATIBILITY。
com.example.myapplication D/MyService: onCreate
com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1
com.example.myapplication D/MyService: startResult= 1
system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 1000ms for start-requested
system_process I/ActivityManager: Start proc 4522:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication
com.example.myapplication D/MyService: onCreate
com.example.myapplication D/MyService: onStartCommand intent=null startId=2 // startID不一致,代表不是同一个实例,intent为null
com.example.myapplication D/MyService: startResult= 1
START_REDELIVER_INTENT
Service被杀死后会重启(执行了onCreate方法),并且onStartCommand方法会执行。从startId可以看出两个Service对象是同一个,并且onStartCommand方法中的intent和前一个intent值是一致的。
com.example.myapplication D/MyService: onCreate
com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1
com.example.myapplication D/MyService: startResult= 3
system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService in 17938ms for start-requested
system_process I/ActivityManager: Start proc 6307:com.example.myapplication/u0a270 for service {com.example.myapplication/com.example.myapplication.MyService} caller=com.example.myapplication
com.example.myapplication D/MyService: onCreate
com.example.myapplication D/MyService: onStartCommand intent=Intent { cmp=com.example.myapplication/.MyService (has extras) } startId=1
com.example.myapplication D/MyService: startResult= 3
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
boolean enqueueOomAdj) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
......
case Service.START_REDELIVER_INTENT: {锁
// 这个返回值,执行完Service的onStartCommand方法后,不会从deliverStarts列表中移除当前的StartItem,Service重启时会遍历deliverStarts列表,并将该列表中item添加到pendingStarts
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
if (si != null) {
// 赋值为0,非0的话则表示app端的onStartCommand方法未正常执行
si.deliveryCount = 0;
// 标明该StartItem的执行成功的次数
si.doneExecutingCount++;
r.stopIfKilled = true;
}
break;
}
.......
}.......
}
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
.......
// 非persistent app才会判断重不重启,persistent app直接重启
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
......
final int N = r.deliveredStarts.size();
// 针对START_REDELIVER_INTENT返回值,deliveredStarts不为空; 也有可能是进程被杀后onStartCommand方法还没执行完。
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.
// 同一个StartItem重启失败<3次或成功<6次才会继续重启
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT //3
&& si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { //6
// 将列表中item添加到pendingStarts
r.pendingStarts.add(0, si);
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
dur *= 2;
// minDuration和resetTime赋值,一般是当前时间到deliver的时间间隔(下面称作devliver Duration),所以重启delay时间会很长,超过1s。
if (minDuration < dur) minDuration = dur;
if (resetTime < dur) resetTime = dur;
} else {
// 否则直接取消重启
Slog.w(TAG, "Canceling start item " + si.intent + " in service "
+ r.shortInstanceName);
canceled = true;
}
}
// 清空Service的deliveredStarts的列表
r.deliveredStarts.clear();
}
Bind情况下如何重启?
Service有BIND_AUTO_CREATE的连接
com.example.testapplication D/MainActivity: flags= 1
com.example.myapplication D/MyService2: onCreate
com.example.myapplication D/MyService2: onBind
com.example.testapplication D/MainActivity: onServiceConnected
com.example.testapplication D/MainActivity: onServiceDisconnected
ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService2 in 10043ms for connection
ActivityManager: Start proc 10102:com.example.myapplication:remote/u0a253 for service {com.example.myapplication/com.example.myapplication.MyService2} caller=com.example.testapplication
com.example.myapplication D/MyService2: onCreate
com.example.myapplication D/MyService2: onBind
com.example.testapplication D/MainActivity: onServiceConnected
先bindService再startService
com.example.myapplication D/MyService2: onCreate
com.example.myapplication D/MyService2: onBind
com.example.myapplication D/MyService2: onStartCommand
com.example.testapplication D/MainActivity: onServiceConnected
com.example.testapplication D/MainActivity: onServiceDisconnected
system_process W/ActivityManager: Scheduling restart of crashed service com.example.myapplication/.MyService2 in 1000ms for start-requested
system_process I/ActivityManager: Start proc 13953:com.example.myapplication:remote/u0a253 for service {com.example.myapplication/com.example.myapplication.MyService2} caller=com.example.myapplication
com.example.myapplication D/MyService2: onCreate
com.example.myapplication D/MyService2: onBind
com.example.testapplication D/MainActivity: onServiceConnected
com.example.myapplication D/MyService2: onStartCommand
重启时间如何计算?
首次重启
1. 如果是START_REDELIVER_INTENT返回值,重启delay时间一般为 (当前时间 - 上次deliver时间)*2,这个时间一般较长。
1. 其它重启时间为1s。
app发生过crash
重启delay时间为1800s*(crashCount -1)
非首次重启
1. START_REDELIVER_INTENT返回值, 当前时间距离上次重启 > 60s(或deliverDuration) ,则重启delay一般为deliverDuration;否则重启delay*60s。
1. 其它,当前时间距离上次重启 > 60s,则重启delay为1s,否则delay * 60s。
最后需要保证两个重启的Service时间间隔不得<10s
/** @return {@code true} if the restart is scheduled. */
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
......
final long now = SystemClock.uptimeMillis();
final String reason;
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
long minDuration = mAm.mConstants.SERVICE_RESTART_DURATION; //1s
long resetTime = mAm.mConstants.SERVICE_RESET_RUN_DURATION; //60s
boolean canceled = false;
........
// 第一次重启
if (r.restartDelay == 0) {
r.restartCount++;
r.restartDelay = minDuration;
// crash重启时间为1800s * n
} else if (r.crashCount > 1) {
r.restartDelay = mAm.mConstants.BOUND_SERVICE_CRASH_RESTART_DURATION
* (r.crashCount - 1);
} else {
// 如果这次重启距离上次重启>60s(或者deliverDuration),重置delay为minDuration(非Deliver一般为1s)
if (now > (r.restartTime+resetTime)) {
r.restartCount = 1;
r.restartDelay = minDuration;
} else {
// 如果重启后很快又被杀,则delay需要* 60s的系数,增大重启延时
r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;
if (r.restartDelay < minDuration) {
r.restartDelay = minDuration;
}
}
}
if (isServiceRestartBackoffEnabledLocked(r.packageName)) {
// 计算下次重启时间
r.nextRestartTime = now + r.restartDelay;
// Make sure that we don't end up restarting a bunch of services
// all at the same time.
boolean repeat;
final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN; //10s
do {
// 避免一次重启太多的Service,至少保证10s的时间间隔
repeat = false;
for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
final ServiceRecord r2 = mRestartingServices.get(i);
if (r2 != r
&& r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween)
&& r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) {
r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
r.restartDelay = r.nextRestartTime - now;
repeat = true;
break;
}
}
} while (repeat);
} else {
// It's been forced to ignore the restart backoff, fix the delay here.
r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION;
r.nextRestartTime = now + r.restartDelay;
}
} else {
// Persistent processes are immediately restarted, so there is no
// reason to hold of on restarting their services.
// persistent app立刻被拉起
r.totalRestartCount++;
r.restartCount = 0;
r.restartDelay = 0;
r.nextRestartTime = now;
reason = "persistent";
}
if (!mRestartingServices.contains(r)) {
r.createdFromFg = false;
mRestartingServices.add(r);
synchronized (mAm.mProcessStats.mLock) {
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
}
cancelForegroundNotificationLocked(r);
// 开始shedule重启
performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis());
return true;
}
如何规避非预期的Service重启
背景
一键清理时会先执行removeTask操作,未添加FLAG_STOP_WITH_TASK flag的Service会触发执行一次onStartCommand方法,且在sendServiceArgsLocked中执行完后将新创建的StartItem从pendingStarts列表移动到deliveredStarts列表。这时候再执行杀进程会导致app端未能及时告知system server端执行serviceDoneExecutingLocked方法去移除deliveredStarts中的StartItem,导致进程被杀后还会立刻通过Service拉起来。
10-12 15:52:22.108 2489 3967 I ProcessManager: OneKeyClean: kill com.miui.huanji:backup Adj=100 State=4
10-12 15:52:22.122 2489 3967 I ProcessManager: OneKeyClean: kill com.miui.huanji Adj=50 State=4
10-12 15:52:22.144 2489 7247 W ActivityManager: Scheduling restart of crashed service com.miui.huanji/.backup.BackupService in 1000ms for connection
10-12 15:52:22.292 2489 7443 W ActivityManager: Scheduling restart of crashed service com.miui.huanji/.scanner.ScannerService in 50853ms for start-requested
代码逻辑
void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
ArrayList services = new ArrayList<>();
ArrayMap alls = getServicesLocked(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.
boolean needOomAdj = false;
for (int i = services.size() - 1; i >= 0; i--) {
ServiceRecord sr = services.get(i);
if (sr.startRequested) {
if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) {
Slog.i(TAG, "Stopping service " + sr.shortInstanceName + ": remove task");
needOomAdj = true;
stopServiceLocked(sr, true);
} else {
// 让app端触发onTaskRemoved方法
sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true,
sr.getLastStartId(), baseIntent, null, 0));
if (sr.app != null && sr.app.getThread() != null) {
// We always run in the foreground, since this is called as
// part of the "remove task" UI operation.
try {
sendServiceArgsLocked(sr, true, false);
} catch (TransactionTooLargeException e) {
// Ignore, keep going.
}
}
}
}
}
if (needOomAdj) {
mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
解决方案
后期cleanUpService是异步执行的不会有这个问题(即杀进程会在cleanUpServices之前,没有Service会被遍历到),当时让app在功能清单文件中配置android.R.attr#stopWithTask。
Service重启三千问
1. 什么情况下进程被杀后Service能重启?Start方式和bind方式的区别?
1. onStartCommand返回值START_STICKY_COMPATIBILITY/START_STICKY和START_REDELIVER_INTENT实现Service重启 有什么区别?
1. onStartCommand不同返回值有什么不同的效果?
1. 什么返回值Service重启后不执行onStartCommand?
1. 什么返回值Service重启不用重新发送intent?
1. 哪些情况下Service会被更快的重启?
1. START_REDELIVER_INTENT 重启时app的Service会执行几次onStartCommand?
1. start方式启动的Service的stopIfKilled为true时,Service一定不可以重启么?
1. deliveredStarts 什么时候添加?什么时候移除?
1. pendingStarts 什么时候添加?什么时候移除?
1. Service重启的restartDelay的计算逻辑?
1. 什么情况下START_REDELIVER_INTENT返回值不再执行重启?
参考文档:https://www.jianshu.com/p/7a659ca38510