Android ContentProviderの使用状況とソースコード分析(Android Q)

AndroidContentProviderの使用


ContentProviderは、Androidの4つの主要コンポーネントの1つであり、コンテンツプロバイダーであり、統合されたインターフェイスを使用してアプリケーションにデータコンテンツを提供します。

ContentProviderを使用すると、データがどのプロセスにあり、プロセスがどのように保存されているかを気にすることなく、プロセス間のデータアクセスを非常に簡単に実現できます。

ContentProvider登録

ContentProviderはデータプロバイダーであり、私たちの目的はそれを使用して外の世界にデータを提供することです。

  1. まず、ContentProviderから継承するクラス(com.budaye.MyProviderなど)を定義し、クラスのいくつかのパブリックメソッドをコピーして、データ処理クエリ、追加、削除、更新などの操作を処理する必要があります。当社のデータは、ファイル、データベース、またはその他の方法で保存できます。

  2. AndroidManifestにカスタムContentProviderクラスを登録します。

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

ContentProviderの使用

ContentProviderのデータは、任意のプログラムまたはプロセスで呼び出すことができ、それらはすべて統合されたインターフェイス(クエリ、追加、削除、更新などを含む)です。

使用手順:

  1. 使用中は、コンテキストオブジェクトの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());
        } 
    }

AndroidContentProviderのソースコード分析


上記では、ContentProviderとその使用方法を紹介しましたが、非常に簡単だと思いますか?

次に、次の質問について考える必要があります。

  1. このような簡単な使い方はどのように実現されていますか?
  2. プロセス間通信をどのように簡素化しますか?(他のプロセスのContentProviderの呼び出しは、現在のプロセスと同じくらい簡単です)
  3. AndroidManifestに登録する必要があるのはなぜですか?
  4. ContentProviderはいつインストールされましたか?ContentProviderサブクラスはいつ呼び出されますか?

上記の質問で、ソースコードを見てみましょう。

ContentProviderのインストールプロセス

前回の記事「アプリケーションの作成とアプリの起動プロセスのソース分析」では、アプリの起動プロセス中にContentProviderがインストールされ、初期化されていることがわかりました。

アプリが起動すると、システムは最初に、APPのエントリメソッドであるActivityThreadのmain()メソッドを呼び出します。ActivityThreadは、アプリのメインスレッドまたはUIスレッドを表します。次に、ActivityThreadのattachメソッドを呼び出して、多くの初期化と準備を実行します。ActivityThreadのattachメソッドは、ActivityManagerServiceサービスのattachApplicationをリモートで呼び出して、アプリケーションを起動します。サービスAMSプロセスのActivityManagerServiceのattachApplicationLockedメソッドは、現在のプロセスのApplicationThreadのbindApplicationをリモートで呼び出して、アプリに対応するアプリケーションを作成して開始します。BIND_APPLICATIONメッセージはクラスHのhandleMessageを介して処理され、handleBindApplicationメソッドがアプリケーションの公式起動プロセスに対して呼び出されます。Applicationオブジェクトが作成された後、ContentProviderインストールプロセスが実行され、次にアプリケーションのonCreateが実行されます。オブジェクトは、アプリの起動プロセスを完了するために呼び出されます。

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のインストールプロセスが、アプリケーションのattachBaseContextの後、アプリケーションのonCreateメソッドの前に実行されることを意味します。一般に、補強などのソリューションを処理する場合、補強アプリケーションのContentProviderを最初にattachBaseContextで処理する必要があります。そうしないと、エラーが報告されます(インストール中にクラスが見つかりません)。これも理由です。
  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(結果)に追加します。
  2. AMSのpublishContentProvidersを呼び出し、結果を渡し、ContentProviderを公開します。

まず、installProviderプロセスを確認します。

installProviderメソッド

installProviderは、主にContentProviderオブジェクトの作成と初期化を実行し、リモート呼び出しに対応するバインダーペアを作成し、最後に関連情報をキャッシュとしてマップに保存します。

    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オブジェクトを作成することです。プロバイダーはTransportのインスタンスオブジェクト、jBinderはContentProviderに対応するBinderオブジェクト、prはProviderClientRecordオブジェクトです。ここでは、最初にContentProviderHolderオブジェクトを作成し、jBinderとprをマップキャッシュに追加します。
  6. 最後に、ContentProviderHolderオブジェクトが返されます。

