在内容提供程序中调用API以进行全局搜索

7
我们正在尝试将我们的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异常。
我已经尝试通过notifty游标来通知数据已经更改,但是也没有成功。
getContext().getContentResolver().notifyChange(Uri.parse("content://com.foo.test"), null);
...
cursor.setNotificationUri(getContext().getContentResolver(), Uri.parse("content://com.foo.test"));

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

谢谢

5个回答

6

其中之一的解决方案是设置内容提供程序进程

android:process:":androidtv"

在进行网络调用之前,将ThreadPolicy设置为LAX。
ThreadPolicy tp = ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);

通过在不同的进程中运行内容提供程序,即使查询在主线程上运行,也不会影响您的UI操作。


你能否请看一下我下面的回答? - Sebastiano
@dextor,我想你可以尝试更改SearchManager.getSearchablesInGlobalSearch()列表中可搜索信息的顺序。我认为你可以通过更改可搜索活动的名称来实现。这样,搜索应用程序将最后完成应用程序搜索,但我不确定它是否有效。另外,创建单独的进程的目的是使查询不在主线程上运行。如果你只设置lax策略,则查询仍将在主线程上运行,并且可能会延迟UI操作或导致ANR。 - nandeesh
我的搜索结果已经显示为最后一个结果,所以这是不行的。我知道在单独的进程上运行可以“解决”问题,但我注意到产生的延迟比在同一进程上运行要高得多。而且我无法弄清楚原因。 - Sebastiano
延迟是因为您阻塞了UI线程,直到结果返回。 在单独的进程上运行并没有什么作用,因为无论请求在您的哪个进程上执行,主UI线程都会等待结果才会继续。 - Cachapa
很棒的解决方案。我创建了一个ContentProvider,它从网络查询结果并以Cursor的形式返回。然后我使用android Loader在RecyclerView中显示数据。但是我遇到了NetworkOnMainThreadException。在我的ContentProvider查询方法中,在网络调用之前调用StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX);,它像冠军一样工作了。非常感谢@nandeesh。 - Pioneer

1

我也曾遇到过这个问题,因为我不认同目前被接受的阻塞用户界面(UI)的方案。

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

我已经在Nexus Player上测试过,确认它可以正常工作。

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


0

因为我对Android线程不是很熟悉。对于任何和我有同样问题的人来说,主要问题在于内容提供程序中的query()方法不在UI线程上运行。

请不要使用异步函数来执行HTTP请求,然后更新游标,而是请使用同步函数来执行HTTP请求,然后返回带有所需数据的游标。


0
为了通过查询方法在全局搜索中使用 API 来解决显示结果的问题,我基本上是通过在获取 API 结果和查询结果以返回光标之间引入延迟来实现的。
你可以通过这种方式来实现:
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;
  }

会继续寻找,看看是否可以找到一种不使用延迟的方式重新连接。


0

编辑回答

我自己也遇到了这个问题,只好依赖于接受的答案提出的解决方案。但是,我注意到在“全局搜索”框中输入时有明显的延迟。这种延迟可能是由以下原因引起的:

  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框架进行通信,告知游标中的数据已更新。 - Darussian
为什么不呢?(也许我漏掉了什么) - Sebastiano
我还是没有明白重点。这是因为你使用了RxJava/Retrofit吗?还是因为你有自己的提供者?因为在我的电视应用程序中,我按照我的答案所写的方式操作,它可以正常工作。 - Sebastiano
为了向AndroidTV提供我们应用程序的搜索结果,我们需要通过ContentProvider传递数据的游标,并注册提供全局结果。为了获取搜索结果,我需要查询我们的API,这必须在单独的线程上或使用ThreadPolicy.LAX。如果您正在应用程序中呈现搜索结果并且可以直接访问显示数据的适配器,则您的答案有效。对于全局搜索,我只能控制传递给处理数据呈现的框架的游标。 - Darussian
我也遇到了和你完全相同的问题。上面发布的解决方案是有效的,但它会在搜索界面中引入延迟(如果我卸载应用程序,则不会出现此问题)。很可能是由于网络调用引起的。你有同样的情况吗? - Sebastiano
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接