Continuing with the analysis of the previous chapter, the third question is "How does a service's binding to its client be implemented, that is, cross-process invocation issues"
(i), Service life cycle
(b) Automatic restart of service
(iii) How the service and its client bindings are implemented, that is, cross-process invocation issues.
The bindings that serve the client are implemented by binder, which is the client-to-bind service. Look at the Bindservicecommon method of Contextimpl.
Private Boolean Bindservicecommon (Intent service, serviceconnection conn, int flags, Userhandle user) { Iserviceconnection SD; if (mpackageinfo! = null) {SD = Mpackageinfo.getservicedispatcher (conn, Getoutercontext (), Mmainthread.gethandler (), flags); } try {IBinder token = Getactivitytoken (); if (token = = null && (flags&bind_auto_create) = = 0 && Mpackageinfo! = null &&am P Mpackageinfo.getapplicationinfo (). targetsdkversion < Android.os.Build.VERSION_CODES. Ice_cream_sandwich) {flags |= bind_waive_priority; } service.preparetoleaveprocess (); int res = Activitymanagernative.getdefault (). Bindservice (Mmainthread.getapplicationthread (), GetActivityTo Ken (), Service, service.resolvetypeifneeded (Getcontentresolver ()), SD, Flags, User.getidenTifier ()); if (Res < 0) {throw new SecurityException ("Not allowed to bind to service" + s Ervice); } return Res! = 0; } }
And then we'll go to Loadedapk.java. A Binder object is created for cross-process connections, which is a servicedispatcher innerconnection.
Public final iserviceconnection Getservicedispatcher (serviceconnection C, Context context, Handler Handler, int flags) {synchronized (mservices) {loadedapk.servicedispatcher SD = null; Here we have a map to save all the connection records arraymap<serviceconnection, loadedapk.servicedispatcher> map = mservices.ge T (context); if (map! = null) {SD = Map.get (c); } if (SD = = null) {SD = new Servicedispatcher (c, context, Handler, flags); if (map = = null) {map = new arraymap<serviceconnection, loadedapk.servicedispatcher> (); Mservices.put (context, map); } map.put (c, SD); } else {sd.validate (context, handler); } return Sd.getiserviceconnection (); } }
Static Final class Servicedispatcher {private final servicedispatcher.innerconnection miserviceconnection; Private final serviceconnection mconnection; private static class ConnectionInfo {IBinder binder; Ibinder.deathrecipient Deathmonitor; } private static Class Innerconnection extends Iserviceconnection.stub {final WEAKREFERENCE<LOADEDAP K.servicedispatcher> Mdispatcher; Innerconnection (Loadedapk.servicedispatcher sd) {mdispatcher = new Weakreference<loadedapk.servicedispa Tcher> (SD); }//This method is the method call when the binding link is executed in Activitymanagerservice//The service here is undoubtedly the one returned when the remote object executes onbind.//So This is the channel where the server and the client pass a Binder object, because this process involves two cross-process operations, so the design must also be reasonable public void connected (componentname name, Ibind ER service) throws remoteexception {loadedapk.servicedispatcher sd = Mdispatcher.get (); if (SD! = null) { sd.connected (name, service); }}} Private final Arraymap<componentname, servicedispatcher.connectioninfo> Mactiveconnec tions = new Arraymap<componentname, servicedispatcher.connectioninfo> (); Serviceconnection getserviceconnection () {return mconnection; } iserviceconnection Getiserviceconnection () {return miserviceconnection; } public void connected (componentname name, IBinder service) {if (mactivitythread! = null) { Mactivitythread.post (New Runconnection (name, service, 0)); } else {doconnected (name, service); }} public void Death (componentname name, IBinder service) {...}.........} Actual execution of Connect public void doconnected (componentname name, IBinder service) {Servicedispatcher.conn Ectioninfo old; ServicedisPatcher. ConnectionInfo info; Synchronized (this) {if (Mforgotten) {//We unbound before receiving the connection; Gnore//any connection received. Return } old = Mactiveconnections.get (name); if (old = null && Old.binder = = Service) {//Huh, already has this one. Oh well! Return } if (service! = NULL) {//A new service is being connected ... set it all up. Mdied = false; info = new ConnectionInfo (); Info.binder = Service; Info.deathmonitor = new Deathmonitor (name, service); try {service.linktodeath (info.deathmonitor, 0); Mactiveconnections.put (name, info); } catch (RemoteException e) { This service is dead before we got it ... just//don ' t do anything with it. Mactiveconnections.remove (name); Return }} else {//The named service is being disconnected ... Mactiveconnections.remove (name); } if (old! = null) {Old.binder.unlinkToDeath (old.deathmonitor, 0); }}//If There was a old service, it's not disconnected. if (old! = null) {mconnection.onservicedisconnected (name); }//If There is a new service, it's now connected. Look familiar, this is the callback we get the remote object proxy after binding the service. &NBSP;IF (Service! = null) {mconnection.onserviceconnected (name, Service); }} public void Dodeath (componentname name, IBinder service) {mconnection.onservicedisconnected (name); } Private Final class Runconnection implements Runnable {runconnection (componentname name, IBinder serv Ice, int command) {mname = name; Mservice = Service; Mcommand = command; public void Run () {if (Mcommand = = 0) {doconnected (mname, Mservice); } else if (Mcommand = = 1) {Dodeath (mname, Mservice); }}} Private final class Deathmonitor implements ibinder.deathrecipient & nbsp; { DeathMonitor (ComponentName Name, IBinder service) { Mname = name; Mservice = service; } public void binderdied () { death (Mname, Mservice); } Final ComponentName mName; Final IBinder mservice; } }
The following is the bind operation, the previous life cycle has been mentioned, here again the method listed:
int bindservicelocked (iapplicationthread caller, IBinder token, Intent service, String resolvedty PE, iserviceconnection connection, int flags, int userId) {........... Servicelookupresult res = retrieveservicelocked (service, Resolvedtype, Binder. Getcallingpid (), Binder.getcallinguid (), UserId, True, CALLERFG); .... try {if (s, CallerApp.info.uid, unscheduleservicerestartlocked), ..., ...... False) {if (Debug_service) slog.v (TAG, "BIND SERVICE while RESTART PENDING:" + s); } ... add a binding request to the//bindings in the subsequent requestservicebindingslocked () process to process the binding interface in the. Appbindrecord B = s.retrieveappbindinglocked (service, Callerapp); ... if ((Flags&context),..... .....bind_auto_create)! = 0) {s.lastactivity = Systemclock.uptimemillis (); If you carry a flag bit that contains auto-start, then create the service operation, the code can look ahead, if it has been started, it is actually what the operation is not dry if (bringupservicelocked (S, service.getflags (), CALLERFG, false)! = null) {return 0; }} if (S.app! = null) {//This could has made the service More important. Mam.updatelruprocesslocked (S.app, s.app.hasclientactivities, b.client); Mam.updateoomadjlocked (S.app); } if (S.app! = null && b.intent.received) {//Service is Already running, so we can immediately//publish the connection. If the service has been started and has been bound, directly back to the binder object, here conn is the aforementioned innerconnection agent, here see connected operation is actually by <pre name= "code" class= " Java ">//InnErconnection it to complete the try {c.conn.connected (s.name, B.intent.binder); } catch (Exception e) {SLOG.W (TAG, "Failure sending service" + S.shortname + "to Connection" + c.conn.asbinder () + "(in" + C.BINDING.CLI Ent.processname + ")", e); }//If This was the first app connected back to this binding,//and the S Ervice had previously asked to being told when//rebound and then do. As can be seen from here, Onbind is normally executed only once, unless the request Dorebind//This flag bit is the old client after all unbind automatically set on if (b. Intent.apps.size () = = 1 && b.intent.dorebind) {requestservicebindinglocked (S, B.intent, CALLERFG, True); }} else if (!b.intent.requested) { The service has not yet been bound, then performing subsequent operations will invoke the Onbind operation Requestservicebindinglocked (S, B.intent, CALLERFG, false); } getservicemap (S.userid). Ensurenotstartingbackground (s); } finally {binder.restorecallingidentity (origid); } return 1; }
Do you notice a problem in the above, there is no unconnected method in Innerconnection, then how to use this connection channel to perform callbacks when the time of the unbind? You can look at the previous unbind process, there is no place to perform this operation, it is only the service side of the unbind and may execute ondestory. Then when will it be executed to serviceconnection.onservicedisconnected, in fact only on the remote server that binder death will be executed. This is done by registering a ibinder.deathrecipient for this binder object, which is the binder's death notification mechanism. I'm not going to talk about it here.
To the Android service here has been briefly analyzed, it is impossible to cover all the right, but also please give us a lot of advice.
Android Services (service) (iii) binding with the service client and cross-process