This chapter describes how the four components of Android work. The purpose of this chapter is to deepen the reader’s understanding of how the four components work. Due to the particularity of the four components, it is necessary to have a certain understanding of their working process, which will also help deepen the understanding of Android’s overall architecture.

The operating status of the four components

In addition to BroadcastReceiver, the other three components must be registered in Androidmanifest.xml.

The Activity is responsible for foreground and user interaction. A Service is a computational component that has two states: a startup state and a binding state. A BroadcastReceiver is a messaging component used to transmit messages between different components and different applications. ContentProvider is a data-sharing component used to share data with other components and even other applications.

The working process of an Activity

Activity#startActivityForResult

  @Override
    public void startActivityForResult(
            String who, Intent intent, int requestCode, @Nullable Bundle options) {
        Uri referrer = onProvideReferrer();
        if(referrer ! =null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        options = transferSpringboardActivityOptions(options);
        
        
        // Start execStartActivity with Instrumentation
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, who,
                intent, requestCode, options);
        // Execute sendActivityResult by ActivityThread
        if(ar ! =null) {
            mMainThread.sendActivityResult(
                mToken, who, requestCode,
                ar.getResultCode(), ar.getResultData());
        }
        
        
        cancelInputsAndStartExitTransition(options);
    }
Copy the code

The mInstrumentation is initialized in the handleBindApplication() of the ActivityThread. Pass the Activity in Attach () when it starts.

HandleBindApplication () is called by bindApplication() of ActivityThread$ApplicationThread.

Instrumentation#execStartActivity

    public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, String target,
        Intent intent, int requestCode, Bundle options) {
    // ...
        try {
            // ...
            
            
            / / really start Activity is ActivityManagerNative getDefault
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0.null, options);
                        
            // Check the result of the Activity launch and throw some exceptions
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
Copy the code

Take a look at ActivityManagerNative,

public abstract class ActivityManagerNative extends Binder implements IActivityManager
Copy the code

ActivityManagerService inherits from ActivityManagerNative

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor.BatteryStatsImpl.BatteryCallback
Copy the code

This method returns AMS(ActivityManagerService)

    /** * Retrieve the system's default/global activity manager. */
    static public IActivityManager getDefault(a) {
        return gDefault.get();
    }
Copy the code

GDefault is a Singleton Singleton object that is called create() to create AMS when it is first created.

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create(a) {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager"."default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager"."default service = " + am);
            }
            returnam; }};Copy the code

AMS and ActivityStack call each other

Now the startActivity execution goes into AMS.

The logical order of calls within startActivity() in AMS.

  1. startActivity()
  2. startActivityAsUser()
  3. startActivityMayWait()
  4. startActivityLocked()
  5. startActivityUnchecked()
  6. resumeTargetStackIfNeeded()
  7. ActivityStackSupervisor#resumeFocusedStackTopActivityLocked()
  8. ActivityStack#resumeTopActivityUncheckedLocked()
  9. ActivityStack#resumeTopActivityInnerLocked()
  10. ActivityStackSupervisor#startSpecificActivityLocked()
  11. ActivityStackSupervisor#realStartActivityLocked()

Will eventually go realStartActivityLocked (com). The android. Server. Am. ActivityStackSupervisor# realStartActivityLocked

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

            // ...
            
            
            // This is where the Activity is actually started.
            / / app. The thread is IApplicationThread
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    newConfiguration(task.mOverrideConfig), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, ! andResume, mService.isNextTransitionForward(), profilerInfo);// ...

    

    
        return true;
    }

Copy the code

IApplicationThread and its implementor

IApplicationThread is a Binder interface that contains a number of interfaces for starting and stopping activities and services.

As you might guess, the implementers of IApplicationThread perform a number of functions related to Activity and Service start/stop.

public interface IApplicationThread extends IInterface
Copy the code

The implementor of IApplicationThread is the inner class ApplicationThread of ActivityThread.

private class ApplicationThread extends ApplicationThreadNative
Copy the code
public abstract class ApplicationThreadNative extends Binder
        implements IApplicationThread
Copy the code

ApplicationThreadNative does exactly what the system generates for AIDL files.

ApplicationThreadNative is the implementer of IApplicationThread. Since ApplicationThreadNative is defined by the system as an abstract class, So ApplicationThread becomes the ultimate implementor of IApplicationThread.


