編輯:關於Android編程
繼續上篇的分析,接下來是第二個問題”Service的自動重啟問題“
(一)、Service的生命周期
(二)、Service的自動重啟問題
這裡要說服務的自動重啟問題,這個問題其實很簡單,只有兩個關鍵的方法。代碼如下:
這個方法在ActivityThread的一系列針對服務的handle方法中都有調用到ActivityManagerSerice的serviceDoneExecuting()方法,但是跟重啟有關的只有handleServiceArgs(),因為只有在這裡才有一個叫res的參數會起作用。
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) { //就是回調了用戶服務的onStartCommand生命周期,這個做應用的都知道了, //這裡可以通過設置其返回值來控制自己的服務是否允許被重新啟動,順理成章的這個值就是res 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) { // nothing to do. } ensureJitEnabled(); } .................. } }下面就是這個特性的關鍵代碼,裡面的注釋已經寫的很全了,關鍵其作用的就是stopIfKilled這個標志。
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { if (type == 1) { // 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. r.findDeliveredStart(startId, true); // Don't stop if killed. 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. 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. r.stopIfKilled = 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) { 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的情況下,入口方法不列舉了,最後都會執行到這來:
final void killServicesLocked(ProcessRecord app, boolean allowRestart) { // Report disconnected services. if (false) { // XXX we are letting the client link to the service for // death notifications. if (app.services.size() > 0) { Iteratorit = app.services.iterator(); while (it.hasNext()) { ServiceRecord r = it.next(); for (int conni=r.connections.size()-1; conni>=0; conni--) { ArrayList cl = r.connections.valueAt(conni); for (int i=0; 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; } } // Clean up any connections this application has to other services. 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); // Now do remaining service cleanup. for (int i=app.services.size()-1; i>=0; i--) { ServiceRecord sr = app.services.valueAt(i); // Sanity check: if the service listed for the app is not one // we actually are maintaining, drop it. 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; } // Any services running in the application may need to be placed // back in the pending list. // 這裡還是分很多種情況的 // 允許重啟時,如果當前服務所在進程crash超過兩次,並且不是persistent的進程就結束不會重啟了 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); // 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. 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); } } } } } if (!allowRestart) { app.services.clear(); // Make sure there are no more restarting services for this process. 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); } } } // Make sure we have no more records on the stopping list. 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(); }
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. 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(); //注意了,這裡的canceled如果為true還是需要結束服務的 //還要關注一下delivery的上限和doneExecuting的上限 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)) { //重新在pendingStart中添加si,所以會在下次執行時重新帶入intent進去 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 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 *= SERVICE_RESTART_DURATION_FACTOR; if (r.restartDelay < minDuration) { r.restartDelay = minDuration; } } } 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; 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)) { r.createdFromFg = false; mRestartingServices.add(r); r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now); } r.cancelNotification(); mAm.mHandler.removeCallbacks(r.restarter); // 最關鍵的操作在這裡,忘ActivityManagerService的handler裡面post一個重啟的Runnable // 這個東西前面啟動過程創建ServiceRecord時有的,很簡單就是一個ServiceRestarter,它裡面保存了這個ServiceRecord本身 // 重啟的時候根據這個record就可以直接啟動服務了 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; }
private class ServiceRestarter implements Runnable { private ServiceRecord mService; void setService(ServiceRecord service) { mService = service; } public void run() { synchronized(mAm) { //後面的事情就順利成章了。 performServiceRestartLocked(mService); } } }整個這個過程中,有好幾個參數控制著是否需要重啟,也定了很多參數的上限等等,這裡單獨列出來解釋一下。
最後將在下篇論第三個問題
(三)、Service與其客戶端的綁定如何實現,即跨進程調用問題。
launcher,也就是Android的桌面應用程序。下圖是我正在使用的魅族手機的launcher應用程序: 接下來我們要開發一個自己的launcher,使其替
上一篇文章中我們講解了android產品研發過程中的代碼Review。通過代碼Review能夠提高產品質量,增強團隊成員之間的溝通,提高開發效率,所以良好的產品開發迭代過
Android中圖案解鎖首先要理解圖案的實現原理,上一張圖: 由上圖,可以看出,圖案中手勢的記錄是1-9或0-8的,保存的順序就是密碼,當然有些是可以重復的,為了安全,
創建一個Android應用程序 Android Application Project 設置工程的相關信息,默認會創建icon和activity