AsyncTaskLoader,用于处理方向更改的http请求,使用generics,inheritance

目前我正在使用异步http库来对我们的服务器执行http请求。 然而,这带来了一个问题,即如果在屏幕旋转期间正在进行http调用,我们将在调用结束时引用旧的上下文。 我通过保持对onCreate中捕获的最新实例的静态引用来解决这个问题,并且会使用该引用调用方法(并在onDestroy中将其置空)。 它工作正常,但似乎是一个黑客。 我见过有人推荐使用片段来解决这个问题,比如这里:

http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html

这似乎是好主意,但我想我可以通过简单地让我的Activity扩展FragmentActivity并使用AsyncTaskLoader子类来实现这一点,该子类专门用于我正在做的事情。

这是我的想法:使用ApiRequest实现AsyncTaskLoader并返回ApiResponse。 但是,我希望能够inheritanceHttpAsyncTask并覆盖一个解析响应的方法,这样我就可以解析响应并将其转换为另一种扩展ApiResponse的对象。 我不知道如何指定类型参数来实现这一点。

这是我的代码:

public class HttpAsyncTaskLoader extends AsyncTaskLoader { private ApiClient mClient ; private ApiRequest mRequest; private volatile boolean isExecuting = false; public HttpAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) { super(context); mClient = client; mRequest = request; } /** * Subclasses should override this method to do additional parsing * @param response * @return */ protected /*subclass of ApiResponse (or ApiResponse itself)*/ onResponse(ApiResponse response) { //base implementation just returns the value, subclasses would //do additional processing and turn it into some base class of ApiResponse return response; } @Override public /** not sure ***/ loadInBackground() { HttpResponse response = null; ResponseError error = null; JSONObject responseJson = null; ApiResponse apiResponse = null; try { isExecuting = true; //synchronous call response = mClient.execute(mRequest); isExecuting = false; responseJson = new JSONObject(EntityUtils.toString(response.getEntity())); } catch (IOException e) { error = new ResponseError(e); } catch (URISyntaxException e) { error = new ResponseError(e); } catch (JSONException e) { error = new ResponseError(e); } finally { mClient.getConnectionManager().closeExpiredConnections(); isExecuting = false; apiResponse = new ApiResponse(getContext().getResources(), response, responseJson, error); } return onResponse(apiResponse); } @Override public void onCanceled(ApiResponse response) { if (isExecuting) { mClient.getConnectionManager().shutdown(); } } } 

任何人都知道如何才能做到这一点? 我不确定如何指定类型参数? 我希望这个类可以正常使用,也可以将它子类化。 关键是我不想在上面的loadInBackground方法中重新实现该function。 我确定我可以使用ApiResponse作为我的通用参数,然后将onLoadFinished中返回的ApiResponse对象转换为我期望的特定基类,但我宁愿以更加类型安全的方式执行此操作。 此外,我对那些基本上完成同样的事情但又以另一种方式完成的想法持开放态度。

好的,这就是我提出的实际上看起来非常好并且在后台工作期间处理屏幕方向更改的内容。 这是我更新的HttpAsyncTaskLoader。

 public class HttpAsyncTaskLoader extends AsyncTaskLoader { private ApiClient mClient ; protected ApiRequest mRequest; private ApiResponse mResponse; private volatile boolean isExecuting = false; public HttpAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) { super(context); mClient = client; mRequest = request; } /** Subclasses should call this from loadInBackground */ protected ApiResponse executeRequest(ApiRequest request) { HttpResponse response = null; ResponseError error = null; JSONObject responseJson = null; try { isExecuting = true; Log.d(TAG, "executing api"); response = mClient.execute(request); Log.d(TAG, "got a response"); isExecuting = false; responseJson = new JSONObject(EntityUtils.toString(response.getEntity())); Log.d(TAG, "parsed response to json"); } catch (IOException e) { error = new ResponseError(e); } catch (URISyntaxException e) { error = new ResponseError(e); } catch (JSONException e) { error = new ResponseError(e); } finally { mClient.getConnectionManager().closeExpiredConnections(); isExecuting = false; mResponse = new ApiResponse(getContext().getResources(), response, responseJson, error); } return mResponse; } protected void onStartLoading() { super.onStartLoading(); if (takeContentChanged() || mResponse == null) { forceLoad(); } if (getResponse() != null) { deliverResult(getResponse()); } } /** * Subclasses should also override this so the correct object * gets delivered in all cases (see onStartLoading above) */ public ApiResponse getResponse() { return mResponse; } @Override public void onCanceled(Object data) { super.onCanceled(data); if (isExecuting) { mClient.getConnectionManager().shutdown(); } } @Override public ApiResponse loadInBackground() { return executeRequest(mRequest); } } 

请注意,在上面的示例中,onCanceled方法采用Object。 如果我试图使用ApiResponse,我会遇到编译错误。 作为类型。 此外,您必须像我上面那样实现onStartLoading(如果结果对象为null则调用forceLoad)否则将不会调用loadInBackground

那么这里是HttpAsyncTaskLoader的子类的一个例子:

 public class LoginAsyncTaskLoader extends HttpAsyncTaskLoader { private LoginResponse mLoginResponse; public LoginAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) { super(context, client, request); } @Override public LoginResponse loadInBackground() { ApiResponse apiResponse = executeRequest(mRequest); mLoginResponse = new LoginResponse(apiResponse.getResources(), apiResponse.response, apiResponse.responseJson, apiResponse.getError()); return mLoginResponse; } @Override public ApiResponse getResponse() { return mLoginResponse; } } 

这是一个使用此加载器的Activity:

 public class LoginActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks { private String username,password; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.login); Loader loader = getSupportLoaderManager().getLoader(0); if (loader != null) { getSupportLoaderManager().initLoader(0, null, this); } } public void loginSubmit(View button) { Bundle data = new Bundle(); data.putString("username", getUsername()); data.putString("password", getPassword()); getSupportLoaderManager().restartLoader(0, data, this); } @Override public Loader onCreateLoader(int i, Bundle bundle) { //might want to start a progress bar ApiClient client = new ApiClient(); LoginApi loginApi = new LoginApi(bundle.getString("username"), bundle.getString("password")); return new LoginAsyncTaskLoader(this, apiClient, loginApi); } @Override public void onLoadFinished(Loader loginLoader, LoginResponse loginResponse) { //handle result, maybe send to a new activity if response doesn't have an error } @Override public void onLoaderReset(Loader responseAndJsonHolderLoader) { //not sure if anything needs to be done here to do } } 

请注意,虽然此加载程序在用户按下“登录”按钮之前不会启动,但您必须使用onCreate中的initLoader重新连接到加载程序,以防它正在进行中,否则当您翻转方向时,您将不会收到任务已完成的通知。

有趣的是,这似乎工作得很好,不需要使用TaskFragment。 我还没有真正看到其他任何人为http的东西做这个,所以也许有一些不利的方面,但似乎工作得很好。

您是否对尝试实施专门针对此类问题的库感兴趣? 例如,谷歌和Robospice都有排球。

http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/

https://github.com/octo-online/robospice