Android four components communication core

preface

Series of articles:

Display process from Android Activity creation to View
Android four components communication core

We know that the four components of Android: Activity/Service/Broadcast/ContentProvider can conduct cross process communication, which are the ability to realize cross process communication with Binder. There are countless connections between the four. This paper will analyze the communication core, connection and difference of the four from a macro perspective.
Through this article, you will learn:

1. Communication foundation of four components
2. Activity interacts with AMS
3. Service interaction with AMS
4. Interaction between Broadcast and AMS
5. ContentProvider interacts with AMS

1. Communication foundation of four components

Basic functions of four components

Let's take a look at the basic functions of the four components:

How do the four interact?

As can be seen from the figure, as long as you get the Context object, you can enable the functions of the four components, which shows the position of Context in Android.
For Context, please move to: Past and present life of Android Context

When we go deeper into the source code in Context, take Context.startService(xx) as an example, we will call ContextImpl.startService(xx), and then call ContextImpl.startServiceCommon(xx).
The implementation of startServiceCommon(xx) method is different before and after Android 8.0.
Take the implementation before Android 8.0 as an example:

#ContextImpl.java
    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //The method in ActivityManagerNative is called
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                    mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

ActivityManagerNative.getDefault() retrieves data from the singleton. This method returns the IActivityManager interface, and all exposed methods are declared in the IActivityManager interface. The actual object is the ActivityManagerProxy instance, and the ActivityManagerProxy implements the IActivityManager interface. When calling the method in the ActivityManagerProxy, it actually calls the Binder driver through BinderProxy.transact(xx) for cross process communication, and the class that receives the call, that is, the class that implements onTransact(xx) is ActivityManagerNative, It is an abstract class, and its subclass is implemented as ActivityManagerService.

Let's take the implementation after Android 8.0 (included) as an example:

#ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
                                             UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //The method in ActivityManagerService is called
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                    getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

Similarly, ActivityManager.getService() also obtains data from the singleton. This method returns the iaactivitymanager interface. The actual object is IActivityManager.Proxy, which implements the IActivityManager interface. When calling the methods in IActivityManager.Proxy, it actually calls the Binder driver through BinderProxy.transact(xx) for cross process communication, and the class that receives the call, that is, the class that implements onTransact(xx), is IActivityManager.Stub, It is an abstract class, and its subclass is implemented as ActivityManagerService.

The difference between the two is shown in the figure below:

ActivityManagerService runs in system_ In the server process; Service manager runs in a separate process; The application runs in another process. Therefore, the above figure involves three processes.

1. ActivityManagerService registers the IBinder reference in ServiceManager. This is step 1. This process is an IPC.
2. When the application process wants to obtain the functions provided by the ActivityManagerService, it needs to query the ServiceManager. This is step 2. This process is an IPC (after taking it once, cache it, and don't use IPC again next time)
3. After obtaining the IBinder reference, the application process looks for the corresponding interface IActivityManager, and then calls the method provided by ActivityManagerService. This is step 3. This process is an IPC.

It can be seen that although the ways to obtain the IActivityManager interface are different before and after Android 8.0, they all have to go through the above three steps. Only after Android 8.0, the automatic code generation function of AIDL is used to replace ActivityManagerProxy(IActivityManager.Proxy), ActivityManagerNative(IActivityManager.Stub) and IActivityManager(AIDL automatically generates this class), which facilitates coding.
For more comparison between AIDL and native Binder, please move to: AIDL application of Android IPC (Part I)

Four component communication bridge

Taking starting Service as an example, the above describes the relationship between the application process and ServiceManager and ActivityManagerService(AMS). The actual ultimate purpose is to call AMS functions. Let's briefly see what functions AMS provides:

        #IActivityManager.aidl
    {
        //Bind Application
        void attachApplication(in IApplicationThread app, long startSeq);
        //Start service
        ComponentName startService(in IApplicationThread caller, in Intent service,
        in String resolvedType, boolean requireForeground, in String callingPackage,
        in String callingFeatureId, int userId);

        //Binding service
        int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
        in String resolvedType, in IServiceConnection connection, int flags,
        in String callingPackage, int userId);

        //Publishing services
        void publishService(in IBinder token, in Intent intent, in IBinder service);

        //Start Activity
        int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
        in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
        int flags, in ProfilerInfo profilerInfo, in Bundle options);

        //Send broadcast
        int broadcastIntent(in IApplicationThread caller, in Intent intent,
        in String resolvedType, in IIntentReceiver resultTo, int resultCode,
        in String resultData, in Bundle map, in String[] requiredPermissions,
        int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);

        //Get contentProvider
        ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,
        in String name, int userId, boolean stable);

        //Publish contentProvider
        void publishContentProviders(in IApplicationThread caller,
        in List<ContentProviderHolder> providers);
    }