android.app.ActivityThread$ApplicationThread#scheduleLaunchActivity

 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            / /...
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
Copy the code
  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);
        }
        mH.sendMessage(msg);
    }

Copy the code

Finally, use mH to send a MSG notification to start the Activity.

MH is a Handler named H.

H Handles the logic for starting the Activity:

case LAUNCH_ACTIVITY: {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

    r.packageInfo = getPackageInfoNoCheck(
    r.activityInfo.applicationInfo, r.compatInfo);
    handleLaunchActivity(r, null."LAUNCH_ACTIVITY");
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
Copy the code

The details of starting the Activity

ActivityThread#handleLaunchActivity().

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // ...
        // Initialize before creating the activity
        WindowManagerGlobal.initialize();
        
        // The Activity object is created by performLaunchActivity
        Activity a = performLaunchActivity(r, customIntent);
        // ...
        if(a ! =null) {
        // ...
            // Start resume
            handleResumeActivity(r.token, false, r.isForward, ! r.activity.mFinished && ! r.startsNotResumed, r.lastProcessedSeq, reason);/ / pause
            if(! r.activity.mFinished && r.startsNotResumed) { performPauseActivityIfNeeded(r, reason);if(r.isPreHoneycomb()) { r.state = oldState; }}}else {
            try {
                // finish Activity
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throwex.rethrowFromSystemServer(); }}}Copy the code

PerformLaunchActivity does five things:

  1. Get component information for the Activity to be started from ActivityClientRecord
  2. Use the class loader to create an Activity object through the newActivity method of Instrumentation
  3. Try to create an Application object through LoadedApk’s makeApplication method
  4. Create the ContextImpl object and initialize some important data using the Attach method of the Activity
  5. Call the Activity’s onCreate method

ActivityThread#performLaunchActivity

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
        // 1. Obtain the component information of the Activity to be started from the ActivityClientRecord
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        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);
        }






        // 2. Use the class loader to create an Activity object through the newActivity method of Instrumentation
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            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) {
            if(! mInstrumentation.onException(activity, e)) {throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ":"+ e.toString(), e); }}try {
            //3. Use LoadedApk's makeApplication method to try to create an Application object
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

         
            // ...   
            
            // 4. Create ContextImpl and initialize some important data using the Attach method of the Activity
            if(activity ! =null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                // ...
                
                // ContextImpl connects the attach and Activity with the Activty
                // In attach, the Activity completes the creation of the Window and associates it with the Window.
                // So that the Window receives an external input event and passes it to the Activity
                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);

                // ...
                
                // 5. Call the Activity's onCreate method,
                // Now the Activity is started
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
               // ...
              
            mActivities.put(r.token, r);

        // ...
        return activity;
    }
Copy the code

NewActivity () creates an Activity object through the class loader

android.app.Instrumentation#newActivity

    public Activity newActivity(Class
        clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, 
            IllegalAccessException {
        Activity activity = (Activity)clazz.newInstance();
        ActivityThread aThread = null;
        activity.attach(context, aThread, this, token, 0, application, intent,
                info, title, parent, id,
                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                new Configuration(), null.null.null);
        return activity;
    }
Copy the code

android.app.LoadedApk#makeApplication

 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if(mApplication ! =null) {
        // An app can only have one Application
            return mApplication;
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            if(! mPackageName.equals("android")) {
                initializeJavaContextClassLoader();
            }
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
            // create an object with the newApplication of Instrumentation. It's also loaded with the class loader.
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            // ...
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if(instrumentation ! =null) {
            instrumentation.callApplicationOnCreate(app);
        }

        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
                .getAssignedPackageIdentifiers();
        final int N = packageIdentifiers.size();
        for (int i = 0; i < N; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
        }

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        return app;
    }

Copy the code

The working process of a Service

A Service has two working states:

  1. The startup state is used to perform background computing
  2. Binding state, mainly used for interaction between other components and services

These two states can coexist.

Intent intentService = new Intent(this,MyService.class);
/ / start
startService(intentService);
/ / binding
bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);
Copy the code

The boot process

Start with ContextWrapper’s startService.

public ComponentName startService(Intent service) {
    return mBase.startService(service);
}

Copy the code

The Activity is created by attaching () to a ContextImpl object, which is the mBase above.

