ContentProviderの詳細分析

10425 ワード

原文アドレスはブログ円で、もう使わないで、移転してきました.
ContentProviderはAndroidの4つのコンポーネントの1つであり、プロセス間でのデータアクセスの重要な役割を果たしています.本文は一回のContentProviderアクセスから着手して、それがどのようにプロセス間のデータアクセスを完成したのかを分析します.
プロセス間である以上、クライアント・プロセスとContentProviderプロセスが必要です.まず、クライアント・プロセスからContentProviderプロセスにアクセスする方法を分析します.Query操作を例にとると、一般的にContentProviderにアクセスする必要がある場合、このような文が実行されます.
getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

ContentResolverとは何ですか?Googleの解釈は「This class providesアプリケーションアクセスto the content model」だ.実際にContentResolverを通じてIContentProviderオブジェクトを取得し、IContentProviderオブジェクトを通じてIPC通信を可能にしました.getContentResolver()メソッド定義Contextクラスでは、実際にContextは抽象クラスであり、クライアントアプリケーションではgetContext()が実際にContextImpオブジェクトを返す、getContentResolver()メソッドはContextImpで定義.JAvaでは、最終的にContextImpの内部クラスApplicationContentResolverに戻り、名前から見るとApplicationレベルのオブジェクトです.ApplicationContentResolver後で、queryメソッドが何をしているかを見てみましょう.
public final Cursor query(final Uri uri, String[] projection,
            String selection, String[] selectionArgs, String sortOrder,
            CancellationSignal cancellationSignal) {
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        try {
            long startTime = SystemClock.uptimeMillis();

            ICancellationSignal remoteCancellationSignal = null;
            if (cancellationSignal != null) {
                cancellationSignal.throwIfCanceled();
                remoteCancellationSignal = unstableProvider.createCancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);
            }
            Cursor qCursor;
            try {
                qCursor = unstableProvider.query(uri, projection,
                        selection, selectionArgs, sortOrder, 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(uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }
            // force query execution
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
            // Wrap the cursor object into CursorWrapperInner object
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
            stableProvider = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }

上のコードには4つの重要なステップがあります。


1. acquireUnstableProvider 2. unstableProvider.query(......) 3. qCursor.getCount(); 4. return new CursorWrapperInner(......)
次に、この4つの重要なステップを一歩一歩分析します.

ステップ1:acquireUnstableProvider


一見変な感じがしますが、なぜUnstableのラベルをつけたのですか?まさかstableがあるのではないでしょうか.確かに、この時点のContentResolverが実際にApplicationContentResovlerオブジェクトであることを知っています.ApplicationContentResovlerを見てみましょう.
private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        private final UserHandle mUser;

        public ApplicationContentResolver(
                Context context, ActivityThread mainThread, UserHandle user) {
            super(context);
            mMainThread = Preconditions.checkNotNull(mainThread);
            mUser = Preconditions.checkNotNull(user);
        }

        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true);
        }

        @Override
        protected IContentProvider acquireExistingProvider(Context context, String auth) {
            return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true);
        }

        @Override
        public boolean releaseProvider(IContentProvider provider) {
            return mMainThread.releaseProvider(provider, true);
        }

        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
        }

        @Override
        public boolean releaseUnstableProvider(IContentProvider icp) {
            return mMainThread.releaseProvider(icp, false);
        }

        @Override
        public void unstableProviderDied(IContentProvider icp) {
            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
        }
    }

実際には、stableであるかどうかは、Activity ThreadのacquireProviderメソッドが呼び出され、最後のパラメータboolean stableと区別されます.このメカニズムはAPI 16が導入したものであり,最後に説明する.今、私たちはそれが最終的にActivity ThreadのacquireProviderに着いたことを知っていればいいです.Activity ThreadのacquireProviderメソッドでは、まずacquireExistingProviderに行きます.これはクラスキャッシュの場所から保存されているContentProviderオブジェクトを読み込み、存在しない場合はActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable);を呼び出します.ここからActivity Management Serviceが登場します.上のコードは最終的にbinder通信でActivity Management ServiceのgetContentProviderImplを呼び出します.この論理は複雑で、Androidコンポーネントの起動プロセスに関連しています.クライアント呼び出しがActivityManagerNative.getDefault().getContentProviderでブロックされていることを知っておくだけで、Activity Management ServicesがターゲットContentProviderプロセスを起動した後(ContentProviderプロセスがすでに存在する場合は再起動する必要はありません)、ターゲットContentProviderのインスタンスに戻ります.ここで説明する必要があるのは、ContentProviderは、android:multiprocessという属性をManifestで構成することができ、デフォルト値はfalseであり、ContentProviderが一例であることを示し、どのクライアントアプリケーションのアクセスもContentProviderオブジェクトである(もちろん、UriまたはAuthority nameが同じである必要がある)場合、trueに設定すると、ContentProviderにアクセスするプロセスごとにインスタンスが作成されます.Android:multiprocessのデフォルト値はfalseなので、自分のContentProviderを書くときは同時の状況に注意しましょう.
少し話が遠くなって、私たちの前のステップに戻って、私たちはすでにContentProviderの例を得て、この時第1歩は完成して、次に第2歩を見ます.

