preface

We know that the general Android engineers are in the application layer development, will not involve system source code, but if you want to go to the bottom, or in-depth plug-in, Framework system layer development work, if you do not understand Android source code but not, So the next I based on their own understanding and learning to record with Android development is closely related to the source analysis, probably from the Android SystemServer start, four components start, AMS, PMS and other dimensions to introduce, the following is my plan, of course, may change in the future.

If you haven’t paid attention to it yet, you can pay attention to it first. The series of articles will be updated continuously.

Android 8.0 source code analysis (a) SystemServer process started

Android 8.0 source code analysis (ii) Launcher

Android 8.0 source code analysis (three) application process creation to the application startup process

Android 8.0 source code analysis (four) Activity start

Android 8.0 source code analysis (5) Service startup

Android 8.0 source code analysis (6) BroadcastReceiver launch

Android 8.0 source code analysis (seven) ContentProvider start

ActivityManagerService

Android 8.0 source code analysis (nine) WindowManager

Android 8.0 source code analysis (ten) WindowManagerService window management

introduce

In this article, we will analyze the startService and bindService starting principles of the four components of Service. In the actual development, there are many scenarios using Service components, such as background music, background file download, etc., as long as you want to put some task logic in the background execution, so you can start a Service. In today’s development environment, it is not necessary to know how to use Service, but to understand the inner workings of Service.

StartService Startup process

ContextImpl call to AMS

Let’s start with a sequence diagram of calling startService in the Activity

When we call the startActivity function in the Activity, click startActivity to see the source implementation and see that it is implemented in ContextWrapper class.

//ContextWrappaer
	Context mBase;

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

So what is ContextWrapper? Let’s take a picture of the inheritance

The startService implementation of the Context is as follows:

//Context.java
public abstract class Context {...@Nullable
    public abstract ComponentName startService(Intent service); . }Copy the code

Context is an abstract class. What class is its implementation? In the last article we introduced how to start an Activity. When it starts, the Context is created. Note the following code:

//ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      

        /** * 1. Obtain ActivityInfo */ by encapsulating the ActivityClientRecord information class that starts the Activity
        ActivityInfo aInfo = r.activityInfo;
        /** * 2. If r.packageInfo == null get LoadedApk instance */
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        /** * 3. Obtain the ComponentName */ for the Activity start component by encapsulating the Activity start information class ActivityClientRecord
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if(r.activityInfo.targetActivity ! =null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        /** * 4. Create a context for the Activity */
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            /** * get the current context-loaded ClassLoader */
            java.lang.ClassLoader cl = appContext.getClassLoader();
            / * * * 5. Call Instrumentation. NewActivity function, internal through this class loader to create the Activity instance * /
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if(r.state ! =null) { r.state.setClassLoader(cl); }}catch (Exception e) {
           ...
        }

        try {
            /** * 6. Get Application object */
            Application app = r.packageInfo.makeApplication(false, mInstrumentation); ./** * 7. Attach the appContext to the Activity attach function and initialize the Activity attach */
                activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); .return activity;
    }
Copy the code

From the above code, we only focus on comment 4 and comment 7. Let’s look at the implementation of comment 4, the code is as follows:

//ActivityThread.java
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {...//1. Create the Activity Context
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); .return appContext;
    }
Copy the code
//ContextImpl.java
    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {...//2. Instantiate the implementation class ContextImpl
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null.0, classLoader); .return context;
    }
Copy the code

The ContextImpl object is instantiated in the createActivityContext function. Now let’s go back to comment 7 of the ActivityThread performLaunchActivity function

//ActivityThread.java
                /** * 7. Attach the appContext to the Activity attach function and initialize the Activity attach */
activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
Copy the code
//Activity.java
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
      	// Call the superclass functionattachBaseContext(context); . }Copy the code

AttachBaseContext is the parent function with the following code:

