Android ContentProvider 的使用&源码分析(Android Q)

Android ContentProvider 使用


ContentProvider 是 Android 四大组件之一,是内容提供者,它使用统一接口的方式,为应用提供数据内容。

ContentProvider 可以帮助我们非常简单的实现跨进程的数据访问,不用关心数据到底是在哪个进程中,以什么方式进程存储的。

ContentProvider 的注册

ContentProvider 是数据的提供者,我们的目的是使用它来对外提供数据。

  1. 首先我们要定义一个继承自 ContentProvider 的类(例如,com.budaye.MyProvider),并且复写类的几个公开方法,对数据进程查询、添加、删除、更新等操作处理。我们的数据可以存储在文件中,也可以存储在数据库中,或者以其他方式存储都可以。

  2. 将自定义的 ContentProvider 类,在 AndroidManifest 中进行注册。

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

ContentProvider 的使用

ContentProvider 的数据可以在任意一个程序或进程中进行调用,都是统一的接口(包括:查询、添加、删除、更新等操作)。

使用步骤:

  1. 在使用时,调用 context 对象的 getContentResolver(),获取 ContentResolver 对象。
  2. 使用 ContentResolver 对象,进行数据操作即可。
    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());
        } 
    }

Android ContentProvider 的源码分析


上面,我们已经介绍了 ContentProvider 以及它的使用方法,大家是不是觉得非常简单呢?

那我们需要想一想,以下几个问题:

  1. 如此简单的使用方式,到底是怎么实现的呢?
  2. 它是如何简化进程间通信的呢?(调用其他进程的 ContentProvider 就像在当前进程中一样简单)
  3. 为什么必须在 AndroidManifest 中进行注册呢?
  4. ContentProvider 是在什么时候安装的呢?我们的 ContentProvider 子类是何时被调用的?

带着以上几个问题,我们来看源码。

ContentProvider 的安装过程

在前文《App进程的Application创建、启动过程源码解析》中,我们可以发现 ContentProvider 是在 App 启动过程进行安装和初始化的。

一个 App 在启动时,系统首先会调用 ActivityThread 的 main() 方法,它是一个 APP 的入口方法,而 ActivityThread 则代表了 App 的主线程或 UI 线程。接着调用 ActivityThread 的 attach 方法,进行大量的初始化和准备工作。ActivityThread 的 attach 方法,远程调用 ActivityManagerService 服务的 attachApplication 来启动一个 Application。ActivityManagerService 中的 attachApplicationLocked 方法在服务 AMS 进程中,远程调用当前进程的 ApplicationThread 的 bindApplication 来创建并启动 App 所对应的的 Application。通过 H 类的 handleMessage 对 BIND_APPLICATION 消息进行处理,调用 handleBindApplication 方法进行 Application 的正式启动过程,当创建完 Application 对象之后,就会进行 ContentProvider 的安装过程,然后调用 Application 对象的 onCreate,完成 App 的启动过程。

我们来看 ActivityThread 的 handleBindApplication 对 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);
                }
            }

我们看到,当创建完 Application 对象之后,就会进行 ContentProvider 的安装过程。

  1. ContentProvider 的安装过程是在 Application 对象创建之后进行的,创建 Application 对象时,会调用它的 attach 方法,attach 方法会调用 attachBaseContext 方法。这也就是说 ContentProvider 的安装过程会在 Application 的 attachBaseContext 之后,并且在 Application 的 onCreate 方法之前执行。通常在加固等方案处理时,首先要在 attachBaseContext 中对被加固应用的 ContentProvider 进行处理,否则就会报错(安装时找不到类),也是这个原因。
  2. 这里调用 installContentProviders 方法进行 ContentProvider 的创建、安装、发布过程。
  3. 这里的 data.providers 是 AppBindData 的 providers 属性。它是在哪赋值的呢,这里我们先暂时先忽略,在后面进行分析。

installContentProviders 方法

installContentProviders 方法负责创建、安装、发布 ContentProvider。

源码:

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

这里主要做了2件事:

  1. 创建 ContentProvider 对象,并获取 ContentProviderHolder 对象,将生成的 ContentProviderHolder 对象添加到一个 ArrayList 中(results)。
  2. 调用 AMS 的 publishContentProviders ,传递 results ,并发布 ContentProvider。

我们首先来看 installProvider 过程。

installProvider 方法

installProvider 主要进行了 ContentProvider 对象的创建和初始化工作,并且创建了对应的用于远程调用的 Binder对,最后将相关信息作为缓存保存在 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. 首先获取有效的 Context 对象。
  2. 使用 ClassLoader 对象,加载 ContentProvider 的类,并创建 ContentProvider 对象。
  3. 获取 ContentProvider 对应的 Binder 对象,以供远程调用。getIContentProvider()方法,返回一个 Transport 对象,Transport 继承自 ContentProviderNative,而 ContentProviderNative 继承自 Binder,它实现了远程对象调用的支持。
  4. 调用 ContentProvider 对象的 attachInfo 方法,该方法最后会调用 ContentProvider 对象的 onCreate() 方法。到了这里,ContentProvider 对象已经创建完成了。
  5. 接下来,就是创建 ContentProviderHolder 对象了。provider 是 Transport 的实例对象,jBinder 该 ContentProvider 对应的 Binder 对象; pr 是一个 ProviderClientRecord 对象。这里首先创建 ContentProviderHolder 对象,并将 jBinder 和 pr 添加到 Map 缓存中。
  6. 最后将 ContentProviderHolder 对象返回。

AMS 的 publishContentProviders 发布 ContentProvider

