Currently I am using an asynchronous http library to execute http requests against our server. However this carries the problem where if an http call is in progress during a
Ok this is what I came up with which actually seems to work pretty good and handles screen orientation changes during the background work. Here is my updated HttpAsyncTaskLoader.
public class HttpAsyncTaskLoader<T extends ApiResponse> 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);
}
}
Note that in the above example the onCanceled method takes an Object. I got compile errors if I attempted to use ApiResponse. as the type. Also, you must implement onStartLoading like I did above (calling forceLoad if the result object is null) or else loadInBackground won't get called
Then here is an example of a subclass of 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;
}
}
Here is an Activity that uses this loader:
public class LoginActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<LoginResponse> {
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<LoginResponse> 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<LoginResponse> loginLoader,
LoginResponse loginResponse)
{
//handle result, maybe send to a new activity if response doesn't have an error
}
@Override
public void onLoaderReset(Loader<LoginResponse> responseAndJsonHolderLoader)
{
//not sure if anything needs to be done here to do
}
}
Note that while this loader doesn't start until the user presses the Login button, You must reconnect to the loader using initLoader in onCreate in case it was already in progress, otherwise when you flip orientations you won't get notified that the task finished.
Interesting that this seems to work good and doesn't require using a TaskFragment. I haven't really seen anyone else do this for http stuff so maybe there are some down sides but it seems to work just fine.
Are you not interested in trying to implement libraries dedicated to this kind of problems? You Have Volley by Google and Robospice for example.
http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/
https://github.com/octo-online/robospice