//ContextThemeWrapper
  public class ContextThemeWrapper extends ContextWrapper {...@Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase); }... }Copy the code

Continue calling the parent class with the following code:

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    

    protected void attachBaseContext(Context base) {
        if(mBase ! =null) {
            throw new IllegalStateException("Base context already set");
        }
      	// Assign to Context
        mBase = base;
    }
Copy the code

ContextImpl: mBase; ContextWrapper: startService; ContextImpl: mBase; startService: mBase;

//ContextImpl
class ContextImpl extends Context {...@Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        /** * Calls the internal startServiceCommon function */
        return startServiceCommon(service, false, mUser); }... }Copy the code

To continue calling the internal function, the code is as follows:

//ContextImpl
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            /** * Calls the startService function */ of the AMS proxy's IActivityManagerComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); .return cn;
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}Copy the code

We have already seen this in the source code for starting an Activity. Internally, aiDL is used to communicate between the application process and AMS(SystemServer) process, and finally callback to THE AMS startService function

ActivityThread start the Service

Call process as usual, look at the overall sequence diagram:

We know from the previous section that we will eventually return to the AMS startService function, so we will directly look at the implementation of AMS, the code is as follows:

//AMS.java

   /** * where ContextImpl calls * via interprocess communication@param caller
     * @param service
     * @param resolvedType
     * @param requireForeground
     * @param callingPackage
     * @param userId
     * @return
     * @throws TransactionTooLargeException
     */
    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {...try {
                /** * 1. Call ActiveService's startServiceLocked function */
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            returnres; }}Copy the code

The code for comment 1 is implemented by calling the startServiceLocked function from the ActiveService class as follows:

//ActiveService.java

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {.../** * 1. Check whether there is a ServiceRecord */ corresponding to the Service parameter
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);

        if (res == null) {
            return null;
        }
        if (res.record == null) {
            return new ComponentName("!", res.permission ! =null
                    ? res.permission : "private to package");
        }

        /** * 2. Get the ServiceRecord for the Service parameter from ServiceLookupResult and pass it to startServiceInnerLocked at comment 3 */ServiceRecord r = res.record; ./** * 3. Call internal startServiceInnerLocked */
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }
Copy the code

The retrieveServiceLocked function at comment 1 looks for a ServiceRecord corresponding to the parameter service, and if it doesn’t, PMS is called to get the service information corresponding to the parameter service. Encapsulate ServiceRecord and return ServiceLookupResult. ServiceRecord is used to describe a Service, similar to ActivityRecord. The ServiceRecord obtained in comment 2 is assigned to comment 3. Finally, call the internal startServiceInnerLocked function as follows:

//ActiveServices.java


    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {.../** * continue to call the internal function */
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false.false); .return r.name;
    }
Copy the code

Let’s move on to the bringUpServiceLocked function

//ActiveServices.java

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {.../** * 1. Get the process in which the Service wants to run */
        final String procName = r.processName;
        String hostingType = "service";
        ProcessRecord app;

        if(! isolated) {/** * 2. Pass the procName and Service uid to AMS's getProcessRecordLocked function to see if the currently started Service */ already exists
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
      

            /** * 3. If the application process running Service already exists */
            if(app ! =null&& app.thread ! =null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    /** * 3.1 Starting Service */
                    realStartServiceLocked(r, app, execInFg);
                    return null; . }}else{.../** * 4. If the application process to run the Service does not exist */
        if (app == null && !permissionsReviewRequired) {
            /** * 5. Create application process */
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingType, r.name, false, isolated, false)) = =null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                returnmsg; }...return null;
    }
Copy the code

The main logic of the above code is to query whether the process of the Service to be started already exists in AMS. If so, start the Service. If not, call the internal function of Comment 5 to create an application process. So let’s go straight to comment 3.1 and start the Service function as follows:

//ActiveService.java

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {.../** * 1. The scheduleCreateService function */ is called on the scheduleCreateService function of the IApplicationThread
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
         ...
    }