当前 APP 的 ActivityThread 将 上一步生成的 ArrayList 队列,传递给服务进程 AMS 的 publishContentProviders 方法,发布所有 APP 中注册的所有 ContentProvider。

publishContentProviders() 方法 ContentProviderHolder 对象所保存的 ContentProvider(尤其是远程 Binder 对象)与 ContentProviderRecord 对象进行关联操作(ContentProviderRecord 对象是在应用解析 AndroidManifest 时创建的,它是 AMS 中保存的 APP 中的 ContentProvider 信息)。过程中会将ContentProviderRecord 对象存储在 mProviderMap 队列中,以后其他应用调用当前 APP 的 ContentProvider 时,AMS 会直接通过缓存队列 mProviderMap 取出 ContentProviderRecord 对象,并使用 ContentProviderRecord 对象包含的 Binder 对象(实现了 IContentProvider 接口)来进行远程调用。

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

发布过程的核心操作,就是将 ContentProvider 对象的相关信息更新并保存到 AMS 的一个过程。

到了这里,我们 APP 中的 ContentProvider 已经准备好了,其他应用随时可以对它们进行访问了。

ContentProvider 的解析、准备过程

上面已经讲解了 ContentProvider 的安装、发布过程。我们再回到 ActivityThread 的 handleBindApplication 中,installContentProviders(app, data.providers) 调用时,参数 data.providers 是在哪进行赋值的呢?

接下来,我们来分析 ContentProvider 的解析、准备过程。

AppBindData 的 providers 赋值过程

installContentProviders 方法的参数值 data.providers 是 AppBindData 对象的 providers 属性(List)。

AppBindData 对象的 providers 属性是在哪赋值的呢?这个我们要从 ActivityManagerService 的 attachApplicationLocked 说起。

ActivityManagerService 的 attachApplicationLocked 方法。

第一段,从安装包的 AndroidManifest 文件中获取 ContentProvider 注册信息:

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

这里,使用了 PMS 服务的 generateApplicationProvidersLocked 方法获取:

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

这时,我们从安装包的 AndroidManifest 中,获取了 ContentProvider 信息。

attachApplicationLocked 方法的第二段:

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

这里主要是调用 APP 所在进程的 bindApplication 方法(ApplicationThread 对象的 bindApplication 方法,这里是 IPC 调用),来启动 Application。

这里 thread 对象是 ApplicationThread 的实例,ApplicationThread 类 继承自 IApplicationThread.Stub,它是一个 Binder 对象,用来进行跨进程通信使用的(比如,这里和 AMS 服务进程进行通信)。

我们来看 ApplicationThread 的 bindApplication 方法:

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

AppBindData 对象就是在这里创建的,AppBindData 对象的 providers,也是在这里赋值的。到了这里,我们也已经找到了 providers 赋值的源头,并且知道了 providers 里面存储的其实是从安装包的 AndroidManifest 中获取的 ContentProvider 的注册信息。

这个方法最后,通过发生消息 H.BIND_APPLICATION,交给 H(一个 Handler)进行消息处理,我们来看:

                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;

这里调用了 ActivityThread 的 handleBindApplication 方法,也就是和 ContentProvider 的安装过程过程接上了,接下来就是 ContentProvider 的创建、安装以及发布过程了。

到了这里,ContentProvider 的源码分析过程已经完成了。

总结


  1. ContentProvider 是 Android 四大组件之一,是内容提供者,它使用统一接口的方式,为应用提供数据内容。

  2. ContentProvider 本质上是通过 Android 系统的 IPC 机制(Binder 机型),APP 在启动时,从 AndroidManifest 文件中解析 ContentProvider 的注册信息,经过处理后,传递给 AMS 服务,并创建相应的 ContentProviderRecord 对象,保存在 AMS 服务中。当客户端应用通过 ContentProvider 访问数据时,会通过 AMS 服务去查询并获取相应的 ContentProviderRecord 对象,获取到真正的 ContentProvider 的 Binder 对象,之后就可以进行数据访问了。

  3. AMS 的 attachApplicationLocked 方法,使用 PMS 服务,从安装包的 AndroidManifest 文件中获取 ContentProvider 注册信息。

  4. AMS 调用 APP 所在进程的 bindApplication 方法(ApplicationThread 对象的 bindApplication 方法,这里是 IPC 调用),来启动 Application,并将 ContentProvider 信息作为参数传递给 APP。

  5. App 所在进程的 ApplicationThread 的 bindApplication 方法,接收传递过来的 ContentProvider 信息,创建 AppBindData 对象,将 ContentProvider 信息赋值给 AppBindData 对象的 providers。

  6. 接着,通过发生消息 H.BIND_APPLICATION,交给 H(一个 Handler)进行消息处理,最终调用了 ActivityThread 的 handleBindApplication 方法,进行 ContentProvider 的创建、安装以及发布过程。

  7. handleBindApplication 方法中,调用 installContentProviders 方法进行 ContentProvider 的创建、安装、发布过程。

  8. installContentProviders 方法,创建 ContentProvider 对象,并获取 ContentProviderHolder 对象,将生成的 ContentProviderHolder 对象添加到一个 ArrayList 中(results)。

  9. 调用 AMS 的 publishContentProviders ,传递 results ,并发布 ContentProvider。

  10. 发布过程的核心操作,就是将 ContentProvider 对象的相关信息更新并保存到 AMS 的一个过程。


PS:更多文章,请查看系列文章–>《Android底层原理解析》专栏。
PS:更多文章,请查看系列文章–>《Android底层原理解析》专栏。
PS:更多文章,请查看系列文章–>《Android底层原理解析》专栏。

猜你喜欢

转载自blog.csdn.net/u011578734/article/details/111812636