Android ContentProvider usage & source code analysis (Android Q)

Android ContentProvider use


ContentProvider is one of the four major components of Android. It is a content provider. It uses a unified interface to provide data content for applications.

ContentProvider can help us to achieve cross-process data access very simply, without worrying about which process the data is in and how the process is stored.

ContentProvider registration

ContentProvider is a data provider, and our purpose is to use it to provide data to the outside world.

  1. First of all, we have to define a class that inherits from ContentProvider (for example, com.budaye.MyProvider), and copy several public methods of the class to process the data process query, add, delete, update and other operations. Our data can be stored in files, in databases, or in other ways.

  2. Register the custom ContentProvider class in AndroidManifest.

        <provider
            android:name="com.budaye.MyProvider"
            android:authorities="com.budaye.application.storage"
            android:exported="false" />

Use of ContentProvider

The data of ContentProvider can be called in any program or process, and they are all unified interfaces (including: query, add, delete, update, etc.).

Steps for usage:

  1. When in use, call getContentResolver() of the context object to obtain the ContentResolver object.
  2. Use the ContentResolver object to perform data manipulation.
    public void readData(String selection) {
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver()
                    .query(getTableUri(), null, selection, null, null);
            ……
        } catch (Exception e) {
            LogX.e(TAG, SUB_TAG, getName() + "; " + e.toString());
        } 
    }

Source code analysis of Android ContentProvider


Above, we have introduced ContentProvider and how to use it. Do you think it is very simple?

Then we need to think about the following questions:

  1. How is such a simple way of use realized?
  2. How does it simplify inter-process communication? (Calling the ContentProvider of other processes is as simple as in the current process)
  3. Why is it necessary to register in AndroidManifest?
  4. When was the ContentProvider installed? When is our ContentProvider subclass called?

With the above questions, let's look at the source code.

ContentProvider installation process

In the previous article "Application Creation and Startup Process Source Analysis of App Process" , we can find that ContentProvider is installed and initialized during App startup process.

When an App starts, the system first calls the main() method of ActivityThread, which is the entry method of an APP, and ActivityThread represents the main thread or UI thread of the App. Then call the attach method of ActivityThread to perform a lot of initialization and preparation. The attach method of ActivityThread calls the attachApplication of the ActivityManagerService service remotely to start an Application. The attachApplicationLocked method in ActivityManagerService in the service AMS process remotely calls the bindApplication of the ApplicationThread of the current process to create and start the Application corresponding to the App. The BIND_APPLICATION message is processed through the handleMessage of class H, and the handleBindApplication method is called for the official startup process of the Application. After the Application object is created, the ContentProvider installation process will be carried out, and then the onCreate of the Application object will be called to complete the startup process of the App.

Let's look at the processing logic of ActivityThread's handleBindApplication to ContentProvider.