Most of the implementation of ContextWrapper is implemented by mBase. This is bridge mode.

StartServiceCommon (); startServiceCommon();

android.app.ContextImpl#startServiceCommon

   private ComponentName startServiceCommon(Intent service, UserHandle user) {
        
        validateServiceIntent(service);
        service.prepareToLeaveProcess(this);
        
        // Call startService remotely from AMS
        ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(),
                service,service.resolveTypeIfNeeded(getContentResolver()), getOpPackageName(),
                user.getIdentifier());
            / /...
            return cn;
        
    }
Copy the code

com.android.server.am.ActivityManagerService#startService


    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // Refuse possible leaked file descriptors
       // ...

     
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            // mServices is ActiveService, assisting AMS management Service.
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            returnres; }}Copy the code

The call chain inside AMS’s startService().

  1. ActiveService#startServiceLocked()
  2. ActiveService#startServiceInnerLocked()
  3. ActiveService#bringUpServiceLocked()
  4. ActiveService#sendServiceArgsLocked()
  5. ActiveService#realStartServiceLocked()

com.android.server.am.ActiveService#startServiceInnerLocked

// ServiceRecord describes a Service record that runs through the Service startup process.
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;
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        
        // Give the startup details to bringUpServiceLocked
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false.false);
        if(error ! =null) {
            return new ComponentName("!!!!!", error);
        }

        if (r.startRequested && addToStarting) {
            boolean first = smap.mStartingBackground.size() == 0;
            smap.mStartingBackground.add(r);
            r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
            // ... 
            if(first) { smap.rescheduleDelayedStarts(); }}else if (callerFg) {
            smap.ensureNotStartingBackground(r);
        }

        return r.name;
    }
Copy the code

com.android.server.am.ActiveService#realStartServiceLocked

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
       
       // ... 
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();

        final boolean newService = app.services.add(r);
        bumpServiceExecutingLocked(r, execInFg, "create");
        mAm.updateLruProcessLocked(app, false.null);
        mAm.updateOomAdjLocked();

        boolean created = false;
        try {
            // ... 
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked();
            }
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            
            
            
            
            
            // Create a Service object through the scheduleCreateService of ActivityThread
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            // ...
        } finally {
            // ...
        }

        if (r.whitelistManager) {
            app.whitelistManager = true;
        }

        requestServiceBindingsLocked(r, execInFg);

        updateServiceClientActivitiesLocked(app, null.true);

        // If the service is in the started state, and there are no
        // pending arguments, then fake up one so its onStartCommand() will
        // be called.
        if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                    null.null));
        }
        
        // Call other methods of the Service. For example, onStartCommand.
        sendServiceArgsLocked(r, execInFg, true);

       // ...
    }

Copy the code

MSG is sent via Handler just like an Activity.

ActivityThread#scheduleCreateService

        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);
        }
Copy the code

ActivityThread#handleCreateService


    private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            // Create an instance of the Service through the class loader
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
           // ... 
        }

        try {
          
            
            // Create ContextImpl object
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            / / create the Application
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            // Attach the Service object to the ContextImpl object.
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            // 调用 service 的 onCreate
            service.onCreate();
            
            // Put the service into the collection
            mServices.put(data.token, service);
            try {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
            } catch (RemoteException e) {
                
            }
        } 
        // ...
    }
Copy the code

ActivieService#sendServiceArgsLocked() internally calls the scheduleServiceArgs() of ActivityThread, which eventually calls handleServiceArgs()

android.app.ActivityThread#handleServiceArgs

    private void handleServiceArgs(ServiceArgsData data) {
        Service s = mServices.get(data.token);
       
        if(data.args ! =null) {
            data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
        }
        int res;
        if(! data.taskRemoved) {// Call service onStartCommand()
            res = s.onStartCommand(data.args, data.flags, data.startId);
        } else {
            s.onTaskRemoved(data.args);
            res = Service.START_TASK_REMOVED_COMPLETE;
        }

        QueuedWork.waitToFinish();

                
        ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token,
                            SERVICE_DONE_EXECUTING_START,
                            data.startId, res);
        ensureJitEnabled();
            
}
Copy the code

Service binding process

It starts with bindServiceCommon() in ContextImpl as the startup process does.

