Service的自動(dòng)重啟問(wèn)題

版權(quán)聲明:本文為作者原創(chuàng),轉(zhuǎn)載必須注明出處。
轉(zhuǎn)載請(qǐng)注明出處:http://www.itdecent.cn/p/1c995328c293

今天簡(jiǎn)單討論下服務(wù)重啟的問(wèn)題和分析一個(gè)常見(jiàn)的系統(tǒng)log:

一、Service自動(dòng)啟動(dòng)服務(wù)流程:
每次調(diào)用startService(Intent)的時(shí)候,都會(huì)調(diào)用該Service對(duì)象的onStartCommand(Intent,int,int)方法,這個(gè)方法return 一個(gè)int值,return 的值有四種:

START_STICKY:如果service進(jìn)程被kill掉,保留service的狀態(tài)為開(kāi)始狀態(tài),但不保留遞送的intent對(duì)象。
隨后系統(tǒng)會(huì)嘗試重新創(chuàng)建service,由于服務(wù)狀態(tài)為開(kāi)始狀態(tài),所以創(chuàng)建服務(wù)后一定會(huì)調(diào)用onStartCommand(
Intent,int,int)方法。如果在此期間沒(méi)有任何啟動(dòng)命令被傳遞到service,那么參數(shù)Intent將為null。
 
START_NOT_STICKY:“非粘性的”。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand后,服務(wù)被異常
kill掉,系統(tǒng)不會(huì)自動(dòng)重啟該服務(wù)。
 
START_REDELIVER_INTENT:重傳Intent。使用這個(gè)返回值時(shí),如果在執(zhí)行完onStartCommand后,服務(wù)被
異常kill掉,系統(tǒng)會(huì)自動(dòng)重啟該服務(wù),并將Intent的值傳入。
 
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務(wù)被kill后一定能重啟。

最終在framework層,s.onStartCommand返回res

1. private void handleServiceArgs(ServiceArgsData data) {  
2.     Service s = mServices.get(data.token);  
3.     if (s != null) {  
4.         try {  
5.             if (data.args != null) {  
6.                 data.args.setExtrasClassLoader(s.getClassLoader());  
7.             }  
8.             int res;  
9.             if (!data.taskRemoved) {  
10.                 //就是回調(diào)了用戶服務(wù)的onStartCommand生命周期,這個(gè)做應(yīng)用的都知道了,  
11.   
12.                 //這里可以通過(guò)設(shè)置其返回值來(lái)控制自己的服務(wù)是否允許被重新啟動(dòng),順理成章的這
                     //個(gè)值就是res 
13.                 res = s.onStartCommand(data.args, data.flags, data.startId);  
14.             } else {  
15.                 s.onTaskRemoved(data.args);  
16.                 res = Service.START_TASK_REMOVED_COMPLETE;  
17.             }  
18.             ...............  
19.             try {  
20.                 //看看系統(tǒng)用這個(gè)值都干了一些什么導(dǎo)致有這個(gè)特性  
21.                 ActivityManagerNative.getDefault().serviceDoneExecuting(  
22.                         data.token, 1, data.startId, res);  
23.             } catch (RemoteException e) {  
24.                 // nothing to do.  
25.             }  
26.             ensureJitEnabled();  
27.         }  
28.         ..................  
29.     }  
30. } 

下面就是這個(gè)特性的關(guān)鍵代碼,里面的注釋已經(jīng)寫的很全了,關(guān)鍵其作用的就是stopIfKilled這個(gè)標(biāo)志。

