We know that there are two working states of a Service, one is started state, and the other is bound state. The former applies to back-end computing, while the latter applies to Service interactions with other components. We know that services have two life cycles

  • StartService: onCreate()– >onStartCommand() –>onDestroy();

  • BindService: onCreate()– >onBind() –>onUnbind()– >onDestroy();

Service startup process

Start the journey with startServic for the Activity. Clicking will take you to the startService method of the ContextWrapper class

android.content.ContextWrapper

 @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
Copy the code

MBase is of ContextImpl type and is associated when attach method is executed during Activity startup. The specific code is appended at the end of the text

Most of ContextWrapper’s operations are implemented through mBase. So that’s the bridge pattern and let’s see how it works

android.appContextImpl
class ContextImpl extends Context {
   @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
            ......
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
    }
}
Copy the code

ActivityManager. GetService () to obtain is ActivityManageService, commonly referred to as AMS, frequently appear in the source code, is not clear can go to see my previous article.

Go ahead and look at startService under AMS

com.android.server.am.ActivityManagerService final ActiveServices mServices; . @Override public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, String callingPackage, String callingFeatureId, int userId) throws TransactionTooLargeException { ...... ComponentName res; Try {key code res = mServices. StartServiceLocked (caller, service, resolvedType callingPid, callingUid, requireForeground, callingPackage, callingFeatureId, userId); } finally { Binder.restoreCallingIdentity(origId); } return res; }}Copy the code

If you want to open AS and follow the code, you can scroll up and down to see the methods, which are all related to Service, so remember the AMS class.

MServices is an ActiveServices type, and it performs subsequent Service operations.

com.android.server.am.ActiveServices ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, @Nullable String callingFeatureId, final int userId, boolean allowBackgroundActivityStarts) throws TransactionTooLargeException { ...... ComponentName CMP = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); if (! r.mAllowWhileInUsePermissionInFgs) { r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid, callingUid, service, r, allowBackgroundActivityStarts); } return cmp; }Copy the code

Call startServiceInnerLocked in startServiceLocked

com.android.server.am.ActiveServices ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { ServiceState stracker = r.getTracker(); if (stracker ! = null) { stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); } r.callStart = false; . String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); . return r.name; }Copy the code

BringUpServiceLocked method internal quite long, look dazed, suddenly look at a realStartServiceLocked method. From the name, it should be where the Service is actually started.

com.android.server.am.ActiveServices private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException { ...... app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), app.getReportedProcState()); . sendServiceArgsLocked(r, execInFg, true); . }Copy the code

App. The thread and ActivityManager. GetService (), is also very common reference, is ApplicationThread object

The Service of onCreate

Next, go to ApplicationThread to see what scheduleCreateService is doing, and you’ll know that it’s probably messaging again.

