生活随笔
收集整理的这篇文章主要介绍了
Android的服务(Service)(二)Service的自动重启问题
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
继续上篇的分析,接下来是第二个问题”Service的自动重启问题“
(一)、Service的生命周期
(二)、Service的自动重启问题
这里要说服务的自动重启问题,这个问题其实很简单,只有两个关键的方法。代码如下:
这个方法在ActivityThread的一系列针对服务的handle方法中都有调用到ActivityManagerSerice的serviceDoneExecuting()方法,但是跟重启有关的只有handleServiceArgs(),因为只有在这里才有一个叫res的参数会起作用。
[java] view plaincopy
private void handleServiceArgs(ServiceArgsData data) { Service s = mServices.get(data.token); if (s != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); } int res; if (!data.taskRemoved) { res = s.onStartCommand(data.args, data.flags, data.startId); } else { s.onTaskRemoved(data.args); res = Service.START_TASK_REMOVED_COMPLETE; } ............... try { ActivityManagerNative.getDefault().serviceDoneExecuting( data.token, 1, data.startId, res); } catch (RemoteException e) { } ensureJitEnabled(); } .................. } } 下面就是这个特性的关键代码,里面的注释已经写的很全了,关键其作用的就是stopIfKilled这个标志。
[java] view plaincopy
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { if (type == 1) { r.callStart = true; switch (res) { case Service.START_STICKY_COMPATIBILITY: case Service.START_STICKY: { r.findDeliveredStart(startId, true); r.stopIfKilled = false; break; } case Service.START_NOT_STICKY: { r.findDeliveredStart(startId, true); if (r.getLastStartId() == startId) { r.stopIfKilled = true; } break; } case Service.START_REDELIVER_INTENT: { ServiceRecord.StartItem si = r.findDeliveredStart(startId, false); if (si != null) { si.deliveryCount = 0; si.doneExecutingCount++; r.stopIfKilled = true; } break; } case Service.START_TASK_REMOVED_COMPLETE: { r.findDeliveredStart(startId, true); break; } default: throw new IllegalArgumentException( "Unknown service start result: " + res); } if (res == Service.START_STICKY_COMPATIBILITY) { r.callStart = false; } } final long origId = Binder.clearCallingIdentity(); serviceDoneExecutingLocked(r, inDestroying, inDestroying); Binder.restoreCallingIdentity(origId); } else { Slog.w(TAG, "Done executing unknown service from pid " + Binder.getCallingPid()); } } 那么这个标志位又是在哪些情况下使得服务可以重启的呢?这种场景入口很多啊,比如系统清理进程等,总之就是APP Died的情况下,入口方法不列举了,最后都会执行到这来:
[java] view plaincopy
final void killServicesLocked(ProcessRecord app, boolean allowRestart) { if (false) { if (app.services.size() > 0) { Iterator<ServiceRecord> it = app.services.iterator(); while (it.hasNext()) { ServiceRecord r = it.next(); for (int conni=r.connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> cl = r.connections.valueAt(conni); for (int i=0; i<cl.size(); i++) { ConnectionRecord c = cl.get(i); if (c.binding.client != app) { try { } catch (Exception e) { Slog.w(TAG, "Exception thrown disconnected servce " + r.shortName + " from app " + app.processName, e); } } } } } } } for (int i=app.services.size()-1; i>=0; i--) { ServiceRecord sr = app.services.valueAt(i); synchronized (sr.stats.getBatteryStats()) { sr.stats.stopLaunchedLocked(); } if (sr.app != null) { sr.app.services.remove(sr); } sr.app = null; sr.isolatedProc = null; sr.executeNesting = 0; sr.forceClearTracker(); if (mDestroyingServices.remove(sr)) { if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr); } final int numClients = sr.bindings.size(); for (int bindingi=numClients-1; bindingi>=0; bindingi--) { IntentBindRecord b = sr.bindings.valueAt(bindingi); if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b + ": shouldUnbind=" + b.hasBound); b.binder = null; b.requested = b.received = b.hasBound = false; } } for (int i=app.connections.size()-1; i>=0; i--) { ConnectionRecord r = app.connections.valueAt(i); removeConnectionLocked(r, app, null); } app.connections.clear(); ServiceMap smap = getServiceMap(app.userId); for (int i=app.services.size()-1; i>=0; i--) { ServiceRecord sr = app.services.valueAt(i); if (smap.mServicesByName.get(sr.name) != sr) { ServiceRecord cur = smap.mServicesByName.get(sr.name); Slog.wtf(TAG, "Service " + sr + " in process " + app + " not same as in map: " + cur); app.services.removeAt(i); continue; } if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) { Slog.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH, sr.userId, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr); } else if (!allowRestart) { 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()) { bringDownServiceLocked(sr); } } } } } if (!allowRestart) { app.services.clear(); for (int i=mRestartingServices.size()-1; i>=0; i--) { ServiceRecord r = mRestartingServices.get(i); if (r.processName.equals(app.processName) && r.serviceInfo.applicationInfo.uid == app.info.uid) { mRestartingServices.remove(i); clearRestartingIfNeededLocked(r); } } for (int i=mPendingServices.size()-1; i>=0; i--) { ServiceRecord r = mPendingServices.get(i); if (r.processName.equals(app.processName) && r.serviceInfo.applicationInfo.uid == app.info.uid) { mPendingServices.remove(i); } } } int i = mDestroyingServices.size(); while (i > 0) { i--; ServiceRecord sr = mDestroyingServices.get(i); if (sr.app == app) { sr.forceClearTracker(); mDestroyingServices.remove(i); if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr); } } app.executingServices.clear(); } [java] view plaincopy
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; 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) { } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) { r.pendingStarts.add(0, si); long dur = SystemClock.uptimeMillis() - si.deliveredTime; 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(); } r.totalRestartCount++; if (r.restartDelay == 0) { r.restartCount++; r.restartDelay = minDuration; } else { 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; 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 { 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); } r.cancelNotification(); mAm.mHandler.removeCallbacks(r.restarter); mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime); r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Slog.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, r.userId, r.shortName, r.restartDelay); return canceled; } [java] view plaincopy
private class ServiceRestarter implements Runnable { private ServiceRecord mService; void setService(ServiceRecord service) { mService = service; } public void run() { synchronized(mAm) { performServiceRestartLocked(mService); } } } 整个这个过程中,有好几个参数控制着是否需要重启,也定了很多参数的上限等等,这里单独列出来解释一下。
ServiceRecord.crashCount、ServiceRecord.StartItem.deliveryCount、ServiceRecord.StartItem.doneExecutingCount
crashCount顾名思义啊,就是crash的次数,这个在handleAppCrashLocked()中自增的,很明显每crash一次就会自增,没什么好说的
deliveryCount也很好理解,他是属于StartItem的,所以表示的是启动信息,是执行onStartCommand方法的次数,也就是外部startService的次数
doneExecutingCount跟deliveryCount还很有关联,类似的也是说的这个服务执行的次数,那么它们有什么区别呢?
还有两个标志位Service.START_FLAG_RETRY、Service.START_FLAG_REDELIVERY要一起看。这个在ActivesService.sendServiceArgsLocked()中可以看到。意思就是说这个服务是直接重启还是重新发送发送请求。
它们还是互斥的,这点在serviceDoneExecutingLocked()方法的START_REDELIVER_INTENT分支处理中可以得到结论,总的来说就是说onStartCommand返回START_STICKY是允许重启,而START_REDELIVER_INTENT会重新将上次的intent请求发送出去,服务中会重新接收到这个。
最后将在下篇论第三个问题
(三)、Service与其客户端的绑定如何实现,即跨进程调用问题。
原文地址:http://blog.csdn.net/hehui1860/article/details/41743549
总结
以上是生活随笔为你收集整理的Android的服务(Service)(二)Service的自动重启问题的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。