ContextImpl#bindServiceCommon

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        IServiceConnection sd;
       
        / / client ServiceConnection object into ServiceDispatcher. InnerConnection.
        // Because service bindings may be cross-process, the ServiceConnection object must be invoked
        // Binder allows remote services to call back their own methods.
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
    
        
        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);
        
        // 调用 AMS 的 bindService
        int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
        returnres ! =0;
    }

Copy the code

android.app.LoadedApk#getServiceDispatcher

    
    
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices;
    
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        // mService is a map
        synchronized (mServices) {
            // If there is a map, get it from the map. If there is no map, create it and save it.
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> 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<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                
                sd.validate(context, handler);
            }
            // Return the internal IServiceConnection object
            returnsd.getIServiceConnection(); }}Copy the code

ActivityManagerService#bindService

    public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // ...
        synchronized(this) {
            returnmServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code

AMS call process:

  1. bindService()
  2. ActiveServices#bindServiceLocked()
  3. ActiveServices#bringUpServiceLocked()
  4. ActiveServices#realStartServiceLocked()
  5. ActiveServices#requestServiceBindingsLocked()
  6. ActiveServices#requestServiceBindingLocked()
  7. ActivityThread$ApplicationThread#scheduleBindService()

And then similar to the startup process. Both end up calling ActivityThread to create the Service instance and execute onCreate().

Service binding process is called ActiveServices requestServiceBindingsLocked (). ScheduleBindService () of ActivityThread$ApplicationThread is finally called. We then turn the H Handler to the ActivityThread’s handleBindService().

ActivityThread#handleBindService()

 private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
     
          
        data.intent.setExtrasClassLoader(s.getClassLoader());
        data.intent.prepareToEnterProcess();
       
        if(! data.rebind) {// Call the Service's onBind method
            IBinder binder = s.onBind(data.intent);
            // call AMS publishService. Notifies the client that the connection has been successful
            ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
        } else {
            // Call the onReBind method of the Service
            s.onRebind(data.intent);
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
        }
        ensureJitEnabled();
    }

Copy the code

Notifies the client that the binding is successful

AMS 的 publishService() 调用了 ActiveServices#publishServiceLocked()

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
        IntentBindRecord b = r.bindings.get(filter);
        if(b ! =null && !b.received) {
            b.binder = service;
            b.requested = true;
            b.received = true;
            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);
                
                    / / c is ConnectionRecord
                    / / conn LoadedApk. ServiceDispatcher. InnerConnection
                    // Service is the Binder returned by service onBind()
                    c.conn.connected(r.name, service);
                }
            }
        }

        serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
    }

Copy the code

Take a look at InnerConnection:

LoadedApk.ServiceDispatcher.InnerConnection

 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) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if(sd ! =null) {
                    Connected of ServiceDispatcher is calledsd.connected(name, service); }}}Copy the code

LoadedApk.ServiceDispatcher#connected

public void connected(ComponentName name, IBinder service) {
    if(mActivityThread ! =null) {
        // mActivityThread is the H in ActivityThread and will not be empty
        // So RunConnection will run in the main thread, so the client
        Methods in ServiceConnection are called back in the main thread.
        // doConnected is also called inside RunConnection
        mActivityThread.post(new RunConnection(name, service, 0));
    } else{ doConnected(name, service); }}Copy the code

LoadedApk.ServiceDispatcher#doConnected

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

            synchronized (this) {
              
                old = mActiveConnections.get(name);
                
                if(old ! =null && old.binder == service) {
                    // Huh, already have this one. Oh well!
                    // just return the same as before
                    return;
                }

                if(service ! =null) {
                    // The new service is connected
                    // A new service is being connected... set it all up.
                    info = new ConnectionInfo();
                    info.binder = service;
                    info.deathMonitor = new DeathMonitor(name, service);
                  
                    service.linkToDeath(info.deathMonitor, 0);
                    mActiveConnections.put(name, info);
                } 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 now disconnected.
            if(old ! =null) {
                // Disconnect the old one
                mConnection.onServiceDisconnected(name);
            }
            // If there is a new service, it is now connected.
            if(service ! =null) {
                // New connectionmConnection.onServiceConnected(name, service); }}Copy the code

So the binding will only bind once.

The execution of the system is similar to the process of stopping and unbinding.

The working process of the BroadcastReceiver

Mainly includes:

  1. The registration process for broadcasts
  2. The sending and receiving of a broadcast

Defining broadcast:

public class MyBroadcastReceiver extends BroadcastReceiver {
  // Do not perform time-consuming operations here. Reference value: within 10s
  @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); }}Copy the code