Copy the code

The scheduleCreateService function has already left the AMS process. What is app.thread? The IApplicationThread is an aiDL file that implements a non-static inner class of activityThreads. The IApplicationThread is an aiDL file that implements a non-static inner class of activityThreads.

//ActivityThread.java
public final class ActivityThread {...private class ApplicationThread extends IApplicationThread.Stub {...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;
						// Call an internal overloaded functionsendMessage(H.CREATE_SERVICE, s); }... }Copy the code

Remember the h.create_servic flag, and the code is as follows:

  //ActivityThread.java 
	private void sendMessage(int what, Object obj) {
        /** * 1. Continue calling the internal overloaded function what = h.create_servic */

        sendMessage(what, obj, 0.0.false); }...private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + "" + mH.codeToString(what)
            + ":" + arg1 + "/" + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        /** * 2. Send what = h. create_servic message what */
        mH.sendMessage(msg);
    }
Copy the code

Look directly at comment 2, which sends the flag H.create_servic via sendMessage of class H. Class H is the inherited Handler, the implementation code is as follows:

//ActivityThread.java
public final class ActivityThread {
// Initialize class H
final H mH = new H();
  
    /** * H inherits the message management class */ from handler, which is the main thread in the application process
    private class H extends Handler {...public static final int CREATE_SERVICE  = 114; .public void handleMessage(Message msg) {...case CREATE_SERVICE:
       / / call handleCreateService
       handleCreateService((CreateServiceData)msg.obj);
       Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
       break; . }}}Copy the code

H’s handleMessage receives the sent message and calls the handleCreateService function. Let’s see the concrete implementation:

//ActivityService.java


    private void handleCreateService(CreateServiceData data) {

        /** * 1. Get the LoadApk */ of the application to start the Service
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            /** * 2. Get the class loader */
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            /** * 3. Create Servcie instance */
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ...
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            /** * 4. Create Context instance */
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            /** * 5
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            /** * 6. Run Service onCreate lifecycle */
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
            } catch (RemoteException e) {
                throwe.rethrowFromSystemServer(); }}catch(Exception e) { ... }}Copy the code

Almost every line of code in this function is important, and I’m just going to summarize it here, basically load the Service class through the ClassLoader, bind the Service to the Application and Context, one at the Application level, one at the Service level, The rest of the Service life cycle is similar. If you are interested, you can check the source code for yourself. The next section gives you another way to start the Service.

BindService Binds the service process

ContextImpl call to AMS

ContextImpl to AMS: ContextImpl to AMS

ContextWrapper relationship here no longer too much explanation, if forgotten can see my article introduced in the beginning, we see directly ContextWrapper bindService function in the class, the code is as follows:

//ContextWrapper.java
public class ContextWrapper extends Context { Context mBase; .@Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        returnmBase.bindService(service, conn, flags); }... }Copy the code

ContextImpl (bindService); ContextImpl (bindService); ContextImpl (bindService)

//ContextImpl.java
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
Copy the code

Call the internally overloaded function bindServiceCommon as follows:

//ContextImp.java
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {.../** * 1. Call LoadApk mPackageInfo's getServiceDispatcher function. Its main function is to encapsulate ServiceConnection as an sd object of type IServiceConncet. Binder, as IServiceConnection's name indicates, This enables cross-process binding of services. * /
        if(mPackageInfo ! =null) {
            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. Get the AMS proxy and call bindService, which will be passed to AMS */
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            returnres ! =0;
        } catch (RemoteException e) {
            throwe.rethrowFromSystemServer(); }}Copy the code

Here we go straight to the core code that calls the bindService function of IActivityManager, which eventually returns to the AMS bindService function, as described in the next section:

Service binding process

Received ContextImp bindService request from AMS, the code is as follows:

