使用 ContentProviderClient 与 ContentResolver 访问内容提供者

发布于 2024-10-18 10:28:56 字数 498 浏览 6 评论 0原文

有关 Android 内容提供程序的文档介绍了如何使用 ContentResolver,从 getContentResolver() 获取,用于访问内容。

不过,还有一个 ContentProviderClient,可以从 getContentResolver().acquireContentProviderClient(authority) 获取。它似乎提供了或多或少与 ContentResolver 中可用的方法相同的方法,用于从提供者访问内容。

我什么时候应该使用 ContentProviderClient 而不是直接使用 ContentResolver?有什么好处?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

怎言笑 2024-10-25 10:28:56

您的 Android 设备有许多数据库,每个数据库都由唯一的内容权威机构标识。这是 content:// uri 中“域名”的等效部分——第一个斜杠之前的所有内容。

ContentResolver 存储提供从 String contentAuthorityContentProvider 映射的数据。当您调用 ContentResolver.query()update() 或其他方法时,URI 将被解析为其组件,识别 contentAuthority 字符串,并且 contentResolver 必须在该映射中搜索匹配的字符串,并将查询定向到正确的提供者。这种昂贵的搜索会在每次调用期间发生,因为每次调用的 URI 可能都不同,并且具有不同的 contentAuthority。此外,建立和断开与特定提供商的连接可能会涉及一些成本——它不能在调用之间重复使用。我不确定其中涉及的开销,这是一些相当深的操作系统级别代码。

相比之下,当您调用 acquireContentProviderClient(authority) 时,“我需要什么提供者?”查找完成一次,然后您将获得一个 ContentProviderClient,它本质上是到 ContentProvider 的直接链接。 (您和提供者之间有一些联系,涉及跨线程通信和并发锁定)。但是,当您使用 ContentProviderClient 时,您将直接与 Provider 对话以获取您请求的权限。这消除了不断重新计算“我想要哪个提供商?”的浪费。

注意:根据 acquireContentProviderClient() 文档:如果您获取 ContentProviderClient,“调用者必须通过调用 ContentProviderClient.release() 这将允许系统在确定没有其他原因保持其活动状态时释放提供程序。” 因此,本质上,让过时的客户端保持打开状态将迫使提供者在后台作为服务继续运行。所以,一定要记得清理哦!

摘要:

多次调用不同的 contentAuthorities: 使用 ContentResolver

重复调用同一权限:获取并使用ContentProviderClient。完成后记得release()它。

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.

悟红尘 2024-10-25 10:28:56

好的,但请注意,它仅在 ContentProvider 与 Activity 在同一进程中运行时才有效。

方法 getLocalContentProvider() 的文档注释:

如果 ContentProvider 在不同的进程中运行,则为 null
将被退回。如果您知道自己正在运行,则可以使用此功能
与提供商相同的流程,并且希望直接访问其
实施细节。

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.

忆梦 2024-10-25 10:28:56

我认为另一个重要区别是 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

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
二智少女猫性小仙女 2024-10-25 10:28:56

我发现了以下区别:
我在应用程序 A 中编写了自己的自定义内容提供程序。
我在 App B 中编写了一个主屏幕 Widget。
当我尝试通过小部件中的 ContentResolver 访问应用程序 A 的 ContentProvider 时,出现“无法找到提供程序信息”错误。
当我通过 ContentResolver 获取 ContentProviderClient 并通过 ContentProviderClient 查询时,它会起作用。
我不必更改任何其他内容,只需使用 ContentProviderClient 而不是 ContentResolver。
我对这种行为没有真正的解释,也没有在互联网上找到任何信息来解释为什么会这样。
我不知道这是否是小部件的一个特殊怪癖,因为我没有从应用程序 B 中的活动中尝试它(应用程序 B 只是一个小部件,没有活动)。

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).

童话里做英雄 2024-10-25 10:28:56

ContentProviderClient的用法之一是有助于在测试中访问ContentProvider的一些方法。例如,我使用 shutdown() 方法在单元测试中以避免实例化多个内容提供者的多个测试。

像这样实现 ContentProvider#shutdown()

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

并在测试方法结束时,使用 ContentProviderClient 调用 shutdown() 来清理测试以便其他测试可以使用内容提供程序:

getContext()
       .getContentResolver()
       .acquireContentProviderClient(URI)
       .getLocalContentProvider()
       .shutdown();

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文