Only some methods related to the four components are listed, and AMS also provides many other methods.
You may have found that the first parameter of each method is "in iaapplicationthread caller". This parameter is very important and we will analyze it later.
It can be seen from the above methods that the four components need to deal with AMS, and AMS controls their life cycle. Therefore, AMS is also called "housekeeper":

Next, we analyze how the four components communicate with themselves (other processes) through AMS.

2. Activity interacts with AMS

There are now two activities, AMSActivity and AMSTargetActivity.
AMSActivity to start AMSTargetActivity, call the following method:

    public static void start(Context context) {
        //Context is AMSActivity
        Intent intent = new Intent(context, AMSTargetActivity.class);
        context.startActivity(intent);
    }

The call stack of this method in AMSActivity process is as follows:

Activity.startActivity-->Activity.startActivityForResult
-->FragmentActivity.startActivityForResult
-->Instrumentation.execStartActivity
-->ActivityTaskManager.getService().startActivity(xx)

ActivityTaskManager.getService() has been analyzed before, that is, get the AMS external interface: IActivityManager, get the reference after calling AMS startActivity(xx) method:

#ActivityManagerService.java
   public int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
    }

Focus on two parameters: iaapplicationthread caller and Intent intent.
Intent is familiar to us. It indicates which Activity to start.
Iaapplicationthread is an interface in which many methods are defined, such as:

#IApplicationThread.java
    {
        //Callback static broadcast
        void scheduleReceiver(in Intent intent, in ActivityInfo info,
        in CompatibilityInfo compatInfo,
        int resultCode, in String data, in Bundle extras, boolean sync,
        int sendingUser, int processState);
        @UnsupportedAppUsage
        //Callback creation service
        void scheduleCreateService(IBinder token, in ServiceInfo info,
        in CompatibilityInfo compatInfo, int processState);
        @UnsupportedAppUsage
        //Callback stop service
        void scheduleStopService(IBinder token);
        //Callback binding Application
        void bindApplication(in String packageName, in ApplicationInfo info,
        in ProviderInfoList providerList, in ComponentName testName,
        in ProfilerInfo profilerInfo, in Bundle testArguments,
        IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
        int debugMode, boolean enableBinderTracking, boolean trackAllocation,
        boolean restrictedBackupMode, boolean persistent, in Configuration config,
        in CompatibilityInfo compatInfo, in Map services,
        in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
        in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges);
        //Callback Service onStartCommand
        void scheduleServiceArgs(IBinder token, in ParceledListSlice args);
        //Callback binding service
        void scheduleBindService(IBinder token,
            in Intent intent, boolean rebind, int processState);
        @UnsupportedAppUsage
        //Callback unbinding service
        void scheduleUnbindService(IBinder token,
            in Intent intent);

        //Callback Activity lifecycle related
        void scheduleTransaction(in ClientTransaction transaction);
    }

This interface is implemented in ActivityThread.java (AIDL definition is used after Android 8.0, which is analyzed here as an example).

