6

我的 ActionBar 中有一个 SearchView,它与 ContentProvider 连接以提供搜索建议。这些建议不是来自数据库(与 ContentProvider 一样),而是来自 Web 服务。这就是为什么我必须异步处理 ContentProvider 的光标。到目前为止,我的代码有效,但搜索建议始终是“后面”一个字母:

输入后"the",我得到了之前搜索的所有结果 =>"th"

输入后"they",我得到了之前搜索的所有结果 =>"the"

如何告诉 SearchView 游标中有新结果?我查看了 ContentObserver 和 ContentResolver().notifyChange(),但它们实际上不可能在 SearchView 的上下文中使用。

到目前为止,这是我的代码。重要的部分在 ContentProvider 的 onResponse-callback 中。我创建了一个新的 MatrixCursor 并使用它来覆盖成员 MatrixCursor。

AutocompleteSuggestionProvider 扩展了 ContentProvider

@Override
public Cursor query(final Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

    String query = selectionArgs[0];

    mNetworkHelper.startAutoCompleteRequest(
        selectionArgs[0],
        SuggestionCategory.EVERYTHING,
        new Response.Listener<AutoCompleteResponse>() {


            /**
             * This is the callback for a successful web service request
             */
            @Override
            public void onResponse(AutoCompleteResponse response) {

                MatrixCursor nCursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 10);
                List<String> suggestions = response.getResults();

                // transfrom suggestions to MatrixCursor
                for (int i = 0; i < suggestions.size() && i < 10; i++) 
                    nCursor.addRow(new String[]{String.valueOf(i), suggestions.get(i)});
                }

                // update cursor
                mAsyncCursor = nCursor;
            }
        }, 

        /**
         * This is the callback for a errornous web service request
         */
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(getContext(), "Fehler", Toast.LENGTH_SHORT).show();
            }
        }
    );
    return mAsyncCursor;
}

AndroidManifest

<activity
        android:name=".activities.MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>

        <meta-data android:name="android.app.default_searchable" android:value=".MainActivity" />
        <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>

    </activity>

    <provider
        android:name=".provider.AutocompleteSuggestionProvider"
        android:authorities="my.package.provider.AutocompleteSuggestion"
        android:exported="false" />

可搜索的.xml

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/app_name"
        android:hint="@string/search_hint"
        android:searchSettingsDescription="@string/search_settings"

        android:searchSuggestAuthority="my.package.provider.AutocompleteSuggestion"
        android:searchSuggestIntentAction="android.intent.action.VIEW"
        android:searchSuggestSelection=" ?"
        android:searchSuggestThreshold="2" >
</searchable>
4

2 回答 2

6

我找到了解决方案。要知道的最重要的事情是 ContentProvider 的查询方法不在 UI 线程上运行。因此,我们可以进行同步 HTTP 调用。由于现在每个理智的人都使用 Volley,因此您必须像这样进行此调用:

String url = NetworkHelper.buildRequestUrl(selectionArgs[0], SuggestionCategory.EVERYTHING, RequestType.AUTOCOMPLETE);
RequestFuture<JSONArray> future = RequestFuture.newFuture();
JsonArrayRequest request = new JsonArrayRequest(url, future, future);

mNetworkHelper.getRequestQueue().add(request);

// this does not run on the UI thread, so we can make a synchronous HTTP request
try {
  JSONArray suggestions = future.get();
  MatrixCursor resultCursor = new MatrixCursor(SEARCH_SUGGEST_COLUMNS, 10);

  for (int i = 0; i < suggestions.length() && i < 10; i++) {
    resultCursor.addRow(new String[]{String.valueOf(i), suggestions.get(i).toString()});
  }

  return resultCursor;

} catch (InterruptedException e) {
  e.printStackTrace();
} catch (ExecutionException e) {
  e.printStackTrace();
} catch (JSONException e) {
  e.printStackTrace();
}

这样一切正常。

于 2014-07-12T23:40:41.750 回答
1

不知道大家是否还需要这个。以防万一,对于未来的搜索者,我找到了解决方案。我还将 Volley 用于我的内容提供程序类,它似乎不适合 Android 的内容提供程序框架。与 muetzenflo 的回答相反,我发现我的内容提供者确实在 UI 线程中运行。因此,当我在其中同步使用 Volley 的 Future 时,它​​会减慢(阻塞)UI,直到请求返回(超时)。除此之外,我在网络上发现了 Volley 的 Future 请求应该在其他线程中运行的信息,例如在异步任务中,以使其正常工作。因此,它并没有解决我的问题,因为如果我必须使用它(在异步任务中),我会首先使用 Volley 的正常(异步)请求(这是我当时使用的)。

  1. 在我的 ContentProvider 子类中,我定义了一个监听器接口:

    公共接口 ResultListener {

      void onProviderResult(Cursor mCursor);
    
      void onProviderError(String errorMsg);
    

    }

  2. 在我的活动(实现了 LoaderCallbacks)中,我也实现了上面的接口。

  3. 在我的单例应用程序类中,我定义了一个用作瞬态数据的静态变量,以及它的 getter/setter 方法:

    私有静态 HashMap 瞬态数据 = 新 HashMap();

    公共静态对象 getTransientData(String objectName) {

    return transientData.get(objectName);
    

    }

    公共静态无效 setTransientData(字符串对象名,对象对象){

    transientData.put(objectName, object);
    

    }

  4. 现在的逻辑:在活动中,在调用getSupportLoaderManager().initLoader(...)之前,我调用了 MyApplication.setTransientData("requestor", this)

在我的内容提供者的课程中,在凌空请求的 onResponse 回调中,我这样做了:

公共无效 onResponse(JSONArray 响应){

  ...

  ResultListener requestor =   (ResultListener)TheApplication.getTransientData("requestor");

  if (requestor!=null) requestor.onProviderResult(mCursor);

}

这样当 volley 请求返回时,它会触发请求者的回调方法,将响应中填充了数据的游标传递给它,然后请求者(活动)通过调用:adapter.swapCursor(c); 通知游标适配器;适配器.notifyDataSetChanged();

希望这可以帮助某人。祝福。

于 2016-04-30T19:32:47.807 回答