//AMS.java

    /** ** passed through ContextImpl call, interprocess communication@param caller
     * @param token
     * @param service
     * @param resolvedType
     * @param connection
     * @param flags
     * @param callingPackage
     * @param userId
     * @return
     * @throws TransactionTooLargeException
     */
    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException {...synchronized(this) {
            // Call ActiveService's bindServiceLocked function
            returnmServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code

To AMS’s bindService function, and internally to ActiveService’s bindServiceLocked function, You can see that both ActiveService start and bind end up handling the Service startup logic in ActiveService. The following call code is as follows:

//ActiveServie.java

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {.../** * 1. Call ServiceRecord's retrieveAppBindingLocked function to retrieve AppBindRecord, * retrieveAppBindingLocked creates an IntentBindRecord internally and assigns a value to the member variable of the IntentBindRecord. * /
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            ConnectionRecord c = newConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); IBinder binder = connection.asBinder(); .if((flags&Context.BIND_AUTO_CREATE) ! =0) {
                s.lastActivity = SystemClock.uptimeMillis();
                /** * realStartServiceLocked realStartServiceLocked realStartServiceLocked realStartServiceLocked ActivityThread starts the Service by calling its onCreate function. */
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) ! =null) {
                    return 0; }}.../** * / ProcessRecord/ProcessRecord/ProcessRecord/ProcessRecord/ProcessRecord/ProcessRecord/ProcessRecord B.inten. received Indicates that the current application process has received Service bindings as returned Binder, */
            if(s.app ! =null && b.intent.received) {
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    /** * connect to IServiceConnect@seeServiceDispatcher InnerConnection, * /
                    c.conn.connected(s.name, b.intent.binder, false);
                } catch (Exception e) {
                  ...
                }

                /** * 5. If the current application process is the first to bind to a Service and the Service has already called onUnBind, you need to invoke comment 6 */
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    Internal requestServiceBindingLocked function called / 6. * * * * /
                    requestServiceBindingLocked(s, b.intent, callerFg, true); }}else if(! b.intent.requested) {//7. If the Client of the application process has not sent a request to bind Service, comment 8 is invoked
                /** * 8. */ is not rebound
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
            getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return 1;
    }

Copy the code

There is a lot of processing logic in the current function, which involves several objects related to the Service.

  • ServiceRecord: Describes a Service
  • ProcessRecord: Information about a process
  • ConnectionRecord: Used to describe a communication between an application process and a Service
  • AppBindRecord: When an application process binds a Service with an Intent, AppBindRecord maintains the association between the Service and the application process. The Intent (IntentBindRecord) attached to the Service (ProcessRecord), AppBindRecord (AppBindRecord), IntentBindRecord (IntentBindRecord) And all bound communication records (ArraySet).
  • IntentBindRecord: Describes an Intent that binds a Service.

I’m not going to go through the code here, but I’m going to comment on each step, so I’m going to refer to comment 2 above, and I’m going to call the onCreate step internally just like startService, and then I’m going to look at comment 6 if the current application process is the first one to bind to a Service, And the Service has already called onUnBind. The code is as follows:

//ActiveServices.java
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        /** * 1. i. rol indicates whether a user incubates a query on a data panel, and the previous step indicates that it does, * i.aps.size () where I is IntentBindRecord, AMS assigns an object of type IntentBindRecord to each Intent bound to a Service. * /
        if((! i.requested || rebind) && i.apps.size() >0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
              	//2. Call IApplicationThread
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if(! rebind) { i.requested =true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
             		...
                throw e;
            } catch (RemoteException e) {
              ...
                return false; }}return true;
    }
Copy the code

According to the above code, comment 2 is finally called to communicate with the ActivityThread application process via IApplicationThread as follows:

//ActivityThread#ApplicationThread.java
 public final void scheduleBindService(IBinder token, Intent intent,boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = newBindServiceData(); s.token = token; s.intent = intent; s.rebind = rebind; ./ / 1.
            sendMessage(H.BIND_SERVICE, s);
        }
Copy the code

Call an internal function:

//ActivityThread.java
    private void sendMessage(int what, Object obj) {
        /** * continue to call the internal overloaded function what = h.launch_activity */

        sendMessage(what, obj, 0.0.false);
    }

    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + "" + mH.codeToString(what)
            + ":" + arg1 + "/" + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        /** * 2. Send what = h.launch_activity message what to H's Handler */
        mH.sendMessage(msg);
    }