#ActivityThread.java
    private class ApplicationThread extends IApplicationThread.Stub {
        ...
        //Implements the methods defined in the iaapplicationthread interface
    }

What's the use of iaapplicationthread? After we call AMS methods, some methods do not return a value or only return int, such as startActivity(xx). How can our process receive the callback of the Activity's life cycle, not only the Activity's life cycle callback, but also the Service callback. At this time, you have to rely on the iaapplicationthread interface callback.
Therefore, when AMS method is called, the iaapplicationthread instance is passed in. When AMS completes an action, it is called back to the application process through iaapplicationthread.
In fact, only one iaapplicationthread instance is saved for each application process in AMS, and it is first passed to AMS when the process starts:

#ActivityThread.java
    private void attach(boolean system, long startSeq) {
        ...
        if (!system) {
            //Get AMS reference
            final IActivityManager mgr = ActivityManager.getService();
            try {
                //Pass in the iaapplicationthread instance to AMS
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
        ...
    }

At this time, AMS saves the iaapplicationthread instance. When an application process calls the AMS method later, it will also pass in the iaapplicationthread instance. AMS will directly take it out for use by finding out whether there is an instance.

After calling AMS startActivity(), AMS will detect whether the process of the target Activity is alive. If not, start the process. If yes, move the Activity to the top of the stack, and then process the activities that were moved out of the top of the stack. After these operations, it is necessary to notify the involved application processes, and this notification is called back through iaapplicationthread.
Assuming AMSActivity and AMSTargetActivity are in the same process, AMS will call back the scheduleTransaction() method in iaapplicationthread:

    #ApplicationThread
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        ActivityThread.this.scheduleTransaction(transaction);
    }

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                //Finally, methods such as handleLaunchActivity() in ActivityThread are called
                //Then call back onCreate/onPause/onResume and other methods of the target Activity
                mTransactionExecutor.execute(transaction);
                break;
        }

    #ClientTransactionHandler.java
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

It can be seen that after the AMS callback, it is sent to the main thread for execution through the Handler. When the Handler processes the Message, it will call the onCreate()/onResume() / and other methods of the Activity.
This is why we often say that the rewriting methods of Activity are executed in the main thread.

It can be seen from the above that after the application process initiates the startActivity action, AMS manages the life cycle of the target Activity. We only need to rewrite the corresponding method of the target Activity in the application process and process the corresponding logic in it to realize the function of an Activity jump.

3. Service interaction with AMS

Start start Service

    Intent intent = new Intent(AMSActivity.this, AMSTargetService.class);
    startService(intent);

The call stack is as follows:

ContextWrapper.startService-->
ContextImpl.startServiceCommon
-->ActivityManager.getService().startService(xx)

The AMS interface is still obtained first, and then startService(xx) is called:

#ContextImpl.java
public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage,
            String callingFeatureId, int userId)

Suppose AMSActivity and AMSTargetService are in the same process.
After receiving the startService request, AMS will find the target Service. If the Service has not been created, AMS will eventually call back the iaapplicationthread methods scheduleCreateService(xx) and scheduleServiceArgs(xx). These methods are processed as follows:

#ActivityThread.java
    public final void scheduleCreateService(IBinder token,
                                            ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
        ...
        //Switch to main thread execution
        sendMessage(H.CREATE_SERVICE, s);
    }

    
    public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
        List<ServiceStartArgs> list = args.getList();

        for (int i = 0; i < list.size(); i++) {
            ...
            //Switch to main thread execution
            sendMessage(H.SERVICE_ARGS, s);
        }
    }

    public void handleMessage(Message msg) {
        switch (msg.what) {
            case CREATE_SERVICE:
                //1. Create a Service instance in this method
                //2. Callback Service onCreate method
                handleCreateService((CreateServiceData)msg.obj);
                break;

            case SERVICE_ARGS:
                //Callback onStartCommand method
                handleServiceArgs((ServiceArgsData)msg.obj);
                break;
        }
    }

