web-dev-qa-db-ja.com

ContentProviderClientとContentResolverを使用してコンテンツプロバイダーにアクセスする

Androidコンテンツプロバイダー のドキュメント)は、getContentResolver()から取得したContentResolverを使用してコンテンツにアクセスする方法を説明しています。

ただし、getContentResolver().acquireContentProviderClient(authority)から取得できるContentProviderClientもあります。プロバイダーからのコンテンツにアクセスするためにContentResolverで利用できるのとほぼ同じメソッドを提供しているようです。

ContentProviderClientを直接使用するのではなく、ContentResolverをいつ使用すればよいですか?メリットは何ですか?

63

あなたのAndroidデバイスには多くのデータベースがあり、各データベースは一意のコンテンツ機関によって識別されます。これはcontent:// uriの「ドメイン名」に相当する部分です-最初のスラッシュの前のすべて。

ContentResolverは、_String contentAuthority_からContentProviderへのマッピングを提供するデータを格納します。 ContentResolver.query()またはupdate()またはあなたが持っているものを呼び出すと、URIはそのコンポーネントに分解され、contentAuthority文字列が識別されます。contentResolverは一致する文字列をそのマップで検索する必要があります。クエリを適切なプロバイダーに送信します。 URIは呼び出しごとに異なる場合があり、異なるcontentAuthorityも含まれるため、この高価な検索は呼び出しごとに行われます。さらに、その特定のプロバイダーへの接続のセットアップと切断に関連するコストが発生する可能性があります-呼び出し間で再利用することはできません。そこに含まれるオーバーヘッドはわかりませんが、これはかなり深いOSレベルのコードです。

対照的に、acquireContentProviderClient(authority)を呼び出すと、「必要なプロバイダーは何ですか?」ルックアップは1回行われ、ContentProviderClientが与えられます。これは、基本的にContentProviderへの直接リンクです。 (あなたとプロバイダーの間には、スレッド間通信と並行処理ロックを含む少しの接着剤があります)。ただし、ContentProviderClientを使用すると、要求した権限についてプロバイダーに直接話しかけます。これにより、「どのプロバイダーが必要ですか?」を常に再計算する無駄がなくなります。

注:Per acquireContentProviderClient()documentation :ContentProviderClientを取得する場合、 "呼び出し元は ContentProviderClient.release() を呼び出して、プロバイダーとのやり取りが完了したことを示します。これにより、システムがプロバイダーを解放できるようになり、それをアクティブに保つ他の理由がないと判断されます。 "したがって、基本的に、古いクライアントを開いたままにすると、プロバイダーはバックグラウンドでサービスとして実行し続ける必要があります。だから、クリーンアップすることを忘れないでください!

まとめ:

さまざまなcontentAuthoritiesへの多くの呼び出し:ContentResolverを使用します。

同じ機関への繰り返しの呼び出し:ContentProviderClientを取得して使用します。完了したら、必ずrelease()してください。

90
jcwenger

わかりましたが、アクティビティと同じプロセスでContentProviderが実行されている場合にのみ機能することに注意してください。

メソッドgetLocalContentProvider()のドキュメントからのメモ:

ContentProviderが別のプロセスで実行されている場合、nullが返されます。これは、プロバイダーと同じプロセスで実行していて、その実装の詳細に直接アクセスしたい場合に使用できます。

7
Jokii

もう1つのインポートの違いは、ContentProviderClientをカスタムプロバイダーオブジェクトにキャストして、CRUD以外のメソッドにアクセスできることです。

ContentProvider cp = getContentResolver().acquireContentProviderClient(mCurUri).getLocalContentProvider();
yourProvider fld = (yourProvider)cp;
fld.query(...);           // you can query as ContentResolver
fld.addFolder(newFolder); // also can invoke the extend method of your custom ContentProvider
4
guangmao.yu

次の違いが見つかりました:アプリAで独自のカスタムコンテンツプロバイダーを作成しました。アプリBでホームスクリーンウィジェットを作成しました。ウィジェットからContentResolverを介してアプリAのContentProviderにアクセスしようとすると、「プロバイダーの検索に失敗しました情報」エラー。代わりに、ContentResolverを介してContentProviderClientを取得し、ContentProviderClientを介してクエリを実行すると、機能します。私は何も変更する必要はありませんでした。ContentResolverの代わりにContentProviderClientを使用するだけです。私はその振る舞いについての本当の説明がなく、なぜそれがそのようなものであるかについて、インターネット上で情報を見つけられませんでした。これがウィジェットの特別な癖であるかどうかはわかりません。アプリBのアクティビティから試したわけではないためです(アプリBは単なるウィジェットであり、アクティビティはありません)。

2
mrd

ContentProviderClientの使用法の1つは、テストでContentProviderのいくつかのメソッドにアクセスするのに役立ちます。たとえば、ユニットテストで shutdown() メソッドを使用して、複数のコンテンツプロバイダーをインスタンス化する複数のテストを回避しています。

次のようにContentProvider#shutdown()を実装します。

_@Override
public void shutdown() {
    openHelper.close();
    super.shutdown();
}
_

そして、テストメソッドの最後で、ContentProviderClientを使用してshutdown()を呼び出し、テストをクリーンアップして、他のテストがコンテンツプロバイダーを使用できるようにします。

_getContext()
       .getContentResolver()
       .acquireContentProviderClient(URI)
       .getLocalContentProvider()
       .shutdown();
_
0