ActivityThread 的 handleBindApplication

        Application app;
        ……
        try {
            //创建 Application 对象
            app = data.info.makeApplication(data.restrictedBackupMode, null);
            ……
            // 如果应用正在安装或恢复这类特殊状态,restrictedBackupMode 值为 true。
            if (!data.restrictedBackupMode) {
                if (!ArrayUtils.isEmpty(data.providers)) {
                    //此处安装 ContentProvider
                    installContentProviders(app, data.providers);
                }
            }

We see that when the Application object is created, the ContentProvider installation process will proceed.

  1. The installation process of ContentProvider is performed after the Application object is created. When the Application object is created, its attach method will be called, and the attach method will call the attachBaseContext method. This means that the installation process of ContentProvider will be executed after the attachBaseContext of the Application and before the onCreate method of the Application. Generally, when processing solutions such as reinforcement, the ContentProvider of the reinforcement application must be processed first in attachBaseContext, otherwise an error will be reported (the class cannot be found during installation), which is also the reason.
  2. Here call the installContentProviders method to create, install, and publish the ContentProvider.
  3. Here data.providers is the providers property of AppBindData. Where is it assigned? Let's ignore it for now and analyze it later.

installContentProviders method

The installContentProviders method is responsible for creating, installing, and publishing ContentProviders.

Source code:

    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        // ContentProviderHolder 是对 ProviderInfo 和 远程接口 IContentProvider 的封装,方便进行跨进程通信
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            //创建 ContentProvider 对象,并获取 ContentProviderHolder 对象
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }
        try {
            //调用 AMS 的 publishContentProviders 发布 ContentProvider
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

Two things are mainly done here:

  1. Create a ContentProvider object, obtain the ContentProviderHolder object, and add the generated ContentProviderHolder object to an ArrayList (results).
  2. Call publishContentProviders of AMS, pass results, and publish ContentProvider.

We first look at the installProvider process.

installProvider method

installProvider mainly performs the creation and initialization of the ContentProvider object, and creates the corresponding Binder pair for remote calls, and finally saves the relevant information as a cache in the Map.

    private ContentProviderHolder installProvider(Context context,
            ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.provider == null) {
            ……
            //首先获取有效的 Context 对象。
            Context c = null;
            ……

            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
                if (packageInfo == null) {
                    // System startup case.
                    packageInfo = getSystemContext().mPackageInfo;
                }
                //创建 ContentProvider 对象,这里是本地对象
                localProvider = packageInfo.getAppFactory()
                        .instantiateProvider(cl, info.name);
                //获取 ContentProvider 对应的 Binder 对象,以支持远程调用
                provider = localProvider.getIContentProvider();
                ……
                //调用 ContentProvider 对象的 attachInfo 方法,该方法最后会调用 ContentProvider 对象的 onCreate() 方法。
                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 {
            // holder 对象存在,则直接获取
            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 != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
                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);
                    //添加到缓存 Map 中
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
                ……
            }
        }
        return retHolder;
    }

  1. First obtain a valid Context object.
  2. Use the ClassLoader object to load the ContentProvider class and create the ContentProvider object.
  3. Obtain the Binder object corresponding to the ContentProvider for remote invocation. The getIContentProvider() method returns a Transport object. Transport inherits from ContentProviderNative, and ContentProviderNative inherits from Binder, which implements the support of remote object calls.
  4. Call the attachInfo method of the ContentProvider object, which will finally call the onCreate() method of the ContentProvider object. At this point, the ContentProvider object has been created.
  5. The next step is to create a ContentProviderHolder object. provider is an instance object of Transport, jBinder the Binder object corresponding to the ContentProvider; pr is a ProviderClientRecord object. Here first create a ContentProviderHolder object, and add jBinder and pr to the Map cache.
  6. Finally, the ContentProviderHolder object is returned.

AMS publishContentProviders publish ContentProvider

The ActivityThread of the current APP passes the ArrayList queue generated in the previous step to the publishContentProviders method of the service process AMS to publish all ContentProviders registered in all APPs.

The ContentProvider (especially the remote Binder object) saved by the publishContentProviders() method ContentProviderHolder object is associated with the ContentProviderRecord object (the ContentProviderRecord object is created when the application resolves the AndroidManifest, and it is the ContentProvider information in the APP stored in the AMS). During the process, the ContentProviderRecord object will be stored in the mProviderMap queue. When other applications call the ContentProvider of the current APP in the future, AMS will directly retrieve the ContentProviderRecord object through the cache queue mProviderMap and use the Binder object contained in the ContentProviderRecord object (implementing the IContentProvider interface) for remote transfer.

    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++) {//遍历 providers List,并处理
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    continue;
                }
                ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
                if (dst != null) {
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    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--;
                        }
                    }
                    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);
        }
    }

The core operation of the publishing process is a process of updating and saving the relevant information of the ContentProvider object to the AMS.

At this point, the ContentProvider in our APP is ready, and other applications can access them at any time.

ContentProvider parsing and preparation process

The installation and release process of ContentProvider has been explained above. Let's go back to the handleBindApplication of ActivityThread. When installContentProviders(app, data.providers) is called, where is the parameter data.providers assigned?

Next, let's analyze the analysis and preparation process of ContentProvider.

AppBindData's provider assignment process

The parameter value data.providers of the installContentProviders method is the providers property (List) of the AppBindData object.

Where is the provider property of the AppBindData object assigned? This we have to start with attachApplicationLocked of ActivityManagerService.

The attachApplicationLocked method of ActivityManagerService.

In the first paragraph, get the ContentProvider registration information from the AndroidManifest file of the installation package:

        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

