For students who are not familiar with ContentProvider, you can read the basic principles and use of Android ContentProvider in detail. This paper mainly analyzes the source code of contentProvider, so as to understand the realization principle of contentProvider.

This article analyzes the source code based on Android 10, API level 29.

ContentProvider starts the process

ContentProvider (CP) startup flowchart is as follows: Read the following for this.

1, ActivityThread. HandleBindApplication

For those looking at the Activity launch process, you can see that the Application instance is created in the handleBindApplication method of the ActivityThread. One thing I missed in explaining this method is that the ContentProvider is created in this method.

// ActivityThread private void handleBindApplication(AppBindData data) { // ...... Application app; try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. app = data.info.makeApplication(data.restrictedBackupMode, null); // Propagate autofill compat state app.setAutofillOptions(data.autofillOptions); // Propagate Content Capture options app.setContentCaptureOptions(data.contentCaptureOptions); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (! data.restrictedBackupMode) { if (! ArrayUtils.isEmpty(data.providers)) { installContentProviders(app, data.providers); } } // Do this after providers, since instrumentation tests generally start their // test thread at this point, And we don't want that racing. Try {// Instrumentation onCreate method mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ":  " + e.toString(), e); } try {/ / application onCreate method mInstrumentation callApplicationOnCreate (app); } catch (Exception e) { if (! mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } finally { // If the app targets < O-MR1, or doesn't change the thread policy // during startup, clobber the policy to maintain behavior of b/36951662 if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1 || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) { StrictMode.setThreadPolicy(savedPolicy); }} / /... }Copy the code

This simplifies a lot of code, but the important parts remain. The providers member variable of the AppBindData object data holds the CP component to be started in the current application process. The installContentProviders method is then called.

You can see that the installContentProviders are called before the onCreate of the Application, so you can conclude:

OnCreate of the ContentProvider is called before onCreate of the Application.

Because onCreate is turned off during startup, try to avoid time-consuming operations, such as IO operations. Otherwise, the Content Provider component may start with a timeout.

ActivityThread.installContentProviders

private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<ContentProviderHolder> results = new ArrayList<>(); for (ProviderInfo cpi : providers) { ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph ! = null) { cph.noReleaseNeeded = true; results.add(cph); Try {}} / / published to AMS ActivityManager getService () publishContentProviders (getApplicationThread (), the results). } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}Copy the code

This approach does two main things.

  • First, through the looping variable ProviderInfo, call the installProvider method to install the provider information and encapsulate it into an object of type ContentProviderHolder. It contains the IContentProvider interface.
  • In the second. Call the publishContentProviders method of the AMS service to publish the installed Provider information to the AMS service for other processes to access.

ContentProviderHolder

What is it? It is a data object (AIDL) that can be passed between processes.