1. void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {  
2.     boolean inDestroying = mDestroyingServices.contains(r);  
3.     if (r != null) {  
4.         if (type == 1) {  
5.             // This is a call from a service start...  take care of  
6.             // book-keeping.  
7.             r.callStart = true;  
8.             switch (res) {  
9.                 case Service.START_STICKY_COMPATIBILITY:  
10.                 case Service.START_STICKY: {  
11.                     // We are done with the associated start arguments.  
12.                     r.findDeliveredStart(startId, true);  
13.                     // Don't stop if killed.  
14.                     r.stopIfKilled = false;  
15.                     break;  
16.                 }  
17.                 case Service.START_NOT_STICKY: {  
18.                     // We are done with the associated start arguments.  
19.                     r.findDeliveredStart(startId, true);  
20.                     if (r.getLastStartId() == startId) {  
21.                         // There is no more work, and this service  
22.                         // doesn't want to hang around if killed.  
23.                         r.stopIfKilled = true;  
24.                     }  
25.                     break;  
26.                 }  
27.                 case Service.START_REDELIVER_INTENT: {  
28.                     // We'll keep this item until they explicitly  
29.                     // call stop for it, but keep track of the fact  
30.                     // that it was delivered.  
31.                     ServiceRecord.StartItem si = r.findDeliveredStart(
                          startId, false);  
32.                     if (si != null) {  
33.                         si.deliveryCount = 0;  
34.                         si.doneExecutingCount++;  
35.                         // Don't stop if killed.  
36.                         r.stopIfKilled = true;  
37.                     }  
38.                     break;  
39.                 }  
40.                 case Service.START_TASK_REMOVED_COMPLETE: {  
41.                     // Special processing for onTaskRemoved().  Don't  
42.                     // impact normal onStartCommand() processing.  
43.                     r.findDeliveredStart(startId, true);  
44.                     break;  
45.                 }  
46.                 default:  
47.                     throw new IllegalArgumentException(  
48.                             "Unknown service start result: " + res);  
49.             }  
50.             if (res == Service.START_STICKY_COMPATIBILITY) {  
51.                 r.callStart = false;  
52.             }  
53.         }  
54.         final long origId = Binder.clearCallingIdentity();  
55.         serviceDoneExecutingLocked(r, inDestroying, inDestroying);  
56.         Binder.restoreCallingIdentity(origId);  
57.     } else {  
58.         Slog.w(TAG, "Done executing unknown service from pid "  
59.                 + Binder.getCallingPid());  
60.     }  
61. }

那么這個(gè)標(biāo)志位又是在哪些情況下使得服務(wù)可以重啟的呢?這種場(chǎng)景入口很多啊,比如系統(tǒng)清理進(jìn)程等,總之就是APP Died的情況下,入口方法不列舉了,最后都會(huì)執(zhí)行到這來(lái):

