【问题标题】:Call API in content provider for global search在内容提供者中调用 API 进行全局搜索
【发布时间】:2014-11-25 19:46:44
【问题描述】:

我们正在尝试连接我们的 AndroidTV 应用以将结果附加到全局搜索中。我遇到了一个问题,我无法通过 api 调用来获取结果,因为系统在主线程上调用了我的内容提供程序。

@Override
public Cursor query(Uri uri, String[] projection, String search, String[] selectionArgs, String searchOrder) {

    ... Logic here that calls the API using RxJava / Retrofit

    return cursor;
}


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/foo"
android:searchSettingsDescription="@string/foo_results"
android:includeInGlobalSearch="true"
android:searchSuggestAuthority="com.foo.search.provider"
android:searchSuggestIntentAction="android.intent.action.VIEW" />

<provider
   android:authorities="com.foo.search.provider"
   android:name=".search.GlobalSearchProvider"
   android:exported="true"/>

当我进行全局搜索时,我可以看到 ContentProvider#query 被调用。如果我尝试在当前线程上进行 api 调用,我会得到 networkonmainthreadexception。

我已尝试通知光标数据已更改,但也没有成功。

getContext().getContentResolver().notifyChange(Uri.parse("content://com.foo.test"), null);
...
cursor.setNotificationUri(getContext().getContentResolver(), Uri.parse("content://com.foo.test"));

我是否可以强制操作系统在单独的线程上调用内容提供程序,或者至少通知搜索光标有新内容?

谢谢

【问题讨论】:

    标签: android android-tv


    【解决方案1】:

    其中一种解决方案可以是设置内容提供者进程

    android:process:":androidtv"
    

    在进行网络调用之前将 ThreadPolicy 设置为 LAX

    ThreadPolicy tp = ThreadPolicy.LAX;
    StrictMode.setThreadPolicy(tp);
    

    通过在不同的进程中运行 contentprovider,即使查询运行在主线程上,也不会影响你的 UI 操作

    【讨论】:

    • 你能看看我下面的回答吗?
    • @dextor 我想您可以尝试更改您的可搜索信息出现在 SearchManager.getSearchablesInGlobalSearch() 列表中的顺序。我认为您可以通过更改可搜索活动的名称来做到这一点。这样您的应用搜索将由搜索应用最后完成,但我不确定它是否会起作用。此外,制作单独进程的目的是使查询不在主线程上运行,如果您只是设置策略松懈,查询仍将在主线程中运行,并且 UI 操作可能会延迟或者您可能会得到 ANR
    • 我的搜索结果已经显示为最后一个结果,所以这是不行的。我知道在单独的进程上运行“解决”了这个问题,但我注意到产生的滞后比在同一个进程上运行要高得多。我不知道为什么。
    • 延迟是由于您在返回结果之前阻塞了 UI 线程。在单独的进程上运行不会做任何事情,因为主 UI 线程将在继续之前等待结果,无论请求在您这边执行的哪个进程。
    • 很好的解决方案。我创建了一个 ContentProvider,它从网络查询结果并以 Cursor 的形式返回它。然后我使用 android Loader 在 RecyclerView 中显示数据。但我得到了 NetworkOnMainThreadException。我打电话给 StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX);在网络调用之前的我的 ContentProvider 查询方法中,它就像一个冠军。非常感谢@nandeesh
    【解决方案2】:

    我也为此苦苦挣扎,因为我没有找到当前接受的阻止 UI 可接受的答案。

    但是,根据 Google TV 团队的 Marc Bächinger 的说法,这只是模拟器的问题。在较新的版本中(例如当前可用硬件中的版本),搜索提供程序在后台线程中调用,这完全避免了该问题。

    我已经能够在 Nexus Player 上对其进行测试,并且可以确认它可以正常工作。

    来源:https://plus.google.com/+DanielCachapa/posts/dbNMoyoRGEi

    【讨论】:

      【解决方案3】:

      编辑答案

      我自己也遇到过这个问题,我不得不依赖接受的答案提出的解决方案。但是,我注意到在“全局搜索”框中输入时会有明显的滞后。这个滞后是:

      1. 由应用程序引起,因为删除它会使滞后消失
      2. 很可能是由于查询的应用程序同步等待 - 由于我们的应用程序执行两个网络请求,query() 方法需要时间才能完成,从而导致此延迟

      我发现不需要单独的进程 (:androidtv)。通过设置ThreadPolicy.LAX配置,网络请求仍然会执行而不抛出NetworkOnMainThreadException

      我还是不明白为什么会有延迟。


      原始答案

      我不认为公认的答案是正确的,虽然它确实有效。

      一旦调用了query() 方法,您应该生成一个新线程/任务/作业来执行网络调用(因此,避免使用NetworkOnMainThreadException),一旦适配器获得所需的数据,它将更新适配器。

      有不同的方法可以做到这一点。您可以使用 回调 或事件总线(例如,Otto)。这是我调用来更新适配器的方法:

      public void updateSearchResult(ArrayList<Data> result) {
          mListRowAdapter.clear();
          mListRowAdapter.addAll(0, result);
          HeaderItem header = new HeaderItem(0, "Search results", null);
          mRowsAdapter.add(new ListRow(header, mListRowAdapter));
      }
      

      【讨论】:

      • 我无法启动单独的线程,因为我无法向 AndroidTV 框架传达光标中的数据已更新。
      • 为什么不呢? (也许我错过了什么)
      • 我无法控制框架的功能,他们没有添加任何逻辑让我与它交流。
      • 我还是没抓住重点。是因为您使用的是 RxJava/Retrofit 吗?还是因为您有自己的提供商?因为在我的电视应用程序中,我按照我在答案中写的那样做,它按预期工作。
      • 为了向 AndroidTV 提供来自我们应用程序的搜索结果,我们需要通过光标传递数据,该光标是通过注册以提供全局结果的 ContentProvider 提供的。为了获得搜索结果,我需要查询我们的 API,该 API 必须位于单独的线程上或使用 ThreadPolicy.LAX。如果您在应用程序中显示搜索结果,则您的答案有效,因为您可以直接访问显示数据的适配器。对于全局搜索,我可以控制的只是传递给处理呈现数据的框架的光标。
      【解决方案4】:

      为了解决在查询方法中使用 API 在全局搜索中显示结果的问题,我基本上所做的是在获取 api 结果和查询 db 以返回游标的结果之间引入延迟。

      你可以通过

      private Cursor getSuggestions(final String query) {
          Cursor cursor;
          cursor = getCursor(query);
          if (cursor==null || cursor.getCount() == 0) {
          //apiCall
            try {
              Thread.sleep(X millis);
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
            cursor = getCursor(query);
          }
          return cursor;
        }
      

      将继续寻找,我们是否可以在不使用延迟的情况下重新连接某种钩子。

      【讨论】:

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