本文檔適合對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重啟后是否執(zhí)行onStartCommand,START_STICKY_COMPATIBILITY時會賦值為false
int crashCount; // number of times proc has crashed with service running
int restartCount; // 連續(xù)執(zhí)行的重啟次數(shù)
long restartDelay; // 下一次重啟的delay,scheduleServiceRestartLocked 賦值,啟動/停止/綁定時會重置為0
long restartTime; //上次調(diào)用realStartServiceLocked的時間
long nextRestartTime; // 當前時間+restartDelay
int totalRestartCount; // 不得不重啟的次數(shù)
final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>(); // 在sendServiceArgsLocked中會添加StartItem到這個列表中,執(zhí)行完serviceDoneExecutingLocked/或stop后會從該列表中移除,返回值為START_REDELIVER_INTENT時不會從列表中移除.
final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>(); // startServiceInnerLocked時添加進列表,sendServiceArgsLocked 中移除;
StartItem
long deliveredTime; // 在sendServiceArgsLocked中賦值,START_REDELIVER_INTENT返回值時,重啟時會根據(jù)這個計算延時時間
int deliveryCount; // 調(diào)用一次 sendServiceArgsLocked增加一次計數(shù),如果onStartCommand執(zhí)行失敗的次數(shù)超過兩次,后面就不會為這個intent重發(fā),主要針對START_REDELIVER_INTENT
int doneExecutingCount; //如果返回START_REDELIVER_INTENT時,則該引用計數(shù)+1
Service重啟時序圖
常見問題問答
什么情況下進程被殺后Service能重啟?
start方式啟動時onStartCommand返回值是START_STICKY_COMPATIBILITY/START_STICKY/START_REDELIVER_INTENT之一
Servive有flag是BIND_AUTO_CREATE的連接
進程被殺時Service還沒執(zhí)行完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<ConnectionRecord> cr = connections.valueAt(conni);
for (int i=0; i<cr.size(); i++) {
if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
return true;
}
}
}
return false;
}
/** @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, // 重啟后只執(zhí)行onCreate
START_STICKY, // 重啟后執(zhí)行onCreate、onStartCommand,但intent為null
START_NOT_STICKY, // 不重啟
START_REDELIVER_INTENT, //重啟后執(zhí)行onCreate、onStartCommand、intent不為null、onStartCommand可能執(zhí)行多次,重啟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.
// 表明默認重啟后需要回調(diào)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被殺死后會重啟(執(zhí)行了onCreate方法),但是onStartCommand方法沒有被執(zhí)行。
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重啟后不執(zhí)行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列表會為空,導致不會遍歷執(zhí)行到onStartCommand方法
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null, 0));
}
// 從這個方法中會觸發(fā)app端回調(diào)Service的onStartCommand方法
sendServiceArgsLocked(r, execInFg, true);
.......
}
START_STICKY
進程被殺后Service會被重啟,執(zhí)行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被殺死后會重啟(執(zhí)行了onCreate方法),并且onStartCommand方法會執(zhí)行。從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: {鎖
// 這個返回值,執(zhí)行完Service的onStartCommand方法后,不會從deliverStarts列表中移除當前的StartItem,Service重啟時會遍歷deliverStarts列表,并將該列表中item添加到pendingStarts
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
if (si != null) {
// 賦值為0,非0的話則表示app端的onStartCommand方法未正常執(zhí)行
si.deliveryCount = 0;
// 標明該StartItem的執(zhí)行成功的次數(shù)
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方法還沒執(zhí)行完。
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次才會繼續(xù)重啟
} 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發(fā)生過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的系數(shù),增大重啟延時
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;
}
如何規(guī)避非預期的Service重啟
背景
一鍵清理時會先執(zhí)行removeTask操作,未添加FLAG_STOP_WITH_TASK flag的Service會觸發(fā)執(zhí)行一次onStartCommand方法,且在sendServiceArgsLocked中執(zhí)行完后將新創(chuàng)建的StartItem從pendingStarts列表移動到deliveredStarts列表。這時候再執(zhí)行殺進程會導致app端未能及時告知system server端執(zhí)行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<ServiceRecord> services = new ArrayList<>();
ArrayMap<ComponentName, ServiceRecord> 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端觸發(fā)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是異步執(zhí)行的不會有這個問題(即殺進程會在cleanUpServices之前,沒有Service會被遍歷到),當時讓app在功能清單文件中配置android.R.attr#stopWithTask。
Service重啟三千問
1. 什么情況下進程被殺后Service能重啟?Start方式和bind方式的區(qū)別?
1. onStartCommand返回值START_STICKY_COMPATIBILITY/START_STICKY和START_REDELIVER_INTENT實現(xiàn)Service重啟 有什么區(qū)別?
1. onStartCommand不同返回值有什么不同的效果?
1. 什么返回值Service重啟后不執(zhí)行onStartCommand?
1. 什么返回值Service重啟不用重新發(fā)送intent?
1. 哪些情況下Service會被更快的重啟?
1. START_REDELIVER_INTENT 重啟時app的Service會執(zhí)行幾次onStartCommand?
1. start方式啟動的Service的stopIfKilled為true時,Service一定不可以重啟么?
1. deliveredStarts 什么時候添加?什么時候移除?
1. pendingStarts 什么時候添加?什么時候移除?
1. Service重啟的restartDelay的計算邏輯?
1. 什么情況下START_REDELIVER_INTENT返回值不再執(zhí)行重啟?