1. final void killServicesLocked(ProcessRecord app, boolean allowRestart) {  
2.     // Report disconnected services.  
3.     if (false) {  
4.         // XXX we are letting the client link to the service for  
5.         // death notifications.  
6.         if (app.services.size() > 0) {  
7.             Iterator<ServiceRecord> it = app.services.iterator();  
8.             while (it.hasNext()) {  
9.                 ServiceRecord r = it.next();  
10.                 for (int conni=r.connections.size()-1; conni>=0; conni--) {  
11.                     ArrayList<ConnectionRecord> cl = r.connections.valueAt(conni);  
12.                     for (int i=0; i<cl.size(); i++) {  
13.                         ConnectionRecord c = cl.get(i);  
14.                         if (c.binding.client != app) {  
15.                             try {  
16.                                 //c.conn.connected(r.className, null);  
17.                             } catch (Exception e) {  
18.                                 // todo: this should be asynchronous!  
19.                                 Slog.w(TAG, "Exception thrown disconnected servce "  
20.                                       + r.shortName  
21.                                       + " from app " + app.processName, e);  
22.                             }  
23.                         }  
24.                     }  
25.                 }  
26.             }  
27.         }  
28.     }  
29.   
30.     // First clear app state from services.  
31.     for (int i=app.services.size()-1; i>=0; i--) {  
32.         ServiceRecord sr = app.services.valueAt(i);  
33.         synchronized (sr.stats.getBatteryStats()) {  
34.             sr.stats.stopLaunchedLocked();  
35.         }  
36.         if (sr.app != null) {  
37.             sr.app.services.remove(sr);  
38.         }  
39.         sr.app = null;  
40.         sr.isolatedProc = null;  
41.         sr.executeNesting = 0;  
42.         sr.forceClearTracker();  
43.         if (mDestroyingServices.remove(sr)) {  
44.             if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying " + sr);  
45.         }  
46.   
47.         final int numClients = sr.bindings.size();  
48.         for (int bindingi=numClients-1; bindingi>=0; bindingi--) {  
49.             IntentBindRecord b = sr.bindings.valueAt(bindingi);  
50.             if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b  
51.                     + ": shouldUnbind=" + b.hasBound);  
52.             b.binder = null;  
53.             b.requested = b.received = b.hasBound = false;  
54.         }  
55.     }  
56.   
57.     // Clean up any connections this application has to other services.  
58.     for (int i=app.connections.size()-1; i>=0; i--) {  
59.         ConnectionRecord r = app.connections.valueAt(i);  
60.         removeConnectionLocked(r, app, null);  
61.     }  
62.     app.connections.clear();  
63.   
64.     ServiceMap smap = getServiceMap(app.userId);  
65.   
66.     // Now do remaining service cleanup.  
67.     for (int i=app.services.size()-1; i>=0; i--) {  
68.         ServiceRecord sr = app.services.valueAt(i);  
69.         // Sanity check: if the service listed for the app is not one  
70.         // we actually are maintaining, drop it.  
71.         if (smap.mServicesByName.get(sr.name) != sr) {  
72.             ServiceRecord cur = smap.mServicesByName.get(sr.name);  
73.             Slog.wtf(TAG, "Service " + sr + " in process " + app  
74.                     + " not same as in map: " + cur);  
75.             app.services.removeAt(i);  
76.             continue;  
77.         }  
78.   
79.         // Any services running in the application may need to be placed  
80.         // back in the pending list.  
81.         // 這里還是分很多種情況的  
82.         // 允許重啟時(shí),如果當(dāng)前服務(wù)所在進(jìn)程crash超過(guò)兩次,并且不是persistent的進(jìn)程就結(jié)束不會(huì)
            //重啟了  
83.         if (allowRestart && sr.crashCount >= 2 && (sr.serviceInfo
              .applicationInfo.flags  
84.                 &ApplicationInfo.FLAG_PERSISTENT) == 0) {  
85.             Slog.w(TAG, "Service crashed " + sr.crashCount  
86.                     + " times, stopping: " + sr);  
87.             EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,  
88.                     sr.userId, sr.crashCount, sr.shortName, app.pid);  
89.             bringDownServiceLocked(sr);  
90.         } else if (!allowRestart) {  
91.             // 不允許重啟直接掛掉  
92.             bringDownServiceLocked(sr);  
93.         } else {  
94.             //  
95.             boolean canceled = scheduleServiceRestartLocked(sr, true);  
96.   
97.             // Should the service remain running?  Note that in the  
98.             // extreme case of so many attempts to deliver a command  
99.             // that it failed we also will stop it here.  
100.             if (sr.startRequested && (sr.stopIfKilled || canceled)) {  
101.                 if (sr.pendingStarts.size() == 0) {  
102.                     sr.startRequested = false;  
103.                     if (sr.tracker != null) {  
104.                         sr.tracker.setStarted(false, mAm.mProcessStats
                              .getMemFactorLocked(),  
105.                                 SystemClock.uptimeMillis());  
106.                     }  
107.                     if (!sr.hasAutoCreateConnections()) {  
108.                         // Whoops, no reason to restart!  
109.                         bringDownServiceLocked(sr);  
110.                     }  
111.                 }  
112.             }  
113.         }  
114.     }  
115.   
116.     if (!allowRestart) {  
117.         app.services.clear();  
118.   
119.         // Make sure there are no more restarting services for this process.  
120.         for (int i=mRestartingServices.size()-1; i>=0; i--) {  
121.             ServiceRecord r = mRestartingServices.get(i);  
122.             if (r.processName.equals(app.processName) &&  
123.                     r.serviceInfo.applicationInfo.uid == app.info.uid) {  
124.                 mRestartingServices.remove(i);  
125.                 clearRestartingIfNeededLocked(r);  
126.             }  
127.         }  
128.         for (int i=mPendingServices.size()-1; i>=0; i--) {  
129.             ServiceRecord r = mPendingServices.get(i);  
130.             if (r.processName.equals(app.processName) &&  
131.                     r.serviceInfo.applicationInfo.uid == app.info.uid) {  
132.                 mPendingServices.remove(i);  
133.             }  
134.         }  
135.     }  
136.   
137.     // Make sure we have no more records on the stopping list.  
138.     int i = mDestroyingServices.size();  
139.     while (i > 0) {  
140.         i--;  
141.         ServiceRecord sr = mDestroyingServices.get(i);  
142.         if (sr.app == app) {  
143.             sr.forceClearTracker();  
144.             mDestroyingServices.remove(i);  
145.             if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove destroying "
                + sr);  
146.         }  
147.     }  
148.   
149.     app.executingServices.clear();  
150. }
1. private final boolean scheduleServiceRestartLocked(ServiceRecord r,  
2.         boolean allowCancel) {  
3.     boolean canceled = false;  
4.   
5.     ServiceMap smap = getServiceMap(r.userId);  
6.     if (smap.mServicesByName.get(r.name) != r) {  
7.         ServiceRecord cur = smap.mServicesByName.get(r.name);  
8.         Slog.wtf(TAG, "Attempting to schedule restart of " + r  
9.                 + " when found in map: " + cur);  
10.         return false;  
11.     }  
12.   
13.     final long now = SystemClock.uptimeMillis();  
14.   
15.     if ((r.serviceInfo.applicationInfo.flags  
16.             &ApplicationInfo.FLAG_PERSISTENT) == 0) {  
17.         long minDuration = SERVICE_RESTART_DURATION;  
18.         long resetTime = SERVICE_RESET_RUN_DURATION;  
19.   
20.         // Any delivered but not yet finished starts should be put back  
21.         // on the pending list.  
22.         final int N = r.deliveredStarts.size();  
23.         if (N > 0) {  
24.             for (int i=N-1; i>=0; i--) {  
25.                 ServiceRecord.StartItem si = r.deliveredStarts.get(i);  
26.                 si.removeUriPermissionsLocked();  
27.                 //注意了,這里的canceled如果為true還是需要結(jié)束服務(wù)的  
28.                 //還要關(guān)注一下delivery的上限和doneExecuting的上限  
29.                 if (si.intent == null) {  
30.                     // We'll generate this again if needed.  
31.                 } else if (!allowCancel || (si.deliveryCount < 
                        ServiceRecord.MAX_DELIVERY_COUNT  
32.                         && si.doneExecutingCount < 
                         ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {  
33.                     //重新在pendingStart中添加si,所以會(huì)在下次執(zhí)行時(shí)重新帶入intent進(jìn)去  
34.                     r.pendingStarts.add(0, si);  
35.                     long dur = SystemClock.uptimeMillis() - si.deliveredTime;  
36.                     dur *= 2;  
37.                     if (minDuration < dur) minDuration = dur;  
38.                     if (resetTime < dur) resetTime = dur;  
39.                 } else {  
40.                     Slog.w(TAG, "Canceling start item " + si.intent +
41.                        " in service "  + r.name);  
42.                     canceled = true;  
43.                 }  
44.             }  
45.             r.deliveredStarts.clear();  
46.         }  
47.   
48.         r.totalRestartCount++;  
49.         if (r.restartDelay == 0) {  
50.             r.restartCount++;  
51.             r.restartDelay = minDuration;  
52.         } else {  
53.             // If it has been a "reasonably long time" since the service  
54.             // was started, then reset our restart duration back to  
55.             // the beginning, so we don't infinitely increase the duration  
56.             // on a service that just occasionally gets killed (which is  
57.             // a normal case, due to process being killed to reclaim memory).  
58.             if (now > (r.restartTime+resetTime)) {  
59.                 r.restartCount = 1;  
60.                 r.restartDelay = minDuration;  
61.             } else {  
62.                 r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;  
63.                 if (r.restartDelay < minDuration) {  
64.                     r.restartDelay = minDuration;  
65.                 }  
66.             }  
67.         }  
68.   
69.         r.nextRestartTime = now + r.restartDelay;  
70.   
71.         // Make sure that we don't end up restarting a bunch of services  
72.         // all at the same time.  
73.         boolean repeat;  
74.         do {  
75.             repeat = false;  
76.             for (int i=mRestartingServices.size()-1; i>=0; i--) {  
77.                 ServiceRecord r2 = mRestartingServices.get(i);  
78.                 if (r2 != r && r.nextRestartTime  
79.                         >= (r2.nextRestartTime-SERVICE_MIN_RESTART_TIME_BETWEEN)  
80.                         && r.nextRestartTime  
81.                         < (r2.nextRestartTime+SERVICE_MIN_RESTART_TIME_BETWEEN)) {  
82.                     r.nextRestartTime = r2.nextRestartTime + 
                         SERVICE_MIN_RESTART_TIME_BETWEEN;  
83.                     r.restartDelay = r.nextRestartTime - now;  
84.                     repeat = true;  
85.                     break;  
86.                 }  
87.             }  
88.         } while (repeat);  
89.   
90.     } else {  
91.         // Persistent processes are immediately restarted, so there is no  
92.         // reason to hold of on restarting their services.  
93.         r.totalRestartCount++;  
94.         r.restartCount = 0;  
95.         r.restartDelay = 0;  
96.         r.nextRestartTime = now;  
97.     }  
98.   
99.     if (!mRestartingServices.contains(r)) {  
100.         r.createdFromFg = false;  
101.         mRestartingServices.add(r);  
102.         r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);  
103.     }  
104.   
105.     r.cancelNotification();  
106.   
107.     mAm.mHandler.removeCallbacks(r.restarter);  
108.   // 最關(guān)鍵的操作在這里,忘ActivityManagerService的handler里面post一個(gè)重啟的Runnable  
109.   // 這個(gè)東西前面啟動(dòng)過(guò)程創(chuàng)建ServiceRecord時(shí)有的,很簡(jiǎn)單就是一個(gè)ServiceRestarter,
       //它里面,保存了這個(gè)ServiceRecord本身  
110.   // 重啟的時(shí)候根據(jù)這個(gè)record就可以直接啟動(dòng)服務(wù)了  
111.     mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);  
112.     r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;  
113.     Slog.w(TAG, "Scheduling restart of crashed service "  
114.             + r.shortName + " in " + r.restartDelay + "ms");  
115.     EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,  
116.             r.userId, r.shortName, r.restartDelay);  
117.   
118.     return canceled;  
119. }
1. private class ServiceRestarter implements Runnable {  
2.     private ServiceRecord mService;  
3.   
4.     void setService(ServiceRecord service) {  
5.         mService = service;  
6.     }  
7.   
8.     public void run() {  
9.         synchronized(mAm) {  
10.             //后面的事情就順利成章了。  
11.             performServiceRestartLocked(mService);  
12.         }  
13.     }  
14. }