Registered broadcast:

  1. Static registration
<receiver android:name=".MyBroadcastReceiver">
    <intent-filter>
        <action android:name="me.luwenjie.chapter9.receiver"/>
    </intent-filter>
</receiver>
Copy the code
  1. Dynamic registration
IntentFilter filter = new IntentFilter();
filter.addAction("me.luwenjie.chapter9.receiver");
registerReceiver(new MyBroadcastReceiver(),filter);
Copy the code

Send broadcast

Intent intent =  new Intent("me.luwenjie.chapter9.receiver");
sendBroadcast(intent);
Copy the code

The registration process

Static registered broadcasts are automatically registered by the system during application installation, and the PackageManagerServcie (PMS) completes the registration process.

Dynamic registration starts with registerReceiver() for ContextWrapper. The interior is implemented by ContextImpl.

Call chain:

 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        
            if(mPackageInfo ! =null&& context ! =null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                // Get the IIntentReceiver object from mPackageInfo
                // This is an IPC, BroadcastReceiver as Android
                // A component cannot be passed directly across processes, but through IIntentReceiver
                / / transshipment. IIntentReceiver is a Binder interface.
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null.true).getIIntentReceiver();
            }
        
        
            final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
            if(intent ! =null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        
    }

Copy the code

LoadedApk#getReceiverDispatcher

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
        synchronized (mReceivers) {
            LoadedApk.ReceiverDispatcher rd = null;
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
            
            // If you are registered, look it up from the storage
            if (registered) {
                map = mReceivers.get(context);
                if(map ! =null) { rd = map.get(r); }}// Otherwise create a new storage
            if (rd == null) {
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) {
                    if (map == null) {
                        map = newArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>(); mReceivers.put(context, map); } map.put(r, rd); }}else {
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            returnrd.getIIntentReceiver(); }}Copy the code

AMS#registerReceiver

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
       

    
        // ...
     
        synchronized (this) {
            // ...
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                // ...
                // Store the remote IIntentReceiver object
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } 
            // ... 
            
            
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            // Store the IntentFilter object
            mReceiverResolver.addFilter(bf);

            // ...

            returnsticky; }}Copy the code

Sending and receiving processes

ContextImpl#sendBroadcast

    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
     
            intent.prepareToLeaveProcess(this);
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null.null.null, AppOpsManager.OP_NONE, null.false.false,
                    getUserId());
    
    }
Copy the code

AMS finally calls broadcastIntentLocked() inside

Since Android 3.1, the system has added two flags for intEnts:

  1. FLAG_INCLUDE_STOPPED_PACKAGES

    Contains stopped applications, and broadcasts are sent to stopped applications

  2. FLAG_EXCLUDE_STOPPED_PACKAGES

    Does not include stopped applications. Broadcasts are not sent to stopped applications

FLAG_EXCLUDE_STOPPED_PACKAGES is added by default for all broadcasts.

    final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);

        // By default broadcasts do not go to stopped apps.
        // By default, broadcasts are not sent to stopped applications
        //
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

        
        // Find the matching broadcast receiver according to intentFilter and filter it through a series of criteria.
        // Add qualified broadcast receivers to the BroadcastQueue
        // The broadcast is sent to the appropriate broadcast receiver.
        
        
        // ... 
     
          
        if((receivers ! =null && receivers.size() > 0) || resultTo ! =null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId);

            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if(! replaced) { queue.enqueueOrderedBroadcastLocked(r);// Send the broadcast to the appropriate receiverqueue.scheduleBroadcastsLocked(); }}else {
            // There was nobody interested in the broadcast, but we still want to record
            // that it happened.
            if (intent.getComponent() == null && intent.getPackage() == null
                    && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                // This was an implicit broadcast... let's record it for posterity.
                addBroadcastStatLocked(intent.getAction(), callerPackage, 0.0.0); }}return ActivityManager.BROADCAST_SUCCESS;
    }

Copy the code

