Android Service (iii) Service client binding and cross-Process
Next, let's continue with the analysis in the previous article. The third question is how to bind the Service to its client, that is, cross-Process calling.
(1) Service Lifecycle
(2) Automatic restart of the Service
(3) how to bind a Service to its client, that is, cross-Process calling.
The binder service is used to bind the client. Let's take a look at the ContextImpl bindServiceCommon method.
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 && mPackageInfo.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } service.prepareToLeaveProcess(); int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, user.getIdentifier()); if (res < 0) { throw new SecurityException( Not allowed to bind to service + service); } return res != 0; } }Then, the binder object used for cross-process connection will be created in LoadedApk. java, which is the InnerConnection of ServiceDispatcher.
Public final IServiceConnection getServiceDispatcher (ServiceConnection c, Context context, Handler handler, int flags) {synchronized (mServices) {LoadedApk. serviceDispatcher sd = null; // here, a map is used to save all connection records as ArrayMap.
Map = mServices. get (context); if (map! = Null) {sd = map. get (c) ;}if (sd = null) {sd = new ServiceDispatcher (c, context, handler, flags); if (map = null) {map = new ArrayMap
(); 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
MDispatcher; InnerConnection (LoadedApk. ServiceDispatcher sd) {mDispatcher = new WeakReference
(Sd );} // This method is used to call the method used to bind the link in ActivityManagerService. // The service here is undoubtedly the one returned when the remote object executes onBind. // This is the one passed by the server and the client. channel of a binder object, because this process involves two cross-process operations, this design must be reasonable public void connected (ComponentName, IBinder service) throws RemoteException {LoadedApk. serviceDispatcher sd = mDispatcher. get (); if (sd! = Null) {sd. connected (name, service) ;}} private final ArrayMap
MActiveConnections = new ArrayMap
(); ServiceConnection getServiceConnection () {return mConnection;} IServiceConnection getIServiceConnection () {return mIServiceConnection;} public void connected (ComponentName, IBinder service) {if (mActivityThread! = Null) {mActivityThread. post (new RunConnection (name, service, 0);} else {doConnected (name, service) ;}} public void death (ComponentName, IBinder service) {.......................} // actually execute connect public void doConnected (ComponentName name, IBinder service) {ServiceDispatcher. connectionInfo old; ServiceDispatcher. connectionInfo info; synchronized (this) {if (mForgotten) {// We unbound Before authentication ing the connection; ignore // any connection already ed. return;} old = mActiveConnections. get (name); if (old! = Null & old. binder = service) {// Huh, already have 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 was dead before we got it... just // don't do anything with it. mActiveConnections. rem Ove (name); return ;}} else {// The named service is being disconnected... clean up. mActiveConnections. remove (name);} if (old! = Null) {old. binder. unlinkToDeath (old. deathMonitor, 0) ;}// If there was an old service, it is not disconnected. if (old! = Null) {mConnection. onServiceDisconnected (name);} // If there is a new service, it is now connected. // you are familiar with it. This is the callback for getting remote object proxy after binding the service. if (service! = Null) {mConnection. onServiceConnected (name, service) ;}} public void doDeath (ComponentName, IBinder service) {mConnection. onServiceDisconnected (name);} private final class RunConnection implements Runnable {RunConnection (ComponentName name, IBinder service, 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 {DeathMonitor (ComponentName name, IBinder service) {mName = name; mService = service;} public void binderDied () {death (mName, mService);} final ComponentName mName; final IBinder mService ;}}
The next step is the bind operation. As mentioned above, we have mentioned in the lifecycle. Here we will list the method:
Int bindServiceLocked (IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) {.................... serviceLookupResult res = retrieveServiceLocked (service, resolvedType, Binder. getCallingPid (), Binder. getCallingUid (), userId, true, callerFg );.................... try {if (unscheduleServiceRestartLocked (s, callerApp.info. Uid, false) {if (DEBUG_SERVICE) Slog. v (TAG, bind service while restart pending: + s );}................... // bindings adds a binding request, and then processes the binding interface AppBindRecord B = s in the later requestServiceBindingsLocked () process. retrieveAppBindingLocked (service, callerApp );.................... if (flags & Context. BIND_AUTO_CREATE )! = 0) {s. lastActivity = SystemClock. uptimeMillis (); // if the flag contains Automatic startup, create the service. You can check the code above. If the service has been started, in fact, if (bringUpServiceLocked (s, service. getFlags (), callerFg, false )! = Null) {return 0 ;}} if (s. app! = Null) {// This cocould have made the service more important. mAm. updateLruProcessLocked (s. app, s. app. hasClientActivities, B. client); mAm. updateOomAdjLocked (s. app);} if (s. app! = Null & B. intent. already ed) {// Service is already running, so we can immediately // publish the connection. // if the service has been started and bound, the binder object is directly returned. The conn here is the proxy of InnerConnection mentioned above. here we can see that the connected operation is actually caused
// InnerConnection it to complete 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. client. processName +), e);} // If this is the first app connected back to this binding, // and the service had previusly asked to be told when // rebound, then do so. // It can be seen from this that, in general, onBind will only be executed once, Unless the requested doRebind // flag is all unbind on the old client, the if (B. intent. apps. size () = 1 & B. intent. doRebind) {requestServiceBindingLocked (s, B. intent, callerFg, true) ;}} else if (! B. intent. requested) {// if the service has not been bound, the following operations will be called to the onBind operation requestServiceBindingLocked (s, B. intent, callerFg, false);} getServiceMap (s. userId ). ensureNotStartingBackground (s);} finally {Binder. restoreCallingIdentity (origId);} return 1 ;}
Have you noticed that InnerConnection does not have the unConnected method, so how does one perform callback through this connection channel when unbinding? You can see that the unBind process mentioned above does not have any place to execute this operation. Some of it is only the unBind of the server and may execute onDestory. Then, when will ServiceConnection. onServiceDisconnected be executed? In fact, only the binder on the remote server will die. This is through registering an IBinder. DeathRecipient for this binder object, which is the mechanism of notification of death of the binder. I will not talk about it here.