android.app.ActivityThread public final void scheduleCreateService(IBinder token,ServiceInfo info, CompatibilityInfo compatInfo, int processState) { updateProcessState(processState, false); CreateServiceData s = new CreateServiceData(); s.token = token; s.info = info; s.compatInfo = compatInfo; sendMessage(H.CREATE_SERVICE, s); } in internal class H, case CREATE_SERVICE: if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj))); } handleCreateService((CreateServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;Copy the code

The familiar routine is to send messages to ApplicationThread, and H to handle the implementation.

android.app.ActivityThread @UnsupportedAppUsage private void handleCreateService(CreateServiceData data) { unscheduleGcIdler(); LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); Create ContextImpl object ContextImpl context = ContextImpl. CreateAppContext (this, packageInfo); Access application application app = packageInfo. MakeApplication (false, mInstrumentation); java.lang.ClassLoader cl = packageInfo.getClassLoader(); By reflecting access service service = packageInfo. GetAppFactory () instantiateService (cl, data. Info. Name, data. The intent); // Service resources must be initialized with the same loaders as the application // context. context.getResources().addLoaders( app.getResources().getLoaders().toArray(new ResourcesLoader[0])); context.setOuterContext(service); Link ContextImpl to the Service, Similar to the Activity service. Attach (context, this, the data. The info. Name, data. The token, app, ActivityManager. GetService ()); Execute onCreate service.oncreate () for Service; Mservices.put (data.token, Service); try { ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to create service " + data.info.name + ": " + e.toString(), e); }}}Copy the code

It’s up to you to do the real work… comb

  1. Create the ContextImpl object
  2. Create an Application. Of course, there is only one global thing, which is described in the Appliction initialization
  3. Obtain the service through reflection
  4. Link ContextImpl to the Service
  5. Execute onCreate for Service
  6. Add the current Service to mServices, defined as follows

At this point, the onCreat of the Service is executed, which means the Service is started.

final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
Copy the code

The Service of onStartCommond

Inside the realStartServiceLocked method in the previous code, there is also a key method, sendServiceArgsLocked

com.android.server.am.ActiveServices private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) throws TransactionTooLargeException { ...... try { r.app.thread.scheduleServiceArgs(r, slice); } catch (TransactionTooLargeException e) { ...... }... }Copy the code

The scheduleServiceArgs method of ApplicationThread is executed

The android. App. ApplicationThread ActivityThread inner class: public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) { List<ServiceStartArgs> list = args.getList(); for (int i = 0; i < list.size(); i++) { ...... sendMessage(H.SERVICE_ARGS, s); }} inside class H: case SERVICE_ARGS: if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj))); } handleServiceArgs((ServiceArgsData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; Private void handleServiceArgs(ServiceArgsData data) {Services = mservices.get (data.token); if (s ! = null) { try { if (data.args ! = null) { data.args.setExtrasClassLoader(s.getClassLoader()); data.args.prepareToEnterProcess(); } int res; if (! Data.taskremoved) {Execute onStartCommand res = s.startCommand (data.args, data.flags, data.startid); } else { s.onTaskRemoved(data.args); res = Service.START_TASK_REMOVED_COMPLETE; }... } catch (Exception e) { if (! mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to start service " + s + " with " + data.args + ": " + e.toString(), e); }}}}Copy the code

In handleServiceArgs, we first fetch the previously created Service from mServices, and then execute the onStartCommand method of the Service. This shows that onStartCommand is executed after onCreate.

Service binding process

As with Service startup, bindService is implemented in ContextImpl and bindServiceCommon is called internally

android.content.ContextWrapper

    @Override
    public boolean bindService(Intent service, ServiceConnection conn, int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
                getUser());
    }
   
Copy the code

What is called internally is the bindServiceCommon method, which we need to focus on

android.content.ContextImpl private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, String instanceName, Handler handler, Executor executor, UserHandle user) { // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser. IServiceConnection sd; . If (mPackageInfo! = null) { if (executor ! = null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags); } else { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); } } else { throw new RuntimeException("Not supported in system context"); } validateServiceIntent(service); 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(this); (2) complete bind operations through AMS int res = ActivityManager. GetService () bindIsolatedService (mMainThread. GetApplicationThread (), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, instanceName, getOpPackageName(), user.getIdentifier()); if (res < 0) { throw new SecurityException( "Not allowed to bind to service " + service); } return res ! = 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}Copy the code

The bindServiceCommon method is longer and does two things:

  1. Converting the ServiceConnection object conn ServiceDispatcher. InnerConnection sd type object
  2. AMS is used to complete the binding

I know it’s a little confusing to just look at the code and conclude that you did both of these things, but let’s go into the code.

Converting ServiceConnection type ServiceDispatcher InnerConnection

First of all, to determine why IServiceConnection instance sd is actually ServiceDispatcher. InnerConnection type.

sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags); Click getServiceDispatcher and you’ll see that LoadedApk is a familiar class because it’s where the Application was created.

android.app.LoadedApk public final IServiceConnection getServiceDispatcher(ServiceConnection c, Context context, Handler handler, int flags) { return getServiceDispatcherCommon(c, context, handler, null, flags); } private IServiceConnection getServiceDispatcherCommon(ServiceConnection c, Context context, Handler handler, Executor executor, int flags) { synchronized (mServices) { LoadedApk.ServiceDispatcher sd = null; . return sd.getIServiceConnection(); }}Copy the code

The last thing that comes in here is the getIServiceConnection method in the ServiceDispatcher class

android.app.LoadedApk static final class ServiceDispatcher{ private final ServiceDispatcher.InnerConnection mIServiceConnection; IServiceConnection getIServiceConnection() { return mIServiceConnection; }}Copy the code

Problem is explained, finally return mIServiceConnection getIServiceConnection method is ServiceDispatcher. InnerConnection type. New problem again, why such a struggle to convert ServiceConnection into ServiceDispatcher. InnerConnection? Here’s how the Art of Android Development explores:

The reason why ServiceConnection objects cannot be used directly is that service bindings may be cross-process, so ServiceConnection objects must use a Binder to allow remote servers to call back their methods. The InnerConnection class of ServiceDispatcher acts as a Binder, so ServiceDispatcher acts as a link between ServiceConnection and InnerConnection.

ActivityManagerSerice The binding is complete

We are already familiar with the guidance of ActivityManager. GetService () is actually obtained ActivityManagerSerice object (AMS), so to find bindIsolatedService inside

com.android.server.am.ActivityManagerSerice final ActiveServices mServices; . public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String instanceName, String callingPackage, int userId) throws TransactionTooLargeException { ...... synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, instanceName, callingPackage, userId); }}Copy the code

The process goes to ActiveServices

com.android.server.am.ActiveServices int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String instanceName, String callingPackage, final int userId) throws TransactionTooLargeException { ...... Execution within onCreate bringUpServiceLocked (serviceRecord, serviceIntent getFlags (), callerFg, false, false); . Internal execution onBind requestServiceBindingLocked (s, b.i ntent, callerFg, true); . }Copy the code

