Android Service (ii) automatic restart of Service

Source: Internet
Author: User

Android Service (ii) automatic restart of Service

Continue with the analysis in the previous article, followed by the second question "Service Automatic Restart problem"

(1) Service Lifecycle

(2) Automatic restart of the Service

Here we will talk about the automatic restart of the service. This problem is actually very simple. There are only two key methods. The Code is as follows:
This method calls the serviceDoneExecuting () method of ActivityManagerSerice in a series of handle methods for services in ActivityThread, but only handleServiceArgs () is related to restart (), this is because only a parameter named res works here.

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) {// The onStartCommand life cycle of the user service is called back. All users who are doing this application know this, // you can set the return value to determine whether your service is allowed to be restarted. The value is res = s. onStartCommand (data. args, data. flags, data. startId);} else {s. onTaskRemoved (data. args); res = Service. START_TASK_REMOVED_COMPLETE ;}............... try {// see what the system has done with this value. As a result, ActivityManagerNative has this feature. getDefault (). serviceDoneExecuting (data. token, 1, data. startId, res);} catch (RemoteException e) {// nothing to do .} ensureJitEnabled ();}..................}}
The following is the key code for this feature. The comments in it have been fully written. The key role is the stopIfKilled mark.
    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());        }    }
Under what circumstances can the service be restarted with this flag? There are many entry points in this scenario, such as system cleanup processes. In short, in the case of APP Died, the entry method is not listed and will be executed here:
Final void killServicesLocked (ProcessRecord app, boolean allowRestart) {// Report disconnected services. if (false) {// XXX we are leader the client link to the service for // death configurations. if (app. services. size ()> 0) {Iterator
 
  
It = 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.exe cuteNesting = 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. written ED = 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 = ge TServiceMap (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. // In many cases, // when restarting is allowed, if the crash process of the current service is more than twice, if (allowRestart & sr. crashCount> = 2 & (sr. ser ViceInfo. 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) {// cannot be restarted to directly Mount bringDownServiceLocked (sr);} else {// boolean canceled = scheduleServiceRestartLocked (sr, true); // shocould 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.exe cutingServices. 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 B Ut not yet finished starts shocould 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 (); // note that if canceled is true, the service still needs to be terminated. // pay attention to the upper limit of delivery and the upper limit of 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) {// re-Add si to pendingStart, so it will re-import intent into 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); // The most critical operation is here. Forget to post a restarted Runnable In the handler of ActivityManagerService. // This Is What You Did when creating ServiceRecord during the startup process. It is simply a ServiceRestarter, it stores the ServiceRecord itself // when the service is restarted, the service can be directly started based on the record. 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) {// The subsequent tasks are successfully completed. Required mservicerestartlocked (mService );}}}
During this process, there are several parameters that control whether to restart or not, as well as the upper limit of many parameters. Here we will separately describe them.
ServiceRecord. crashCount, ServiceRecord. StartItem. deliveryCount, ServiceRecord. StartItem. doneExecutingCount
CrashCount, as its name implies, is the number of crash times, which is automatically increased in handleAppCrashLocked (). It is obvious that each crash increases automatically.
DeliveryCount is also well understood. It belongs to StartItem, so it indicates the start information, the number of times the onStartCommand method is executed, that is, the number of external startService
DoneExecutingCount is also associated with deliveryCount. Similarly, it refers to the number of times this service is executed. What is the difference between them?
There are also two flag spaces: Service. START_FLAG_RETRY and Service. START_FLAG_REDELIVERY. This can be seen in ActivesService. sendServiceArgsLocked. This indicates whether the service is directly restarted or re-sent.
They are still mutually exclusive. This can be concluded in the START_REDELIVER_INTENT branch processing of the serviceDoneExecutingLocked () method. In general, the onStartCommand returns START_STICKY to allow restart, START_REDELIVER_INTENT resends the previous intent request and receives it again in the service.

The third question will be discussed in the next article.

(3) how to bind a Service to its client, that is, cross-Process calling.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.