Here, use the generateApplicationProvidersLocked method of the PMS service to obtain:

    private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
        List<ProviderInfo> providers = null;
        try {
            //远程调用 PMS 获取应用中注册的 ContentProvider 信息
            providers = AppGlobals.getPackageManager()
                    .queryContentProviders(app.processName, app.uid,
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
                                    | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
                    .getList();
        } catch (RemoteException ex) {
        }
    }

At this time, we obtained the ContentProvider information from the AndroidManifest of the installation package.

The second paragraph of the attachApplicationLocked method:

final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
            if (app.isolatedEntryPoint != null) {
                // This is an isolated process which should just call an entry point instead of
                // being bound to an application.
                thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
            } else if (instr2 != null) {
                thread.(processName, appInfo, providers,
                        instr2.mClass,
                        profilerInfo, instr2.mArguments,
                        instr2.mWatcher,
                        instr2.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions);
            } else {
                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.compat, getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions);
            }

This is mainly to call the bindApplication method of the process where the APP is located (the bindApplication method of the ApplicationThread object, here is the IPC call) to start the Application.

The thread object here is an instance of ApplicationThread, and the ApplicationThread class inherits from IApplicationThread.Stub, which is a Binder object used for cross-process communication (for example, here to communicate with the AMS service process).

Let's look at the bindApplication method of ApplicationThread:

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, AutofillOptions autofillOptions,
                ContentCaptureOptions contentCaptureOptions) {
            ……
            ……
            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableBinderTracking = enableBinderTracking;
            data.trackAllocation = trackAllocation;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            data.buildSerial = buildSerial;
            data.autofillOptions = autofillOptions;
            data.contentCaptureOptions = contentCaptureOptions;
            sendMessage(H.BIND_APPLICATION, data);
        }

The AppBindData object is created here, and the providers of the AppBindData object are also assigned here. At this point, we have also found the source of the provider assignment, and know that what is stored in the provider is actually the registration information of the ContentProvider obtained from the AndroidManifest of the installation package.

At the end of this method, the message H.BIND_APPLICATION is sent to H (a Handler) for message processing. Let’s look at:

                case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

The handleBindApplication method of ActivityThread is called here, which is connected with the installation process of ContentProvider, and then the process of creating, installing and publishing ContentProvider is the process.

At this point, the source code analysis process of ContentProvider has been completed.

to sum up


  1. ContentProvider is one of the four major components of Android. It is a content provider. It uses a unified interface to provide data content for applications.

  2. ContentProvider essentially uses the IPC mechanism of the Android system (Binder model). When the APP starts, it parses the registration information of the ContentProvider from the AndroidManifest file, and after processing, passes it to the AMS service, and creates the corresponding ContentProviderRecord object and saves it in the AMS in service. When the client application accesses data through the ContentProvider, it will query and obtain the corresponding ContentProviderRecord object through the AMS service, obtain the real ContentProvider Binder object, and then data access can be performed.

  3. The attachApplicationLocked method of AMS uses the PMS service to obtain the ContentProvider registration information from the AndroidManifest file of the installation package.

  4. AMS calls the bindApplication method of the process where the APP is located (the bindApplication method of the ApplicationThread object, here is the IPC call) to start the Application, and passes the ContentProvider information as a parameter to the APP.

  5. The bindApplication method of the ApplicationThread of the App process receives the passed ContentProvider information, creates an AppBindData object, and assigns the ContentProvider information to the providers of the AppBindData object.

  6. Then, through the occurrence of the message H.BIND_APPLICATION, it is handed over to H (a Handler) for message processing, and finally the handleBindApplication method of ActivityThread is called to carry out the process of creating, installing and publishing ContentProvider.

  7. In the handleBindApplication method, call the installContentProviders method to create, install, and publish the ContentProvider.

  8. The installContentProviders method creates a ContentProvider object, obtains a ContentProviderHolder object, and adds the generated ContentProviderHolder object to an ArrayList (results).

  9. Call publishContentProviders of AMS, pass results, and publish ContentProvider.

  10. The core operation of the publishing process is a process of updating and saving the relevant information of the ContentProvider object to the AMS.


PS: For more articles, please check the series of articles -> "Analysis of the underlying principles of Android" column.
PS: For more articles, please check the series of articles -> "Analysis of the underlying principles of Android" column.
PS: For more articles, please check the series of articles -> "Analysis of the underlying principles of Android" column.

Guess you like

Origin blog.csdn.net/u011578734/article/details/111812636