using ContentProviderClient vs ContentResolver to access content provider
Asked Answered
D

5

65

The documentation on Android content providers describes using a ContentResolver, obtained from getContentResolver(), to access the content.

However there is also a ContentProviderClient, which can be obtained from getContentResolver().acquireContentProviderClient(authority). It seems to provide more or less the same methods available in the ContentResolver for accessing content from the provider.

When should I use a ContentProviderClient instead of just using the ContentResolver directly? What are the benefits?

Deadradeadweight answered 22/2, 2011 at 22:39 Comment(0)
A
98

Your android device has many databases, each of which is identified by a unique Content Authority. This is the "domain name" equivalent part in the content:// uri -- everything before the first slash.

ContentResolver stores data providing a mapping from String contentAuthority to ContentProvider. When you call ContentResolver.query() or update() or what have you, the URI is parsed apart into its components, the contentAuthority string is identified, and contentResolver has to search that map for a matching string, and direct the query to the right provider. This expensive search occurs during every single call, because the URI might be different from call to call, with a different contentAuthority as well. Additionally, there may be some costs involved in setting up and tearing down a connection to that specific provider -- It can't be reused across calls. I'm not sure of the overhead involved there, that's some pretty deep OS level code.

By contrast, when you call acquireContentProviderClient(authority), that "what-provider do I need?" lookup is done once, and you are given a ContentProviderClient which is essentially a direct link to the ContentProvider. (There's a bit of glue between you and the provider that involves cross-thread communication and concurrency locking). However, when you use ContentProviderClient, you will talk directly to the Provider for the authority you requested. This removes the waste of constantly re-computing "which provider do I want?"

NOTE: Per acquireContentProviderClient() documentation: If you obtain a ContentProviderClient, "The caller must indicate that they are done with the provider by calling ContentProviderClient.release() which will allow the system to release the provider it it determines that there is no other reason for keeping it active." So essentially, leaving a stale Client open will force the Provider to keep running as a service in the background. So, remember to clean up!

Summary:

Many calls to varying contentAuthorities: Use ContentResolver.

Repeated calls to the same Authority: Obtain and use ContentProviderClient. Remember to release() it when you're done.

Abruzzi answered 8/3, 2011 at 14:14 Comment(6)
Thanks. I'm curious as to how long to keep a ContentProviderClient around. I have an app which browses the content from a specific provider; it includes a number of activities. I could acquire and release it in each activity, either at onCreate/onDestroy or at onStart/onStop; or I could cache a copy at Application level which is only released when all activities (or the root activity) have been destroyed. Any thoughts?Deadradeadweight
I'd keep it around on a per-activity basis. If you're doing single queries and keeping a cursor, like that you bind to a ListView with a CursorAdapter, then don't bother -- just go straight to the resolver for that case. If you do that model, ContentObservers will ensure automatic updates occur if the underlying DB changes. It's a nice model.Abruzzi
There's this little difference when using ContentProviderClient. It's methods throw RemoteException, whereas those of ContentResolver do not. Could someone elaborate: ContentResolver obviously handles these cases on it's own, how does it do it? And what's the right way to do it when using ContentResolverClient?Bluegill
ContentProviderClient seems to require you to catch Exceptions though. Doesn't that slow down the query?Sales
@ChristopherMasser, when you use ContentResolver, it opens and allocates a ContentProviderClient, catching any exceptions that might occur, returns you the result, and maybe decides to destroy the ContentProviderClient, after... batches of calls, or every single call, or, who knows. So, no, the exceptions get caught either way, and ContentResolver is slower. Slower enough to matter? Who knows. But it's not like ContentResolver can just not catch the exceptions.Abruzzi
I tried this out and carried out a lot of tests - the performance gain is almost negligible - it averaged around 0.7 milliseconds per query.Remains
C
8

Ok, but be aware that it works only when ContentProvider running in this same process as Activity.

Note from documentation for method getLocalContentProvider():

If the ContentProvider is running in a different process then null will be returned. This can be used if you know you are running in the same process as a provider, and want to get direct access to its implementation details.

Coequal answered 29/10, 2012 at 7:54 Comment(1)
It concerns post from guangmao.yuCoequal
J
6

I think the another import difference is ContentProviderClient can be cast into your custom provider object and access other method besides 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
Jobye answered 23/7, 2012 at 12:11 Comment(0)
C
2

I found the following difference: I wrote my own custom contentprovider in app A. I wrote a homescreen Widget in App B. When I tried to access the ContentProvider of app A via a ContentResolver from my widget, I got an "failed to find provider info" error. When I instead would aquire a ContentProviderClient through the ContentResolver and query through the ContentProviderClient, it would work. I had to change nothing else, only use the ContentProviderClient instead of the ContentResolver. I have no real explanation for that behaviour and found no information on the internet, as to why it is like that. I do not know, if this is a special quirk of widgets, because I did not try it from an activity in app B (app B is a mere widget, with no activity).

Coloring answered 9/3, 2012 at 21:17 Comment(0)
F
0

One of the usages of ContentProviderClient is helpful for accessing some of the methods of ContentProvider in testing. For example I use the shutdown() method in unit tests to avoid multiple tests instantiating multiple content providers.

Implement ContentProvider#shutdown() like this:

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

And at the end of the test method, call shutdown() using the ContentProviderClient to clean up the test so that other tests can use the content provider:

getContext()
       .getContentResolver()
       .acquireContentProviderClient(URI)
       .getLocalContentProvider()
       .shutdown();
Fisherman answered 8/4, 2016 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.