Androidのサービスセルフスタートプロセス
サービスが異常に破棄された場合、デフォルトでは自動的に再起動されますが、サービスの自動再起動はどのように実現されますか?サービスの自動再起動の流れを整理します. サービスが起動してもバインドに成功しても、
サービスが起動またはバインドに成功すると、現在のサービスに
上記のコードから、再起動を試みる具体的な実装は
最後に
Androidのサービス(Service)(二)サービスの自動再起動問題Androidサービス再起動リカバリ(サービスプロセス再起動)原理解析
ActiveServices
のserviceDoneExecutingLocked
メソッドが呼び出されます.このメソッドの実装を見てみましょう. void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
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.
// Service startCommand
r.callStart = true;
switch (res) {
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
// Don't stop if killed.
// kill , Service
r.stopIfKilled = false;
break;
}
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
// kill , Service
r.stopIfKilled = true;
}
break;
}
case Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
// Don't stop if killed.
// kill , 。 deliveredStarts
r.C= true;
}
break;
}
case Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
r.findDeliveredStart(startId, true);
break;
}
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
}
if (res == Service.START_STICKY_COMPATIBILITY) {
// startCommand START_STICKY_COMPATIBILITY startCommand
r.callStart = false;
}
} else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
...
}
...
} else {
...
}
}
サービスが起動またはバインドに成功すると、現在のサービスに
stopIfKilled
というフラグビットが付与され、このフラグビットは、サービスが異常killによってドロップされた後に再起動する必要があるかどうかを決定する. Serviceは予期せぬkillによってActiveServices
のkillServicesLocked
メソッドを呼び出し、このメソッドの実装を見てみましょう. final void killServicesLocked(ProcessRecord app, boolean allowRestart) {
...
ServiceMap smap = getServiceMapLocked(app.userId);
// Now do remaining service cleanup.
for (int i=app.services.size()-1; i>=0; i--) {
// Service
ServiceRecord sr = app.services.valueAt(i);
...
// Any services running in the application may need to be placed
// back in the pending list.
// Service
if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
...
//
bringDownServiceLocked(sr);
} else if (!allowRestart//
|| !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) {
//
bringDownServiceLocked(sr);
} else {
//
boolean canceled = scheduleServiceRestartLocked(sr, true);
// , 。
if (sr.startRequested && (sr.stopIfKilled || canceled)) {
if (sr.pendingStarts.size() == 0) {
sr.startRequested = false;
if (sr.tracker != null) {
sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
if (!sr.hasAutoCreateConnections()) {
// Whoops, no reason to restart!
// , !
bringDownServiceLocked(sr);
}
}
}
}
}
//
...
}
上記のコードから、再起動を試みる具体的な実装は
scheduleServiceRestartLocked
という方法で、その実装を見てみましょう. private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
boolean canceled = false;
...
final long now = SystemClock.uptimeMillis();
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
long minDuration = mAm.mConstants.SERVICE_RESTART_DURATION;
long resetTime = mAm.mConstants.SERVICE_RESET_RUN_DURATION;
// Any delivered but not yet finished starts should be put back
// on the pending list.
final int N = r.deliveredStarts.size();
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 , Intent
// , START_REDELIVER_INTENT
r.pendingStarts.add(0, si);
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
// , kill ,
dur *= 2;
if (minDuration < dur) minDuration = dur;
if (resetTime < dur) resetTime = dur;
} else {
Slog.w(TAG, "Canceling start item " + si.intent + " in service "
+ r.name);
canceled = true;
}
}
r.deliveredStarts.clear();
}
// 1
r.totalRestartCount++;
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).
if (now > (r.restartTime+resetTime)) {
r.restartCount = 1;
r.restartDelay = minDuration;
} else {
r.restartDelay *= mAm.mConstants.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;
final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
for (int i=mRestartingServices.size()-1; i>=0; i--) {
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 {
// 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)) {
r.createdFromFg = false;
mRestartingServices.add(r);
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
cancelForegroundNotificationLocked(r);
mAm.mHandler.removeCallbacks(r.restarter);
// Service , restarter ServiceRestarter , ServiceRecord
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
...
return canceled;
}
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) {
...
try {
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false);
} catch (TransactionTooLargeException e) {
...
}
}
最後に
ServiceRestarter
でサービス再作成操作を実行し、run
メソッドでperformServiceRestartLocked
を呼び出して再起動を行い、performServiceRestartLocked
でbringUpServiceLocked
というメソッドを直接呼び出した.これがサービス再作成の方法であり、このメソッドの詳細な実装についてはAndroidソースのサービス起動プロセスという文章を読むことができる.Androidのサービス(Service)(二)サービスの自動再起動問題Androidサービス再起動リカバリ(サービスプロセス再起動)原理解析