Similar to Activity, the Service Lifecycle callback is finally switched to the main thread for execution.
This is why we often say that time-consuming tasks cannot be executed in the Service, otherwise ANR is easy to occur, because the callback method is executed in the main thread.

bind start Service

Assuming that AMSActivity and AMSTargetService are not in the same process, take the process of AMSActivity as the client and the process of AMSTargetService as the server, as shown in the following figure:

The premise is that the client and server have been bound to AMS (iaapplicationthread is passed)

1. The client initiates a binding request.
2. AMS looks for the target Service, creates the server-side Service through the iaapplicationthread callback scheduleCreateService, and binds the Service through scheduleBindService.
3. The server creates and binds successfully, and passes the IBinder to AMS.
4. AMS receives the IBinder reference and tells the client that the service binding is successful through the IServiceConnection callback connected method, that is, callback the ServiceConnection.onServiceConnected method registered during client binding.

4. Interaction between Broadcast and AMS

Static registration

There are two registration methods for broadcasting: static and dynamic.
First analyze the static registration:

Declare the broadcast in AndroidManifest.xml, and specify the received Action and the class that handles the Action.

As follows:

        <receiver android:name=".ams.AMSTargetBroadcast">
            <intent-filter>
                <action android:name="my_action"></action>
            </intent-filter>
        </receiver>

Send a broadcast in AMSActivity. The call stack for sending a broadcast is as follows:

ContextWrapper.sendBroadcast-->ContextImpl.sendBroadcast
-->ActivityManager.getService().broadcastIntent(xx)

The AMS interface is still obtained first, and then broadcastIntent(xx) is called:

#ContextImpl.java
int broadcastIntent(in IApplicationThread caller, in Intent intent,
        in String resolvedType, in IIntentReceiver resultTo, int resultCode,
        in String resultData, in Bundle map, in String[] requiredPermissions,
        int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);

Suppose AMSActivity and AMSTargetBroadcast are in the same process.
After receiving the broadcastIntent request, AMS forwards it to BroadcastQueue for processing. If the broadcast receiver is statically registered, it will call back the iaapplicationthread schedulereceiver method. The following process is similar to Activity/Service:
After receiving the scheduleReceiver callback, the process where AMSActivity is located switches to the main thread, then constructs the AMSTargetBroadcast instance by reflection, and calls the onReceive(xx) method. The onReceive(xx) method is exactly the processing logic we have rewritten after receiving the broadcast.
Therefore, the onReceive(xx) method is also executed in the main thread and cannot perform time-consuming operations, otherwise ANR is easy to occur

Dynamic registration

Dynamic registration is as follows:

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(AMSTargetBroadcast.MY_ACTION);
        registerReceiver(new AMSTargetBroadcast(), intentFilter);

registerReceiver(xx) is finally called to:

#ContextImpl.java
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                                            IntentFilter filter, String broadcastPermission,
                                            Handler scheduler, Context context, int flags) {
        //AIDL implementation
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                //Construct receiver callback
                rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                        mMainThread.getInstrumentation(), true);
            } else {
                ...
            }
        }
        try {
            //Register with AMS
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            ...
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

Before registering with AMS, the IIntentReceiver object is constructed. The interface is declared by AIDL, that is, a callback interface is registered with AMS. When AMS receives the request to send a broadcast, it is found that it is dynamically registered, so it calls the onReceive(xx) method in BroadcastReceiver by calling the performReceive(xx) method of IIntentReceiver interface, I don't seem to see switching to the main thread for execution? See the processing of IIntentReceiver performReceive(xx):

#LoadedApk.java
    public void performReceive(Intent intent, int resultCode, String data,
                               Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
        ...
        //Mmactivitythread.post switches to the main thread for execution
        if (intent == null || !mActivityThread.post(args.getRunnable())) {
            ...
        }
    }

    public final Runnable getRunnable() {
        return () -> {
            final BroadcastReceiver receiver = mReceiver;
            ...
            try {
                ...
                //BroadcastReceiver passed in at registration
                receiver.onReceive(mContext, intent);
            } catch (Exception e) {
                ...
            }
        };
    }

Obviously, this is a switch to the main thread.
It can be seen that whether static registration or dynamic registration, the onReceive(xx) method is finally called back in the main thread.
The interaction diagram between Broadcast and AMS is as follows:

It can also be concluded from the above:

1. Every time you send a broadcast, you need to go through the IPC twice (requesting AMS/AMS callback). Therefore, if the broadcast is only sent / received in the same process, it is not necessary to use the broadcast. It is recommended to use the local broadcast: LocalBroadcastManager.
2. If the broadcast is statically registered, AMS will re create the BroadcastReceiver instance every time it calls back. Therefore, static registration is not recommended when the broadcast is sent / received frequently, and dynamic registration is recommended.

5. ContentProvider interacts with AMS

ContentProvider as its name implies: content provider.
Taking a typical mobile phone address book as an example, how can the address book be provided to other processes to use its data? According to the previous experience, the address book needs to expose an external interface. If an external program wants to use the address book, it has to get the exposed interface.
Next, let's see how to implement a custom ContentProvider:

Declare ContentProvider

#AndroidManifest.xml
        <provider
            android:authorities="com.fish.AMSTargetProvider"
            android:name=".ams.AMSTargetProvider">
        </provider>
public class AMSTargetProvider extends ContentProvider {
    public static String AUTHORITY = "com.fish.AMSTargetProvider";
    private static int MATCH_CODE = 1000;
    private static UriMatcher uriMatcher;
    ...
    //Rewrite the addition, deletion, modification and query methods to deal with specific logic
}

With interfaces and processing logic, you need to show your ability. Of course, we don't need to complete this process. The system processes it automatically.

Publish ContentProvider to AMS

Remember that after the Application process is started, it will bind (register) iaapplicationthread to AMS. After successful binding, it will call back the Application process through the bindApplication(xx) of iaapplicationthread, and then switch to the main thread to execute handleBindApplication(xx). In this method, it will create an Application instance, and then process the Provider declared in AndroidManifest.xml.
The call stack is as follows:

ActivityThread.handleBindApplication-->ActivityThread.installContentProviders
-->ActivityManager.getService().publishContentProviders(xx)

In ActivityThread.installContentProviders, ContentProvider is instantiated and its reference is saved to Map, and finally AMS publishContentProviders(xx) is passed to AMS.

#AMS
 public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers)

ContentProviderHolder implements the Parcelable interface, so it can be passed across processes, and it internally holds IContentProvider member variables. You may wonder here that IContentProvider cannot be passed across processes! In fact, IContentProvider is just an interface. Its specific implementation class is ContentProvider.Transport. Its declaration is as follows:

#ContentProvider.java
class Transport extends ContentProviderNative {
    ...
}

#ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {
    ...
}

Therefore, ContentProvider.Transport can be passed across processes.

Get ContentProvider

Relevant information about ContentProvider has been stored in AMS. When a process needs to use ContentProvider, take inserting data as an example. The use method is as follows:

    {
        ContentValues contentValues = new ContentValues();
        getContentResolver().insert(uri, contentValues);
    }

When calling the insert(xx) method, the call stack is as follows:

ContentResolver.insert-->ContentResolver.acquireProvider-->ApplicationContentResolver.acquireProvider
-->ActivityThread.acquireProvider-->ActivityThread.acquireExistingProvider
-->ActivityManager.getService().getContentProvider(xx)

Among them, ActivityThread.acquireExistingProvider will first query whether there is a ContentProvider cached locally. If so, it will directly return (corresponding to the AMSTargetProvider instance in the same process as the caller). At this time, it will directly return the ContentProvider instance without querying AMS.

