Is there a specific way to handle failure in an AsyncTask? As far as I can tell the only way is with the return value of task. I\'d like to be able to provide more details o
You can simply save the exception in a field and check it in onPostExecute()
(to ensure that any error handling code is run on the UI thread). Something like:
new AsyncTask<Void, Void, Boolean>() {
Exception error;
@Override
protected Boolean doInBackground(Void... params) {
try {
// do work
return true;
} catch (Exception e) {
error = e;
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
Toast.makeText(ctx, "Success!",
Toast.LENGTH_SHORT).show();
} else {
if (error != null) {
Toast.makeText(ctx, error.getMessage(),
Toast.LENGTH_SHORT).show();
}
}
}
}
I modified Nicholas's code a bit, should you want to do something in the UI thread in exception.
Remember the AsyncTask can only be executed once after instantiated.
class ErrorHandlingAsyncTask extends AsyncTask<..., ..., ...> {
private Exception exception = null;
protected abstract void onResult(Result result);
protected abstract void onException(Exception e);
protected abstract ... realDoInBackground(...);
@Override
final protected void onPostExecute(Result result) {
if(result != null) {
onResult(result);
} else {
onException(exception);
}
}
@Override
protected ... doInBackground(...) {
try {
return realDoInBackground(...);
} catch(Exception e) {
exception = e;
}
return null;
}
}
You can do this yourself pretty easily by creating a subclass of AsyncTask
. Perhaps something like ErrorHandlingAsyncTask
. First create an abstract callback method onException(Exception e)
. Your doInBackground(Generic... params)
method should wrap all of its code in a try-catch
block. In the catch
block, call out to onException(Exception e)
passing in your exception.
Now, when you need this functionality, just override your new ErrorHandlingAsyncTask class.
Quick and dirty pseudo code:
class ErrorHandlingAsyncTask extends AsyncTask<..., ..., ...> {
protected abstract void onException(Exception e);
protected abstract ... realDoInBackground(...);
protected ... doInBackground(...) {
try {
return realDoInBackground(...);
} catch(Exception e) {
onException(e);
}
}
}
What I always do is create a new Object (you can call it AsyncTaskResult or whatever you like) that can get returned with doInBackground. This Object would have two things:
I would then return this object to postExecute() and let that method check for the error, if there is then I handle it accordingly, otherwise I take the expected result and do whatever with it.
The object would be something like:
public class AsyncTaskResult<T extends Object> {
Exception exception;
T asyncTaskResult;
public void setResult(T asyncTaskResult) {
this.asyncTaskResult = asyncTaskResult;
}
public T getResult() {
return asyncTaskResult;
}
public void setException(Exception exception) {
this.exception = exception;
}
public boolean hasException() {
return exception != null;
}
public Exception getException() {
return exception;
}
}
And your code becomes :
/** this would be cool if it existed */
protected void onError(Exception ex) {
// handle error...
}
@Override
protected AsyncTaskResult<String> doInBackground(String... params) {
AsyncTaskResult<String> result = new AsyncTaskResult<String>();
try {
// ... download ...
} catch (IOException e) {
result.setException(e);
}
return result;
}
@Override
protected void onPostExecute(AsyncTaskResult<String> result) {
if(result.hasException()) {
// handle error here...
onError(result.getException());
} else {
// deal with the result
}
}
I combined momo's and Dongshengcn's answers, and created my own base class with both background and foreground exception handling (in case you want to do some serious error logging)
The thing is, my code encapsulates all of the ResultOrError class stuff and simply lets you return the normal result or throw an exception
public abstract class HandledAsyncTask<Params, Progress, Result> extends
AsyncTask<Params, Progress, ResultOrException<Result>> {
/**
* Wraps the calling of the {@link #doTask(Object[])} method, also handling
* the exceptions possibly thrown.
*/
protected final ResultOrException<Result> doInBackground(Params... params) {
try {
Result res = doTask(params);
return new ResultOrException<Result>(res);
} catch (Exception e) {
onBackgroundException(e);
return new ResultOrException<Result>(e);
}
}
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to
* {@link #doTask(Object[])} by the caller of this task. This method can
* call {@link #publishProgress(Object...)} to publish updates on the UI
* thread.
*
* @param params
* The parameters of the task.
* @return A result, defined by the subclass of this task.
*/
protected abstract Result doTask(Params[] params);
/**
* Handles calling the {@link #onSuccess(Object)} and
* {@link #onFailure(Exception)} methods.
*/
@Override
protected final void onPostExecute(ResultOrException<Result> result) {
if (result.getException() != null) {
onFailure(result.getException());
} else {
onSuccess(result.getResult());
}
}
/**
* Called when an exception was thrown in {@link #doTask(Object[])}. Handled
* in the background thread.
*
* @param exception
* The thrown exception
*/
protected void onBackgroundException(Exception exception) {
}
/**
* Called when the {@link #doTask(Object[])} method finished executing with
* no exceptions thrown.
*
* @param result
* The result returned from {@link #doTask(Object[])}
*/
protected void onSuccess(Result result) {
}
/**
* Called when an exception was thrown in {@link #doTask(Object[])}. Handled
* in the foreground thread.
*
* @param exception
* The thrown exception
*/
protected void onFailure(Exception exception) {
}
}
class ResultOrException<TResult> {
/**
* The possibly thrown exception
*/
Exception mException;
/**
* The result, if no exception was thrown
*/
TResult mResult;
/**
* @param exception
* The thrown exception
*/
public ResultOrException(Exception exception) {
mException = exception;
}
/**
* @param result
* The result returned from the method
*/
public ResultOrException(TResult result) {
mResult = result;
}
/**
* @return the exception
*/
public Exception getException() {
return mException;
}
/**
* @param exception
* the exception to set
*/
public void setException(Exception exception) {
mException = exception;
}
/**
* @return the result
*/
public TResult getResult() {
return mResult;
}
/**
* @param result
* the result to set
*/
public void setResult(TResult result) {
mResult = result;
}
}