Continue with the analysis of the previous chapter, followed by the second question, "Automatic restart of service"
(i), Service life cycle
(b) Automatic restart of service
Here is the problem of automatic restart of services, which is actually very simple, only two key methods. The code is as follows:
This method has the Servicedoneexecuting () method called to Activitymanagerserice in Activitythread's series of handle methods for service, But only Handleserviceargs () is associated with a restart, because only a parameter called res will work here.
private void Handleserviceargs (Serviceargsdata data) {Service s = mservices.get (Data.token); if (s! = null) {try {if (Data.args! = null) {Data.args.setExtrasClassLoade R (S.getclassloader ()); } int res; if (!data.taskremoved) {//callback The Onstartcommand life cycle of the user service, this application knows,//This can be controlled by setting its return value Whether the system's own service is allowed to be restarted, the logical value is res res = S.onstartcommand (Data.args, Data.flags, Data.startid); } else {s.ontaskremoved (Data.args); res = Service.start_task_removed_complete; } ... try {//To see what the system does with this value. What causes this feature to be Activ Itymanagernative.getdefault (). servicedoneexecuting (Data.token, 1, Data.startid, res); } catch (RemoteException e) {//Nothing to do. } ensurejitenabled (); } .................. } }Here is the key code of this feature, the comments are already written in a very full, the key to its role is stopifkilled this symbol.
void servicedoneexecutinglocked (Servicerecord r, int type, int startid, int res) {Boolean indestroying = Mdest Royingservices.contains (R); if (r! = null) {if (type = = 1) {//This was 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 doing with the associated start arguments. R.finddeliveredstart (Startid, true); Don ' t stop if killed. r.stopifkilled = false; Break } case Service.start_not_sticky: {//We do with the associated START Argu ments. 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 expl icitly//Call stop for it, but keep track of the fact//it is delive Red. 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 ("U Nknown 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 ()); } }So where does this flag bit make the service restartable? This kind of scene entrance many Ah, such as system cleanup process, in short, is the app died case, the entry method is not listed, the final will be executed to this:
final void killserviceslocked (Processrecord app, Boolean allowrestart) {//Report disconnected services. if (false) {//XXX We is letting the client link to the service for//death notifications. 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 {//c.conn.connecte D (r.classname, NULL); } catch (Exception e) {// Todo:this should be asynchronous! SLOG.W (TAG, "Exception thrown disconnected servce" + r.shortname + "from App" + App.processname, E); } } } } } } } First clear app state from services. 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 (bind Ingi); if (debug_service) slog.v (TAG, "killing binding" + B + ": shouldunbind=" + b.hasbound); B.binder = null; b.requested = b.received = B.hasbound = false; }}//Connections This application have 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 was not one//we actually was 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 + "No same as in map:" + cur); App.services.removeAt (i); Continue }//Any services running in the application could need to being placed//back in the pending list. There are a number of situations here//allow restart, if the current service is in the process crash more than two times, and not persistent process will not be restarted if (Allowrestart & & Sr.crashcount >= 2 && (sr.serviceInfo.applicationInfo.flags &applicationinfo.flag_ Persistent) = = 0) {SLOG.W (TAG, "Service crashed" + Sr.crashcount + "times, Stoppi NG: "+ sr); Eventlog.writeevent (Eventlogtags.am_service_crashed_too_much, Sr.userid, Sr.crashcount, Sr.shortname, app.pid); Bringdownservicelocked (SR); } else if (!allowrestart) {//does not allow reboot to hang Bringdownservicelocked (SR) directly; } 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'll 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 is 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 = mpendings Ervices.get (i); if (R.processname.equals (app.processname) && r.serviceiNfo.applicationInfo.uid = = App.info.uid) {mpendingservices.remove (i); }}}//Make sure we had 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 c anceled = 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 mind Uration = service_restart_duration; Long resettime = service_reset_run_duration; Any delivered and not yet finished starts should is 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 the canceled here if true or need to end the service//Also 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 < S Ervicerecord.max_done_executing_count) {//re-add Si in Pendingstart, so it will be re-entered into intent in the next execution 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/is started, then R ESET Our restart duration back to//the beginning, so we don ' t infinitely increase the duration On a service that just occasionally gets killed (which are//a normal case, due to process being Ki Lled 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, 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.next Restarttime+service_min_restart_time_between)) {r.nextrestarttime = R2.nextrestarttime + SERVICE_M In_restart_time_between; R.restartdelay = R.nextrestarttime-now; repeat = true; Break }}} while (repeat); } else {//persistent processes was immediately restarted, so there was no//reason to hold of the on R Estarting 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 here, forget Activitymanagerservice handler inside post a reboot runnable//This thing in front of the boot process created Servicerecord when there is, Very simple is a servicerestarter, it is stored in the Servicerecord itself//Restart the time according to this record can be directly started service MAm.mHandler.postAtTime (R.resta Rter, 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 following things are Cheng Zhang smoothly. performservicerestartlocked (Mservice);}}}
Throughout this process, there are several parameters to control whether the need to restart, but also set a number of parameters of the upper limit and so on, here alone to explain.
Servicerecord.crashcount, ServiceRecord.StartItem.deliveryCount, ServiceRecord.StartItem.doneExecutingCount
Crashcount as the name implies Ah, is the number of crash, the handleappcrashlocked () in the self-increase, it is obvious that every crash will be self-increasing, there is nothing to say
Deliverycount also very well understand that he belongs to StartItem, so that is the start message, is the number of execution Onstartcommand method, that is, the number of external StartService
Doneexecutingcount and Deliverycount are also very relevant, similar also said the number of times the service is executed, then what is the difference between them?
There are also two flags service.start_flag_retry, service.start_flag_redelivery to see together. This can be seen in the activesservice.sendserviceargslocked (). This means that the service is restarting or resending the sending request.
They are also mutually exclusive, which can be concluded in the start_redeliver_intent branching process of the servicedoneexecutinglocked () method, which generally means that Onstartcommand returns Start_ Sticky is allowed to restart, and Start_redeliver_intent will re-send the last intent request, which will be received again in the service.
Finally, in the next chapter, we will discuss the third question
(iii) How the service and its client bindings are implemented, that is, cross-process invocation issues.
Android Services (Service) (ii) Automatic restart of the service