【问题标题】:using ContentProviderClient vs ContentResolver to access content provider使用 ContentProviderClient 与 ContentResolver 访问内容提供者
【发布时间】:2011-02-22 22:39:57
【问题描述】:

documentation on Android content providers 描述了使用从getContentResolver() 获得的ContentResolver 来访问内容。

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

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

【问题讨论】:

    标签: android android-contentprovider android-contentresolver


    【解决方案1】:

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

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

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

    注意: Per acquireContentProviderClient() documentation:如果您获得 ContentProviderClient,“调用者必须通过调用 ContentProviderClient.release() 来表明他们已完成与提供者的合作,这将允许系统释放它确定没有其他原因使其保持活动状态的提供者。” 因此,本质上,让陈旧的客户端保持打开状态将迫使提供者继续在后台作为服务运行。所以,记得清理干净!

    总结:

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

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

    【讨论】:

    • 谢谢。我很好奇将ContentProviderClient 保留多长时间。我有一个应用程序可以浏览特定提供商的内容;它包括一些活动。我可以在每个活动中获取和释放它,可以在onCreate/onDestroyonStart/onStop;或者我可以在Application 级别缓存一个副本,该副本仅在所有活动(或根活动)被销毁时才释放。有什么想法吗?
    • 我会在每个活动的基础上保留它。如果您正在执行单个查询并保留一个游标,就像您使用 CursorAdapter 绑定到 ListView 一样,那么请不要打扰 - 直接进入该案例的解析器。如果您使用该模型,ContentObservers 将确保在底层数据库更改时发生自动更新。这是一个很好的模型。
    • 在使用 ContentProviderClient 时有一点不同。它的方法会抛出 RemoteException,而 ContentResolver 的方法不会。有人可以详细说明一下: ContentResolver 显然是自己处理这些情况的,它是如何做到的?使用 ContentResolverClient 时正确的做法是什么?
    • ContentProviderClient 似乎要求您捕获异常。这不会减慢查询速度吗?
    • @ChristopherMasser,当您使用 ContentResolver 时,它会打开并分配一个 ContentProviderClient,捕获可能发生的任何异常,返回结果,并可能决定销毁 ContentProviderClient,在...批量调用之后,或者每一个电话,或者,谁知道呢。所以,不,无论哪种方式都会捕获异常,并且 ContentResolver 更慢。慢到重要吗?谁知道。但这不像 ContentResolver 只能捕获异常。
    【解决方案2】:

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

    方法getLocalContentProvider()的文档注释:

    如果 ContentProvider 在不同的进程中运行,则为 null 将被退回。如果您知道自己在 与提供者相同的进程,并希望直接访问其 实现细节。

    【讨论】:

    • 涉及到guangmao.yu的帖子
    【解决方案3】:

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

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

      【讨论】:

        【解决方案5】:

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

        像这样实现ContentProvider#shutdown()

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多