The process here is similar to the process started by Serice. The first step is to create the Service and then execute the start or bind method. OnCreate bringUpServiceLocked internal is to perform a Service, requestServiceBindingLocked internal binding is execution.

com.android.server.am.ActiveServices private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired) throws TransactionTooLargeException { ...... realStartServiceLocked(r, app, execInFg); . }Copy the code

You can see that inside the bringUpServiceLocked, the realStartServiceLocked method is also executed, as seen in the analysis of the Service startup process, executing the onCreate of the Service. Then look at

com.android.server.am.ActiveServices private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException { ...... r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.getReportedProcState()); . }Copy the code

In the same way, send a message to ApplicationThread and execute it in H.

android.app.ActivityThread public final void scheduleBindService(IBinder token, Intent intent, boolean rebind, int processState) { ...... sendMessage(H.BIND_SERVICE, s); } Inside H, the message is handled by case BIND_SERVICE: trace.tracebegin (trace.trace_tag_activity_manager, "serviceBind"); handleBindService((BindServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break;Copy the code

Take a look at what the handleBindService does

android.app.ActivityThread private void handleBindService(BindServiceData data) { Service s = mServices.get(data.token);  if (DEBUG_SERVICE) Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); if (s ! = null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(); // Try {if (! data.rebind) { IBinder binder = s.onBind(data.intent); System to inform the client already successful link Service ActivityManager. GetService () publishService (data. The token and data. The intent, binder); } else { s.onRebind(data.intent); ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to bind to service " + s + " with " + data.intent + ": " + e.toString(), e); }}}}Copy the code

HandleBindService does two things,

  1. Perform onBind
  2. Notifies the client that the Service binding is complete

By the time I figured it out for myself, I thought it was over. OnBind is a Service method that the client does not know that the link has been successfully linked, so there is a later action to notify the client via AMS.

How to notify the client that the service has been successfully bound? Go straight to AMS

com.android.server.am.ActivityManagerService public void publishService(IBinder token, Intent intent, IBinder service) { // Refuse possible leaked file descriptors if (intent ! = null && intent.hasFileDescriptors() == true) { throw new IllegalArgumentException("File descriptors passed in Intent"); } synchronized(this) { if (! (token instanceof ServiceRecord)) { throw new IllegalArgumentException("Invalid service token"); } analysis mentioned before mServices is ActiveServices type mServices publishServiceLocked (ServiceRecord token, intent, service). }}Copy the code
com.android.server.am.ActiveServices

  void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
           ......
            c.conn.connected(r.name, service, false);	
        } catch (Exception e) {
          
        }
    }
Copy the code

This conn is the premise of ServiceDispatcher InnerConnection type

android.app.LoadedApk static final class ServiceDispatcher { private final ServiceConnection mConnection; . public void connected(ComponentName name, IBinder service, boolean dead) { if (mActivityExecutor ! = null) { mActivityExecutor.execute(new RunConnection(name, service, 0, dead)); } else if (mActivityThread ! = null) { mActivityThread.post(new RunConnection(name, service, 0, dead)); } else {doConnected(name, service, dead); } } public void doConnected(ComponentName name, IBinder service, boolean dead) { ...... mActivityThread.post(new RunConnection(name, service, 0, dead)); }}Copy the code

MActivityThread has an H of type Handler inside, so this method is directly posted to the main thread for execution. RunConnection is defined as follows

private final class RunConnection implements Runnable { RunConnection(ComponentName name, IBinder service, int command, boolean dead) { mName = name; mService = service; mCommand = command; mDead = dead; } public void run() {if (mCommand == 0) {// Connected(mName, mService, mDead); } else if (mCommand == 1) { doDeath(mName, mService); } } final ComponentName mName; final IBinder mService; final int mCommand; final boolean mDead; }Copy the code
public void doConnected(ComponentName name, IBinder service, boolean dead) {
    if (service != null) {
          mConnection.onServiceConnected(name, service);
    } 
}
Copy the code

MConnection is the second parameter passed by bindService. At this point, the client’s onServiceConnected interface is executed, and the Service binding process is complete.

If you’re mConnection. OnServiceConnected (name, service); Why does this line of code represent a client that already executes onServiceConnected results remain a question that will be resolved at the end of this article

Plus: what is mBase

Assignment of android. Content. ContextWrapper mBase protected void attachBaseContext (Context base) {if (mBase! = null) { throw new IllegalStateException("Base context already set"); } mBase = base; } android.app.Activity @UnsupportedAppUsage final void attach(Context context, ...) { attachBaseContext(context); . } and we know that the attach method is in ActivityThread performLaunchActivity call android app. ActivityThread private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ...... ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; . So mBase is ContextImpl activity. Attach (appContext,...) ; . }Copy the code