public class ContentProviderHolder implements Parcelable { public final ProviderInfo info; public IContentProvider provider; public IBinder connection; .Copy the code

It contains a lot of information about CP, so when AMS gets the ContentProviderHolder (CPH), it is equivalent to getting all the information about CP. If AMS cannot send it later, it depends on this object.

ActivityThread.installProvider

The ActivityThread installProvider method is then called. If the holder is passed in as null, So the ContentProvider instance is created in installProvider and cached in HashMap.

Let’s look at the installProvider process first, and then look at the AMS publishing ContentProvider process.

private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, Boolean Noisy, Boolean Noreleasenneeded, Boolean stable) {ContentProvider localProvider = null; // Remember the type here IContentProvider provider; / / the first installation must be empty if (holder = = null | | holder. The provider = = null) {Context c = null; ApplicationInfo ai = info. ApplicationInfo; If (context.getPackagename ().equals(ai.packagename)) {c = context; } else if (mInitialApplication ! = null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } the catch (PackageManager NameNotFoundException e) {/ / Ignore}} / / context is empty returned directly if (c = = null) {Slog w. (TAG, "Unable to get context for package " + ai.packageName + " while loading content provider " + info.name); return null; } if (info.splitName ! = null) { try { c = c.createContextForSplit(info.splitName); } catch (NameNotFoundException e) { throw new RuntimeException(e); } } try { final java.lang.ClassLoader cl = c.getClassLoader(); LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); if (packageInfo == null) { // System startup case. packageInfo = getSystemContext().mPackageInfo; } / / instantiate localProvider = packageInfo. GetAppFactory () instantiateProvider (cl, info. Name); / / access interface provider = localProvider getIContentProvider (); if (provider == null) { Slog.e(TAG, "Failed to instantiate class " + info.name + " from sourceDir " + info.applicationInfo.sourceDir); return null; } if (DEBUG_PROVIDER) Slog.v( TAG, "Instantiating local provider " + info.name); // XXX Need to create the correct context for this provider. Call the attachInfo method for initialization, as shown in the flowchart localprovider.attachInfo (c, info); } catch (java.lang.Exception e) { if (! mInstrumentation.onException(null, e)) { throw new RuntimeException( "Unable to get provider " + info.name + ": " + e.toString(), e); } return null; } } else { provider = holder.provider; if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": " + info.name); } ContentProviderHolder retHolder; synchronized (mProviderMap) { if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider + " / " + info.name); IBinder jBinder = provider.asBinder(); if (localProvider ! Cname = new ComponentName(info.packagename, info.name); / / see if according to the name can be found ProviderClientRecord pr = mLocalProvidersByName. Get (cname); // Create pr for the first time if (pr! = null) { if (DEBUG_PROVIDER) { Slog.v(TAG, "installProvider: lost the race, " + "using existing local provider"); } provider = pr.mProvider; } else { holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = true; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else { ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if (prc ! = null) { if (DEBUG_PROVIDER) { Slog.v(TAG, "installProvider: lost the race, updating ref count"); } // We need to transfer our new reference to the existing // ref count, releasing the old one... but only if // release is needed (that is, it is not running in the // system process). if (! noReleaseNeeded) { incProviderRefLocked(prc, stable); try { ActivityManager.getService().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } else { ProviderClientRecord client = installProviderAuthoritiesLocked( provider, localProvider, holder); if (noReleaseNeeded) { prc = new ProviderRefCount(holder, client, 1000, 1000); } else { prc = stable ? new ProviderRefCount(holder, client, 1, 0) : new ProviderRefCount(holder, client, 0, 1); } mProviderRefCountMap.put(jBinder, prc); } retHolder = prc.holder; } } return retHolder; }Copy the code

When this method is called in the installContentProviders method, the holder parameter is passed Null because the ContentProviders are installed for the first time. So holder must be Null. So the if condition is satisfied. In an If statement, the corresponding Context Context information is first obtained based on the condition.

The ClassLoader then loads the corresponding ContentProvider class, creates an object for that class, and calls the attachInfo method of the ContentProvider. This method associates the newly created ContentProvider with the Context, ProviderInfo, and invokes the Provider’s onCreate method to launch the ContentProvider. Now that a ContentProvider is created, the next step is to save it to the application process for easy lookup and management. And published to the AMS service for other processes to call.

Get the ContetProvider’s IContentProvider (ICP) assigned to the provider variable. IContentProvider is the interface through which the ContentProvider client communicates with the server. GetIcontentProvider is understood to be an object of type Binder for communication between the ContentProvider client and server.

Since the ContentProvider is started for the first time, this information is not saved, so the variable pr is empty. At this point, based on ProviderInfo information and the Binder IContentProvider object, Create a ContentProviderHolder object that encapsulates the ContentProvider ProviderInfo and IContentProvider information.

Method returns the created ContentProviderHolder object.

Transport

Transport is an internal class, derived from ContentProviderNative, which is a binder with remote communication capabilities.

See the code below for the getIcontentProvider.

// ContentProvider.java /** * Binder object that deals with remoting. */ Class Transport extends ContentProviderNative {volatile AppOpsManager mAppOpsManager = null; volatile int mReadOp = AppOpsManager.OP_NONE; volatile int mWriteOp = AppOpsManager.OP_NONE; volatile ContentInterface mInterface = ContentProvider.this; } private Transport mTransport = new Transport(); /** * Returns the Binder object for this provider. * * @return the Binder object for this provider * @hide */ @UnsupportedAppUsage public IContentProvider getIContentProvider() { return mTransport; }Copy the code
// ContentProviderNative.java
Copy the code
abstract public class ContentProviderNative extends Binder implements IContentProvider {}
Copy the code
 
Copy the code

IContentProvider is a binder.

ContentProviderRecord

There is a ContentProviderRecord(CPR), which is an object used by the system (ActivityManagerService) to record information about a ContentProvider.

final class ContentProviderRecord implements ComponentName.WithComponentName {
    final ActivityManagerService service;
    public final ProviderInfo info;
    final int uid;
    final ApplicationInfo appInfo;
    final ComponentName name;
    final boolean singleton;
    public IContentProvider provider;
    public boolean noReleaseNeeded;
    // All attached clients
    final ArrayList<ContentProviderConnection> connections
            = new ArrayList<ContentProviderConnection>();
    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
    // Handles for non-framework processes supported by this provider
    HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
    // Count for external process for which we have no handles.
    int externalProcessNoHandleCount;
    ProcessRecord proc; // if non-null, hosting process.
    ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
    String stringName;
    String shortStringName;
Copy the code

You can see that there is quite a bit of information in the Record.

ProviderInfo 

Used to store information about a ContentProvider ( in the manifest), such as authority, readPermission, and so on.

public final class ProviderInfo extends ComponentInfo implements Parcelable { /** The name provider is published under content:// */ public String authority = null; /** Optional permission required for read-only access this content * provider. */ public String readPermission = null; /** Optional permission required for read/write access this content * provider. */ public String writePermission = null;  /** If true, additional permissions to specific Uris in this content * provider can be granted, as per the * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions * grantUriPermissions} attribute. */  public boolean grantUriPermissions = false; /** * If non-null, these are the patterns that are allowed for granting URI * permissions. Any URI that does not match one of these patterns will not * allowed to be granted. If null, all URIs are allowed. The * {@link PackageManager#GET_URI_PERMISSION_PATTERNS * PackageManager.GET_URI_PERMISSION_PATTERNS} flag must be specified for * this field to be filled in. */ public PatternMatcher[] uriPermissionPatterns = null; /** * If non-null, these are path-specific permissions that are allowed for * accessing the provider. Any permissions listed here will allow a * holding client to access the provider, and the provider will check * the URI it provides when making calls against the patterns here. */ public PathPermission[] pathPermissions = null; /** If true, this content provider allows multiple instances of itself * to run in different process. If false, a single instances is always * run in {@link #processName}. */ public boolean multiprocess = false;Copy the code

ActivityThread.installProviderAuthoritiesLocked

Then look at the installProviderAuthoritiesLocked implementation

private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider, ContentProvider localProvider, ContentProviderHolder holder) { final String auths[] = holder.info.authority.split(";" ); final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); if (provider ! = null) { // If this provider is hosted by the core OS and cannot be upgraded, // then I guess we're okay doing blocking calls to it. for (String auth : auths) { switch (auth) { case ContactsContract.AUTHORITY: case CallLog.AUTHORITY: case CallLog.SHADOW_AUTHORITY: case BlockedNumberContract.AUTHORITY: case CalendarContract.AUTHORITY: case Downloads.Impl.AUTHORITY: case "telephony": Binder.allowBlocking(provider.asBinder()); } } } final ProviderClientRecord pcr = new ProviderClientRecord( auths, provider, localProvider, holder); for (String auth : auths) { final ProviderKey key = new ProviderKey(auth, userId); final ProviderClientRecord existing = mProviderMap.get(key); if (existing ! = null) { Slog.w(TAG, "Content provider " + pcr.mHolder.info.name + " already published as " + auth); } else { mProviderMap.put(key, pcr); } } return pcr; }Copy the code

A ProviderClientRecord (PCR) object is created according to the Provider’s information. The authority is a multi-attribute value, and the variable is all the authority corresponding to the Provider. Each authority attribute is key. Save the ProviderClientReocrd to the HashMap described by the mProviderMap.

There are three lists in an application process (ActivityThread) that store information about contentProviders in the process.

  • ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap

    • The authority key is used to save providerClientRecord information
  • ArrayMap<IBinder, ProviderClientRecord> mLocalProviders

    • Save the ProviderClientRecord object as key with the interface Binder object for communication. This contains information about the process’s ContentProvider
  • ArrayMap<ComponentName,ProviderClientRecord> mLocalProvidersByName

    • Save the ProviderClientRecord object with the Provider’s ComponentName information as the key. This contains information about the process’s ContentProvider

Use the installProvider method to load the ContentProvider class into memory, create the ContentProvider object, and call onCreate of the ContentProvider to launch it. It is then stored separately in different ContentProvider collections of different storage types.

AMS.publishContentProviders 

When the ContentProvider is created locally and saved, encapsulate it as a ContentProviderHolder object and return, We then call AMS’s publishContentProviders method (which actually sends an AMP (Active ManagerProxy) of type PUBLISH_CONTENT_PROVIDERS_TRANSACTION interprocess communication request), sends these Holder objects to the AMS service to publish them to the AMS service.

// AMS public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null) { return; } enforceNotIsolatedCaller("publishContentProviders"); synchronized (this) { final ProcessRecord r = getRecordForAppLocked(caller); if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid); if (r == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + Binder.getCallingPid() + ") when publishing content providers"); } final long origId = Binder.clearCallingIdentity(); final int N = providers.size(); for (int i = 0; i < N; i++) { ContentProviderHolder src = providers.get(i); if (src == null || src.info == null || src.provider == null) { continue; } // Get CPR ContentProviderRecord DST = r.puprovider.get (src.info.name); if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid); if (dst ! = null) {ComponentName comp = new ComponentName(dt.info.packagename, dt.info.name); / / component name for the key mProviderMap. PutProviderByClass (comp, DST); String names[] = dst.info.authority.split(";" ); for (int j = 0; j < names.length; j++) { mProviderMap.putProviderByName(names[j], dst); } int launchingCount = mLaunchingProviders.size(); int j; boolean wasInLaunchingProviders = false; for (j = 0; j < launchingCount; j++) { if (mLaunchingProviders.get(j) == dst) { mLaunchingProviders.remove(j); wasInLaunchingProviders = true; j--; launchingCount--; }} Launchingproviders (wasInLaunchingProviders) {// If (wasInLaunchingProviders) mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } // Make sure the package is associated with the process. // XXX We shouldn't need to do this, since we have added the package // when we generated the providers in generateApplicationProvidersLocked(). // But for some reason in some cases we get here with the package no longer // added... for now just patch it in to make things happy. r.addPackage(dst.info.applicationInfo.packageName, dst.info.applicationInfo.longVersionCode, mProcessStats); synchronized (dst) { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); } updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); }}Copy the code

The caller argument is a Binder proxy object of type ApplicationThread that refers to an ApplicationThread object running in the newly created application process. Line 8 uses it to get a ProcessRecord object that describes the newly created application process.

When a newly created application process starts, it starts the Content Provider component that needs to run inside it. The ContentProvider component is described in AMS using a ContentProviderRecord object, They are stored in pubProviders, a ProcessRecord object R member variable used to describe the newly created application process.

The providers parameter contains the ContentProvider component to be published in AMS. Each ContentProvider component is described using the ContentProviderHolder object. It contains an IContentProvider interface for the ContentProvider component to be published.

The first big for loop in the code removes each ContentProviderHolder object SRC stored in the Providers parameter, and then finds the corresponding CPR object DST in AMS. Finally, an IContentProvider access interface for a CP component described by the ContentProviderHolder object SRC is saved in the provider member variable of the CPR object DST.

This is the end of the process of loading and initializing the ContentProvider as the application starts.

Here’s a look at the workflow of using ContentProvider.

Data transfer process

After starting the Content Provider, let’s take a look at how its data is transferred.

Normally we get the code for a ContentResolver as follows:

ContentResolver cr = context.getContentResolver(); / / get a ContentResolverCopy the code

In the Context of all implementation is ContextImpl, so the Context… getContentResolver () method is the same.

ContextImpl.getContentResolver().query()

We from ContextImpl. GetContentResolver (). The query () began to see:


// ContentResolver.java

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "Uri"); IContentProvider unstableProvider = acquireUnstableProvider(URI); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null; if (cancellationSignal ! = null) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } try {qCursor = unstableProvider. Query (mPackageName, URI, projection, queryArgs, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable // reference though, so we might recover!!! Let's try!!!! // This is exciting!! 1!!!!! 1!!!!!!!!!! 1 unstableProviderDied (unstableProvider); StableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query( mPackageName, uri, projection, queryArgs, remoteCancellationSignal); } if (qCursor == null) { return null; } // Force query execution. Might fail and throw a runtime exception here. qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs); // Wrap the cursor object into CursorWrapperInner object. final IContentProvider provider = (stableProvider ! = null) ? stableProvider : acquireProvider(uri); final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider); stableProvider = null; qCursor = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { if (qCursor ! = null) { qCursor.close(); } if (cancellationSignal ! = null) { cancellationSignal.setRemote(null); } if (unstableProvider ! = null) { releaseUnstableProvider(unstableProvider); } if (stableProvider ! = null) { releaseProvider(stableProvider); }}}Copy the code

The main method is to get the IContentProvider and get the data based on the URI.

To understand the implementation details of the above process, we need to analyze the calling process of the acquireProvider method of ContentResolver class first, and then analyze the implementation of the IContentProvider interface method Query.

ContentResolver acquireProvider is an abstract method, the specific implementation can see ApplicationContentResolver:

    private final ActivityThread mMainThread;

    protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
Copy the code

ApplicationContentResolver member variable mMainThread pointed to a ActivityThread object, it is initialized in the constructor. Therefore, you actually get the Proxy object for the contentProvider through the ActivityThread.

ActivityThread.acquireProvider

Let’s look at the implementation:

Public Final IContentProvider acquireProvider(Context C, String Auth, int userId, Boolean stable) {public Final IContentProvider acquireProvider(Context C, String Auth, int userId, Boolean stable) AcquireExistingProvider (c, Auth, userId, stable); acquiReTentProvider (c, Auth, userId, stable); if (provider ! = null) { return provider; } // There is a possible race here. Another thread may try to acquire // the same provider at the same time. When this happens, we want to ensure // that the first one wins. // Note that we cannot hold the lock while acquiring and installing the //  provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. ContentProviderHolder holder = null; try { synchronized (getGetProviderLock(auth, UserId)) {/ / get holder by ams holder = ActivityManager. GetService () getContentProvider ( getApplicationThread(), auth, userId, stable); } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); If (holder == null) {slog. e(TAG, "Failed to find provider info for "+ auth); return null; } // Install provider will increment the reference count for us, and break // any ties in the race. holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }Copy the code

That is, if you have not obtained IContentProvider locally, Directly to launch getContentProvider ActivityManagerService request, finally call ActivityManagerService. GetContentProviderImpl (), This method is at the heart of the ContentProvider instantiation logic:

Let’s start with the declaration of this method:

ContentProviderHolder getContentProviderImpl(IApplicationThread caller,String name, IBinder token, boolean stable, int userId)
Copy the code

 

That is, eventually a ContentProviderHolder is returned, as explained earlier.

Moving on to getContentProviderImpl(), this method is quite long, so let’s look at the method in sections (1), (2), (3)… This kind of:

(1) ActivityManagerService. GetContentProviderImpl () ` `

// Three key objects ContentProviderRecord CPR; ContentProviderConnection conn = null; ProviderInfo cpi = null; . cpr = mProviderMap.getProviderByName(name, userId); // See if the system has cached the ContentProviderCopy the code

ContentProviderConnection here mainly to explain to you

/**
 * Represents a link between a content provider and client.
 */
public final class ContentProviderConnection extends Binder {
    public final ContentProviderRecord provider;
    public final ProcessRecord client;
    public final long createTime;
    public int stableCount;
    public int unstableCount;
    // The client of this connection is currently waiting for the provider to appear.
    // Protected by the provider lock.
    public boolean waiting;
    // The provider of this connection is now dead.
    public boolean dead;
Copy the code

It’s a Binder. Connect the server (ActivityManagerService) to the client (our app). It records the state of a ContentProvider, such as whether it has died. The others have been explained.

(2) ActivityManagerService. GetContentProviderImpl ()

cpr = mProviderMap.getProviderByName(name, userId); ContentProvider Boolean providerRunning = CPR! = null && cpr.proc ! = null && ! cpr.proc.killed; if (providerRunning) { ... } if (! providerRunning) { ... }Copy the code

That is, depending on whether the process in which the ContentProvider is running is active or whether the ContentProvider has been started (cached) :

The ContentProvider is loaded and the process is running

That is, if (providerRunning) {… } code

ProcessRecord r = getRecordForAppLocked(caller); If (r! // If the requested ContentProvider and client are in the same process ContentProviderHolder holder = CPR.newholder (null); / / ContentProviderConnection parameter pass null holder. The provider = null; // Note that the client is left to instantiate itself!! return holder; Conn = incProviderCountLocked(r, CPR, token, stable); conn = incProviderCountLocked(r, CPR, token, stable) / / direct according to ContentProviderRecord and ProcessRecord construct a ContentProviderConnection...Copy the code

If you request a ContentProvider from the same process, go straight back to the main thread of the process to instantiate the ContentProvider. Otherwise, use a ContentProviderConnection ContentProviderRecord and ProcessRecord tectonic.

The ContentProvider process is not running and the server (ActivityManagerService) has not loaded it

I.e., the if (! providerRunning){ … } code

/ / to parse out a ProviderInfo cpi = AppGlobals getPackageManager (). ResolveContentProvider (name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); . ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr = mProviderMap.getProviderByClass(comp, userId); Final Boolean firstClass = CPR == null; if (firstClass) { ... cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); // Construct a ContentProviderRecord}... final int N = mLaunchingProviders.size(); // mLaunchingProviders is an int I that caches the collection of contentProviders being launched; for (i = 0; i < N; I ++) {if (mlaunchingproviders.get (I) == CPR) {if (mlaunchingproviders.get (I) == CPR) { }} // This ContentProvider is not started, If (I >= N) {ProcessRecord Proc = getProcessRecordLocked(CPI.processName, CPR.appInfo.uid, false); . if (proc ! = null && proc.thread ! = null && ! Proc. Killed) {/ / content provider's process has begun the proc. Thread. ScheduleInstallProvider (cpi); } else {// Start the process where the Content Provider is located, And arouse the content provider proc = startProcessLocked (cpi processName, CPR. AppInfo, false, 0, "content provider",new ComponentName(cpi.applicationInfo.packageName,cpi.name)...) ; } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); / / added to the starting of queue} / / cache the ContentProvider information if (firstClass) {mProviderMap. PutProviderByClass (comp, CPR); } mProviderMap.putProviderByName(name, cpr); / / construct a ContentProviderConnection conn = incProviderCountLocked (r, CPR, token, stable); if (conn ! = null) { conn.waiting = true; // Set this connection}Copy the code

(3) ActivityManagerService. GetContentProviderImpl ()

// Wait for the provider to be published... synchronized (cpr) { while (cpr.provider == null) { .... if (conn ! = null) { conn.waiting = true; } cpr.wait(); } } return cpr ! = null ? cpr.newHolder(conn) : null; // return to the process that requested the clientCopy the code

Based on the previous analysis, the process in which the ContentProvider is located is not running or is not andTo obtain theThe same process creates oneContentProviderConnection,The server then hangs, starts the process in which the ContentProvider is located, and waits for it to instantiateContentProvider 。Ok, we know that from the previous analysisContentProvider It is instantiated in the process in which it is instantiated.

Then look at the client code, the previous analysis we know that if the client process and request the ContentProvider is located in the same process, the ActivityManager. GetService () getContentProvider (…). ; An empty ContentProviderHolder is returned. Let’s look at the code where the client requested the ContentProvider from the server:

holder = ActivityManager.getService().getContentProvider( getApplicationThread(), auth, userId, stable); If the ContentProvider process is found to be the same as the current client process, the client process will instantiate the ContentProvider. Holder = installProvider(c, holder, holder.info, true /* Noisy */, holder.noreleasenneeded, stable);Copy the code

The ContentProvider instantiation process is not in the same process

If the client process and the requested ContentProvider are not in the same process, we can see from the logic of ActivityManagerService. ActivityManagerService invokes the ContentProvider’s process of proc. Thread. ScheduleInstallProvider (cpi), Actually end up calling to ActivityThread installContentProviders

private void installContentProviders(Context context, List<ProviderInfo> providers) { final ArrayList<ContentProviderHolder> results = new ArrayList<>(); //ActivityManagerService lets the client start a list of ContentProviders for (ProviderInfo CPI: providers) { ContentProviderHolder cph = installProvider(context, null, cpi,false, true ,true); if (cph ! = null) { cph.noReleaseNeeded = true; results.add(cph); } } ActivityManager.getService().publishContentProviders(getApplicationThread(), results); // Notify the server that the content provider is ok}Copy the code

At this point, the startup logic is basically the same as the previous one. How do we get data from query once we get the interface?

Query procedure Parsing

IContentProvider is an instance of Transport, so you need to check the specific query logic

@Override public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); if (enforceReadPermission(callingPkg, uri, null) ! = AppOpsManager.MODE_ALLOWED) { if (projection ! = null) { return new MatrixCursor(projection, 0); } Cursor cursor; final String original = setCallingPackage(callingPkg); Try {// mInterface cursor = mInterface. Query (URI, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { setCallingPackage(original); } if (cursor == null) { return null; } // Return an empty cursor for all columns. return new MatrixCursor(cursor.getColumnNames(), 0); } Trace.traceBegin(TRACE_TAG_DATABASE, "query"); final String original = setCallingPackage(callingPkg); try { return mInterface.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { setCallingPackage(original); Trace.traceEnd(TRACE_TAG_DATABASE); }}Copy the code

Here the query () method is called via mInterface

        volatile ContentInterface mInterface = ContentProvider.this;
Copy the code

This is actually a call to the self-implemented provider method. The concrete implementation is actually through db to query. So far, the data transmission process of CP is finished.

But for cross-process, you should actually make cross-process calls through ContentProviderNative’s inner class ContentProviderProxy, which eventually calls back to ContentProviderNative’s onTransact method, MInterface’s Query method is then called to implement the query.

class ContentProviderProxy implements IContentProvider{}
Copy the code

ContentProviderProxy implements the IContentProvider interface. So when did you switch to ContentProviderProxy?

// ContentProviderHolder.java
Copy the code
private ContentProviderHolder(Parcel source) { info = ProviderInfo.CREATOR.createFromParcel(source); provider = ContentProviderNative.asInterface( source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() ! = 0; }Copy the code

When you create a CPH, you will return the corresponding instance based on whether it is the current process or cross-process.

// ContentProviderNative.java static public IContentProvider asInterface(IBinder obj) { if (obj == null) { return null; } IContentProvider in = (IContentProvider)obj.queryLocalInterface(descriptor); if (in ! = null) { return in; } return new ContentProviderProxy(obj); }Copy the code

At this point you should be able to understand CP startup and its cross-process communication capabilities.

conclusion

There are two ways to start a ContentProvider:

  1. One is when you start the App, you start CP;
  2. The other is to access the data of other apps. If the corresponding App is not started, CP will also be started at this time.

Once CP is started, ContentProviderHolder(containing ICP interface) will be published to AMS, so that other apps or themselves can obtain ICP interface through AMS to obtain data. Additionally, the onCreate of the ContentProvider is called before the onCreate of the Application.

Refer to the article

Android system source code analysis -ContentProvider

Principle analysis of ContentProvider

Android-contentprovider source code interpretation

ContentProvider startup process analysis