Copy the code

BindService is notified by H, just like startService.

//ActivityThread.java
//ActivityThread.java
public final class ActivityThread {
// Initialize class H
final H mH = new H();
  
    /** * H inherits the message management class */ from handler, which is the main thread in the application process
    private class H extends Handler {...public static final int CREATE_SERVICE  = 114; .public void handleMessage(Message msg) {...case BIND_SERVICE:
       / / call handleBindService
				handleBindService((BindServiceData)msg.obj);
       break; . }}}Copy the code

Let’s look at the handleUnbindService implementation:

//ActivityThread.java
    private void handleBindService(BindServiceData data) {
        /** * 1. Obtain Service */ to be bound
        Service s = mServices.get(data.token);
        if(s ! =null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    /** * 2. There is no binding */
                    if(! data.rebind) {/** * 3
                        IBinder binder = s.onBind(data.intent);
                        /** * 4. Call AMS publishService */
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        /** * 5. Call the onRebind lifecycle function */ if it has already been bound
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throwex.rethrowFromSystemServer(); }}catch(Exception e) { ... }}}Copy the code

If data.rebind is false, there is no binding. Perform s.bind (data.intent). Call AMS publishService ();

//AMS.java
    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(! (tokeninstanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token"); } mServices.publishServiceLocked((ServiceRecord)token, intent, service); }}Copy the code

Internally call ActiveServices’ publishServiceLocked function

//ActiveServices.java

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try{...if(r ! =null) {
                Intent.FilterComparison filter
                        = newIntent.FilterComparison(intent); IntentBindRecord b = r.bindings.get(filter); .for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) { ConnectionRecord c = clist.get(i); .try {
                              
                              / / 1.
                                c.conn.connected(r.name, service, false);
                            } catch (Exception e) {
                                ...
                            }
                        }
                    }
                }
                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false); }}finally{ Binder.restoreCallingIdentity(origId); }}Copy the code

The connected function of an SD object of type ServiceDispatcher is called in comment 1 as follows:

//LoadedApk.java
        private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if(sd ! =null) {
                  / / 1.sd.connected(name, service, dead); }}}public void connected(ComponentName name, IBinder service, boolean dead) {
            if(mActivityThread ! =null) {
              / / 2.
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else{ doConnected(name, service, dead); }}Copy the code

The connected function of LoadedApk is called. The connected function of LoadedApk is called. The post function of LoadedApk is called as follows:

//LoadedApk.java

        private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, booleandead) { mName = name; . }public void run(a) {
                if (mCommand == 0) {
                    doConnected(mName, mService, mDead);
                } else if (mCommand == 1) { doDeath(mName, mService); }}}Copy the code

Call the doConnected function as follows:

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {...// If there was an old service, it is now disconnected.
            if(old ! =null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if(service ! =null) {
                /** * 1. Called onServiceConnected function (connected to a connected object). * /mConnection.onServiceConnected(name, service); }}Copy the code

As you can see from comment 1 of the code above, the onServiceConnected callback is called whenever the client is bound with the following code:

        bindService(new Intent(XXXX,XXXX), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.d(TAG,"Binding successful");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
 								Log.d(TAG,"Bind failed, or unbind."); }})Copy the code

At this point, the entire bindService process is analyzed. Finally, a sequence diagram from AMS to Service – connection is given.

conclusion

Here we can divide the service binding into three stages, again represented by a diagram (ps: here is the more difficult bindService process).

reference

  • Android Advanced Decryption