com.android.server.am.BroadcastQueue#scheduleBroadcastsLocked


    public void scheduleBroadcastsLocked(a) {
        if (mBroadcastsScheduled) {
            return;
        }
        // Send a MSG to the internal Handler. This Handler runs on the main thread
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
Copy the code

com.android.server.am.BroadcastQueue#processNextBroadcast

    final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;

            
            mService.updateCpuStats();

            if (fromMsg) {
                mBroadcastsScheduled = false;
            }

            // First, deliver any non-serialized broadcasts right away.
            // Distribute an unordered broadcast first. Unordered broadcasts are stored in mParallelBroadcasts.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                final int N = r.receivers.size();
                / / traverse
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    // Distribute the broadcast
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "]" + r);
            }

    / /...
    }

Copy the code

The final call

LoadedApk$ReceiverDispatcher#performReceive


    public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                
            // Create an Args object
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
             // ...
             
             // Execute the logic in arGS through the H in ActivityThread
            if (intent == null| |! mActivityThread.post(args)) {if(mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); args.sendFinished(mgr); }}}}Copy the code

The run of Arg () :

public void run(a) {
                // ... 
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    // call onReceive
                    receiver.onReceive(mContext, intent);
                }
                
                if(receiver.getPendingResult() ! =null) { finish(); }}}Copy the code

How the ContentProvider works

ContentProvider is a content sharing component that provides data to other components or applications through Binder.

Start the

When the ContentProvider process starts, the ContenProvider is started and published to AMS at the same time.

At this time of ContentProvideronCreate()Should precede ApplicationonCreate()The execution.

Code snippet from ActivityThread#handleBindApplication:


/ /...

// Initialize the ContentProviders
if(! ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers);// For process that contains content providers, we want to
    // ensure that the JIT is enabled "at some point".
    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
            

// ...

// Then call application onCreate
mInstrumentation.callApplicationOnCreate(app);
Copy the code

After the ContentProvider is started, the outside world can manipulate the data in the ContentProvider through the four interfaces it provides. Binder calls insert, DELETE, Update, and Query.

The external world cannot access ContentProvider directly. AMS can only obtain IContentProvider, the Binder interface of ContentProvider, according to Uri, and then access internal data through it.

A single instance

In general, contentProviders are single instances. This is determined by the attribute Android: multiProcess, where false is a single instance and the default is false.

When multiple instances are enabled, there is a ContentProvider object in each caller’s process. In the actual development, there is no specific use scenario of multi-instance, which is explained by the official explanation to reduce the overhead of inter-process communication.

Access to the ContentProvider

A ContentResolver is required. This is an abstract class. The implementation class is $ApplicationContentResolver ContextImpl

public abstract class ContentResolver
Copy the code

When the thread in which the ContentProvider is created is started, the first access to it triggers the creation of the ContentProvider. All four methods can be fired through the ContentResolver.

ContextImpl$ApplicationContentResolver#query

 public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder,
            @Nullable CancellationSignal cancellationSignal) {
        / /...
        
        
        
        try {
            // ... 
          
            
            // Wrap the cursor object into CursorWrapperInner object.
            // Get the IContentProvider object
        
            finalIContentProvider provider = (stableProvider ! =null)? stableProvider : acquireProvider(uri);// ...
           
            return wrapper;
        } catch (RemoteException e) {
        } finally{}}Copy the code

AcquireProvider () finally calls ActivityThread’s acquireProvider().

ActivityThread#acquireProvider

 public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        
        // Check if it already exists
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if(provider ! =null) {
            return provider;
        }

    
        // When two processes request at the same time, only the first one is guaranteed to succeed. The lock cannot be used because the request takes a long time and the same process may re-enter the lock.
        
        
        // Send an interprocess request to AMS to start if it does not find it
        IActivityManager.ContentProviderHolder holder = null;
        try {
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        }
    

        // Install provider will increment the reference count for us, and break
        // any ties in the race.
        
        // Modify the reference count
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

Copy the code

How does AMS start a ContentProvider?

The getContentProvider() above calls getContentProviderImpl()

AMS#getContentProviderImpl

    private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
// ... 
// Start the process with startProcessLocked
proc = startProcessLocked(cpi.processName,
                            cpr.appInfo,
                            false.0."content provider".new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false.false.false);
       // ...                  
        returncpr ! =null ? cpr.newHolder(conn) : null;
    }


Copy the code

Inside startProcessLocked(), a new process is started by Progress’s start().

The entry method after a new process starts is the main() of ActivityThread. Then do the same startup process as above.

Complete process