ContentProviderの詳細分析
10425 ワード
原文アドレスはブログ円で、もう使わないで、移転してきました.
ContentProviderはAndroidの4つのコンポーネントの1つであり、プロセス間でのデータアクセスの重要な役割を果たしています.本文は一回のContentProviderアクセスから着手して、それがどのようにプロセス間のデータアクセスを完成したのかを分析します.
プロセス間である以上、クライアント・プロセスとContentProviderプロセスが必要です.まず、クライアント・プロセスからContentProviderプロセスにアクセスする方法を分析します.Query操作を例にとると、一般的にContentProviderにアクセスする必要がある場合、このような文が実行されます.
ContentResolverとは何ですか?Googleの解釈は「This class providesアプリケーションアクセスto the content model」だ.実際にContentResolverを通じてIContentProviderオブジェクトを取得し、IContentProviderオブジェクトを通じてIPC通信を可能にしました.
1.
次に、この4つの重要なステップを一歩一歩分析します.
一見変な感じがしますが、なぜUnstableのラベルをつけたのですか?まさかstableがあるのではないでしょうか.確かに、この時点のContentResolverが実際にApplicationContentResovlerオブジェクトであることを知っています.ApplicationContentResovlerを見てみましょう.
実際には、stableであるかどうかは、Activity ThreadのacquireProviderメソッドが呼び出され、最後のパラメータboolean stableと区別されます.このメカニズムはAPI 16が導入したものであり,最後に説明する.今、私たちはそれが最終的にActivity ThreadのacquireProviderに着いたことを知っていればいいです.Activity ThreadのacquireProviderメソッドでは、まずacquireExistingProviderに行きます.これはクラスキャッシュの場所から保存されているContentProviderオブジェクトを読み込み、存在しない場合は
少し話が遠くなって、私たちの前のステップに戻って、私たちはすでにContentProviderの例を得て、この時第1歩は完成して、次に第2歩を見ます.
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の 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を呼び出して返す最終的なタイプではないでしょうか.焦らないで、下を見てください.
このステップは鶏の肋骨のように見えますが、実際にはSQLiteCursorのメモリ共有というSQLiteCursorの設計のポイントに関連しています.getCountはSQLiteCursorのfillWindowを呼び出します.後でSQLiteCursorについてお話しします.ここではデータベースqueryを強制的に実行していることを知っていればいいです.
はい、わかりました.クライアント呼び出し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がデフォルトで使用されます.次のコードを見てください
ContentResolver queryの一部ですが、unstable ContentProviderは、query中にDeadObjectExeptionが発生するとキャプチャされ、stableのContentProviderが再取得されることがわかります.実はstableとunstableを深く分析したContentProviderには多くの内容があるので、後で時間があれば話しましょう.
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つの主要な手順があります.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には多くの内容があるので、後で時間があれば話しましょう.