ステップ2:unstableProvider.query(......)


unstableProviderは実際にはIContentProviderのインスタンスであり、IContentProviderはIPC通信を行うインタフェースであり、このqueryは実際にターゲットContentProviderのqueryメソッドを呼び出している.もちろん、ターゲットContentProviderのqueryメソッドを本当に呼び出す前に、enforceReadPermissionメソッドを通過する必要がある.このステップは主にこのContentProvierにexportがあるかどうかを見ることである.読み書き権限など(enforceReadPermissionメソッドは読み書き権限のみを判断します).次にqueryメソッドを実行し、cursorオブジェクトを返します.
以上が第2歩の大まかな論理ですが、こんなに簡単に終わったとは思わないでください.IContentProviderのqueryはプロセスをまたいでいるので、ContentProviderのqueryの方法はいろいろあることを知っています.データベースにアクセスしてSQLiteCursorを返す人もいれば、MatrixCursorを返す人もいるなど、IContentProviderが返すcursorはいったい何なのでしょうか.IContentProviderのこのIPC通信がどうなっているのか見てみましょう
IPC通信には両端が必要であり、我々の例では、この両端がContentProviderProxyとContentProviderNativeであり、まずContentProviderProxyのqueryメソッドを実行し、binder通信によりContentProviderNativeのonTransactメソッドを実行する.ContentProviderProxyのqueryメソッドには、次の5つの主要な手順があります.
  • new BulkCursorToCursorAdaptorオブジェクト-adaptor
  • パディングdata binder通信用
  • mRemoteを呼び出す.ContentProviderNativeのonTransactメソッドが
  • を返すまでブロックされたプロセスです
  • replyデータを読み出し、BulkCursorDescriptorをnewし、adaptor
  • を初期化する.
  • return adaptor

  • ContentProviderNativeのonTransactはContentProviderのqueryメソッドを呼び出し、queryが返すcursorに従ってCursorToBulkCursorAdaptorオブジェクトを初期化し、最終的にBulkCursorDescriptorオブジェクトをreplyに書き込む.
    これで、ContentProvider queryメソッドがどのようなcursorを返すかにかかわらず、最終的にクライアントプロセスがBulkCursorToCursorAdaptorオブジェクトにカプセル化されることになります.では、このBulkCursorToCursorAdaptorオブジェクトは、クライアントがqueryを呼び出して返す最終的なタイプではないでしょうか.焦らないで、下を見てください.

    ステップ3:qCursor.getCount();


    このステップは鶏の肋骨のように見えますが、実際にはSQLiteCursorのメモリ共有というSQLiteCursorの設計のポイントに関連しています.getCountはSQLiteCursorのfillWindowを呼び出します.後でSQLiteCursorについてお話しします.ここではデータベースqueryを強制的に実行していることを知っていればいいです.

    ステップ4:return new CursorWrapperInner(...)


    はい、わかりました.クライアント呼び出しqueryは、ContentResolverの内部クラスであるCursorWrapperInnerタイプを返します.実際,我々がよく用いているgetCount,onMoveなどのいくつかの列法は,BulkCursorToCursorAdaptorとCursorToBulkCursorAdaptorのインタラクションによって実現されている.
    これでContentProviderのアクセスプロセスは終了し、次に最初に買ったピット:unstableとstableのContentProviderについてお話しします.
    4.1の前に、私たちはこのようなシーンに出会ったことがあります.私たちのアプリケーションはContentProviderにアクセスしましたが、このContentProviderは気にしていません.この時、私たちのアプリケーションも連帯して殺されます.これはAndroidがデータセキュリティを考慮した決定であるが、Googleもこのような方式があまり友好的ではないと感じているようで、4.1以降にstableとunstableの概念を提案した.ContentResolverのqueryメソッドでは、unstableのContentProviderがデフォルトで使用されます.次のコードを見てください
    Cursor qCursor;
                try {
                    qCursor = unstableProvider.query(uri, projection,
                            selection, selectionArgs, sortOrder, 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(uri, projection,
                            selection, selectionArgs, sortOrder, remoteCancellationSignal);
                }
    

    ContentResolver queryの一部ですが、unstable ContentProviderは、query中にDeadObjectExeptionが発生するとキャプチャされ、stableのContentProviderが再取得されることがわかります.実はstableとunstableを深く分析したContentProviderには多くの内容があるので、後で時間があれば話しましょう.