For different processes, you need to query the ContentProvider through AMS:

public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String callingPackage, String name, int userId,
            boolean stable) 

If AMS has cached the ContentProvider before, return directly. Otherwise, check whether the target process is alive. If not, pull it up and take the ContentProvider.
One thing to note is:

The remote ContentProvider will also be cached, but releaseProvider(xx) will be called at the end of the insert()/delete()/query() method in ContentResolver to release the cache. Therefore, the remote ContentProvider is retrieved through AMS every time.

The interaction between ContentProvider and AMS is shown in the figure:

ContentProvider data change

When the ContentProvider data changes, it needs to notify the listener. If other processes want to listen for changes, they need to register the observer.
Registered observers are as follows:

        Handler handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
            }
        };
        Uri uri = Uri.parse("content://" + AMSTargetProvider.AUTHORITY + "/ams");
        getContentResolver().registerContentObserver(uri, false, new ContentObserver(handler) {
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
            }
        });

The call stack of registerContentObserver is as follows:

ContentResolver.registerContentObserver
-->ContentResolver.registerContentObserver
-->getContentService().registerContentObserver(xx)

ContentObserver itself has no cross process capability, so it needs to be wrapped:

    #ContentObserver.java
    public IContentObserver getContentObserver() {
        synchronized (mLock) {
            if (mTransport == null) {
                mTransport = new Transport(this);
            }
            return mTransport;
        }
    }

    private static final class Transport extends IContentObserver.Stub {
        //Hold ContentObserver
        private ContentObserver mContentObserver;
        ...
    }

After encapsulating the observer, you need to pass it out.
Focus on getContentService():

 #ContentResolver.java
    public static IContentService getContentService() {
        if (sContentService != null) {
            return sContentService;
        }
        //Get IBinder reference from ServiceManager
        IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
        sContentService = IContentService.Stub.asInterface(b);
        return sContentService;
    }

The services registered in ServiceManager are as follows:

#ContentService.java
    public final class ContentService extends IContentService.Stub {
        ...
    }

Therefore, call ContentService.registerContentObserver(xx) to pass ContentObserver.Transport to ContentService.

When the ContentProvider data changes, the following code is called:

#ContentResolver.java
    private void notifyChange() {
        Uri notifyUri = Uri.parse("content://" + AUTHORITY + "/ams");
        getContext().getContentResolver().notifyChange(notifyUri, null);
    }

//Finally called here
    public void notifyChange(@android.annotation.NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
                             @UserIdInt int userHandle) {
        try {
            //Methods in ContentService
            getContentService().notifyChange(
                    uri, observer == null ? null : observer.getContentObserver(),
                    observer != null && observer.deliverSelfNotifications(),
                    syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
                    userHandle, mTargetSdkVersion, mContext.getPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

In fact, it still calls the ContentService, finds the previously registered ContentObserver. Transport, and then calls back Transport.onChange. Finally, it will call the ContentObserver.onChange() method just registered.
It should be noted that onChange() can choose to call back in the main / sub thread, and only need to pass in the Handler when registering.

The data change notice does not deal with AMS, but with ContentService, as shown in the figure below:

So far, the core of the communication between the four components and AMS has been analyzed. This paper does not analyze the processing details of the four components in AMS, but analyzes the communication process from the perspective of IPC. As long as you understand the communication process and communication framework, it is easy to find the corresponding processing details.

The test code includes examples of startService/bindService, dynamic / static registration broadcast, cross process writing of ContentProvider and so on:

This article is based on Android 10.0/8.0/7.0
Complete code demonstration If it's helpful, give github a compliment

If you like it, please praise and pay attention. Your encouragement is my driving force

During continuous update, work with me step by step to learn more about Android/Java

Keywords: Android SQLite

Added by bj_ on Mon, 04 Oct 2021 21:46:46 +0300