mue*_*flo 5 android asynchronous android-contentprovider search-suggestion searchview
我在ActionBar中有一个SearchView,它与ContentProvider连接以提供搜索建议.这些建议不是来自DB(与ContentProvider一样),而是来自Web服务.这就是为什么我必须异步处理ContentProvider的Cursor.我的代码到目前为止工作,但搜索建议总是一个字母"后面":
在我输入之后"the",我得到了之前搜索的所有结果=>"th"
在我输入之后"they",我得到了之前搜索的所有结果=>"the"
如何告诉SearchView Cursor有新的结果呢?我查看了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;
}
Run Code Online (Sandbox Code Playgroud)
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" />
Run Code Online (Sandbox Code Playgroud)
searchable.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>
Run Code Online (Sandbox Code Playgroud)
我找到了解决方案.最重要的是要知道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();
}
Run Code Online (Sandbox Code Playgroud)
这样一切正常.
不知道人们是否还需要这个。为了以防万一,对于未来的搜索者,我找到了一个解决方案。我还在我的内容提供程序类中使用了 Volley,它似乎并不自然地适合 Android 的内容提供程序框架。与 muetzenflo 的答案相反,我发现我的内容提供程序确实在 UI 线程中运行。因此,当我在其中同步使用 Volley 的 Future 时,它会减慢(阻塞)UI,直到请求返回(超时)。除此之外,我在网上发现了一些信息,Volley 的 Future 请求应该在其他线程中运行,例如在异步任务中,才能正常工作。因此,它并没有解决我的问题,因为如果我必须使用它(在异步任务中),我会首先使用 Volley 的正常(异步)请求(这就是我当时使用的)。我所做的是这样的:
在我的 ContentProvider 子类中,我定义了一个侦听器接口:
公共接口结果监听器{
void onProviderResult(Cursor mCursor);
void onProviderError(String errorMsg);
Run Code Online (Sandbox Code Playgroud)
}
在我的活动(实现了 LoaderCallbacks)中,我还实现了上面的接口。
在我的单例应用程序类中,我定义了一个用作瞬态数据的静态变量及其 getter/setter 方法:
私有静态 HashMap 瞬态数据 = new HashMap();
公共静态对象 getTransientData(String objectName) {
return transientData.get(objectName);
Run Code Online (Sandbox Code Playgroud)
}
公共静态无效setTransientData(字符串对象名,对象对象){
transientData.put(objectName, object);
Run Code Online (Sandbox Code Playgroud)
}
在我的内容提供者的类中,在齐射请求的 onResponse 回调中,我这样做了:
公共无效onResponse(JSONArray响应){
...
ResultListener requestor = (ResultListener)TheApplication.getTransientData("requestor");
if (requestor!=null) requestor.onProviderResult(mCursor);
Run Code Online (Sandbox Code Playgroud)
}
这样,当 volley 请求返回时,它将触发请求者的回调方法,将填充有响应数据的游标传递给它,然后请求者(活动)通过调用:adapter.swapCursor(c); 通知游标适配器。适配器.notifyDataSetChanged();
希望这对某人有帮助。祝福。