AMSpublishContentProvidersはContentProviderを公開します

現在のAPPのActivityThreadは、前の手順で生成されたArrayListキューをサービスプロセスAMSのpublishContentProvidersメソッドに渡して、すべてのAPPに登録されているすべてのContentProviderを公開します。

publishContentProviders()メソッドによって保存されたContentProvider(特にリモートBinderオブジェクト)ContentProviderHolderオブジェクトは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のプロバイダー割り当てプロセス

installContentProvidersメソッドのパラメーター値data.providersは、AppBindDataオブジェクトのprovidersプロパティ(List)です。

AppBindDataオブジェクトのプロバイダープロパティはどこに割り当てられていますか?これは、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メソッドの2番目の段落:

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呼び出し)を呼び出して、アプリケーションを起動するためのものです。

ここでのスレッドオブジェクトはApplicationThreadのインスタンスであり、ApplicationThreadクラスはIApplicationThread.Stubを継承します。IApplicationThread.Stubは、プロセス間の通信(たとえば、ここではAMSサービスプロセスとの通信)に使用されるBinderオブジェクトです。

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オブジェクトのプロバイダーもここで割り当てられます。この時点で、プロバイダー割り当てのソースも見つかりました。プロバイダーに格納されているのは、実際にはインストールパッケージのAndroidManifestから取得したContentProviderの登録情報であることがわかります。

このメソッドの最後に、メッセージH.BIND_APPLICATIONがメッセージ処理のためにH(ハンドラー)に送信されます。以下を見てみましょう。

                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の4つの主要コンポーネントの1つであり、コンテンツプロバイダーであり、統合されたインターフェイスを使用してアプリケーションにデータコンテンツを提供します。

  2. ContentProviderは、基本的にAndroidシステム(バインダーモデル)のIPCメカニズムを使用します。APPが起動すると、AndroidManifestファイルからContentProviderの登録情報が解析され、処理後、AMSサービスに渡され、対応するContentProviderRecordオブジェクトが作成されます。稼働中のAMSに保存します。クライアントアプリケーションがContentProviderを介してデータにアクセスすると、AMSサービスを介して対応するContentProviderRecordオブジェクトをクエリおよび取得し、実際のContentProvider Binderオブジェクトを取得してから、データアクセスを実行できます。

  3. AMSのattachApplicationLockedメソッドは、PMSサービスを使用して、インストールパッケージのAndroidManifestファイルからContentProvider登録情報を取得します。

  4. AMSは、APPが配置されているプロセスのbindApplicationメソッド(ApplicationThreadオブジェクトのbindApplicationメソッド、ここではIPC呼び出し)を呼び出してアプリケーションを開始し、ContentProvider情報をパラメーターとしてAPPに渡します。

  5. AppプロセスのApplicationThreadのbindApplicationメソッドは、渡されたContentProvider情報を受け取り、AppBindDataオブジェクトを作成し、ContentProvider情報をAppBindDataオブジェクトのプロバイダーに割り当てます。

  6. 次に、メッセージH.BIND_APPLICATIONの発生を通じて、メッセージ処理のためにH(ハンドラー)に渡され、最後にActivityThreadのhandleBindApplicationメソッドが呼び出されて、ContentProviderの作成、インストール、および公開のプロセスが実行されます。

  7. handleBindApplicationメソッドで、installContentProvidersメソッドを呼び出して、ContentProviderを作成、インストール、および公開します。

  8. installContentProvidersメソッドは、ContentProviderオブジェクトを作成し、ContentProviderHolderオブジェクトを取得して、生成されたContentProviderHolderオブジェクトをArrayList(結果)に追加します。

  9. AMSのpublishContentProvidersを呼び出し、結果を渡し、ContentProviderを公開します。

  10. 公開プロセスの中核となる操作は、ContentProviderオブジェクトの関連情報を更新してAMSに保存するプロセスです。


PS:その他の記事については、一連の記事-> 「Androidの基本原則の分析」を確認してください
PS:その他の記事については、一連の記事-> 「Androidの基本原則の分析」を確認してください
PS:その他の記事については、一連の記事-> 「Androidの基本原則の分析」を確認してください

おすすめ

転載: blog.csdn.net/u011578734/article/details/111812636