Create asynchronous ContentProvider for Actionbar SearchView

梦想与她 提交于 2019-11-30 22:25:43

I found the solution. The most important thing to know ist that the query-method of a ContentProvider does NOT run on the UI-thread. Therefore we can do a synchronous HTTP call. Since every sane person uses Volley these days you have to do this call like this:

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();
}

This way everything works fine.

Don't know if people still need this. Just in case, for future searchers, I found a solution for this. I also used Volley for my content provider class, which seemed not to fit naturally into Android's content provider framework. As opposed to muetzenflo's answer, I found that my content provider DOES run in the UI thread. Therefore, when I used Volley's Future synchronously in it, it slowed down (blocked) the UI until the request returned (timeout). In addition to this, I found information around the web that Volley's Future request should be run in other thread, such as in an Async task, for it to work well. Thus, it didn't solve my problem, because if I'd have to use it (in an async task), i would have used Volley's normal (async) request in the first place instead (which was what I used then). What I did was this:

  1. In my ContentProvider subclass, I define a listener interface:

    public interface ResultListener {

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

    }

  2. In my activity (which implemented LoaderCallbacks), I implemented also above interface.

  3. In my singleton application class, I define a static variable which is used as transient data, together with its getter/setter methods:

    private static HashMap transientData = new HashMap();

    public static Object getTransientData(String objectName) {

    return transientData.get(objectName);
    

    }

    public static void setTransientData(String objectName, Object object) {

    transientData.put(objectName, object);
    

    }

  4. now the logic: In the activity, before calling getSupportLoaderManager().initLoader(...), I called MyApplication.setTransientData("requestor", this) :

In my content provider's class, in the volley request's onResponse callback, I did this:

public void onResponse(JSONArray response){

  ...

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

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

}

So that when the volley request returned, it will trigger the requestor's callback method, passing it the cursor filled with data from the response, and in turn the requestor (the activity) notified the cursor adapter by calling: adapter.swapCursor(c); adapter.notifyDataSetChanged();

Hope this helps someone. Be blessed.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!