整個(gè)這個(gè)過(guò)程中,有好幾個(gè)參數(shù)控制著是否需要重啟,也定了很多參數(shù)的上限等等,這里單獨(dú)列出來(lái)解釋一下。
ServiceRecord.crashCount、ServiceRecord.StartItem.deliveryCount、ServiceRecord.StartItem.doneExecutingCount
crashCount顧名思義啊,就是crash的次數(shù),這個(gè)在handleAppCrashLocked()中自增的,很明顯每crash一次就會(huì)自增,沒(méi)什么好說(shuō)的
deliveryCount也很好理解,他是屬于StartItem的,所以表示的是啟動(dòng)信息,是執(zhí)行onStartCommand方法的次數(shù),也就是外部startService的次數(shù)
doneExecutingCount跟deliveryCount還很有關(guān)聯(lián),類似的也是說(shuō)的這個(gè)服務(wù)執(zhí)行的次數(shù),那么它們有什么區(qū)別呢?
還有兩個(gè)標(biāo)志位Service.START_FLAG_RETRY、Service.START_FLAG_REDELIVERY要一起看。這個(gè)在ActivesService.sendServiceArgsLocked()中可以看到。意思就是說(shuō)這個(gè)服務(wù)是直接重啟還是重新發(fā)送發(fā)送請(qǐng)求。
它們還是互斥的,這點(diǎn)在serviceDoneExecutingLocked()方法的START_REDELIVER_INTENT分支處理中可以得到結(jié)論,總的來(lái)說(shuō)就是說(shuō)onStartCommand返回START_STICKY是允許重啟,而START_REDELIVER_INTENT會(huì)重新將上次的intent請(qǐng)求發(fā)送出去,服務(wù)中會(huì)重新接收到這個(gè)。
二、一個(gè)常見(jiàn)的系統(tǒng)log分析:
很多時(shí)候會(huì)看到這樣的系統(tǒng)log:
10-27 16:31:57.300 2108 3930 [system_server] I ActivityManager: Process xxx (pid xxx) has died
10-27 16:31:57.300 2108 3930 [system_server] D ActivityManager: cleanUpApplicationRecord – xxx
10-27 16:31:57.303 2108 3930 [system_server] W ActivityManager: Scheduling restart of crashed service xxxr/.xxxService in 319547ms

在319547ms后xxx又被xxxService喚醒:

10-27 16:37:16.882 2108 2138 [system_server] I ActivityManager: Start proc (pid):xxx/u10a97 for service xxx/.xxxService caller=xxx
根據(jù)log反推流程:



在執(zhí)行到第9步之后,請(qǐng)參照啟動(dòng)服務(wù)的流程圖:



通過(guò)流程圖看出,在app主進(jìn)程died之后,延遲喚醒該app內(nèi)對(duì)應(yīng)service,然后再由service拉起app主進(jìn)程,造成app的自啟動(dòng)。

參考:http://blog.csdn.net/hehui1860/article/details/41743549

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容