Как обрабатывать сбой AsyncTask
Существует ли конкретный способ обработки сбой в AsyncTask? Насколько я могу судить, единственный способ - вернуть возвращаемое значение задачи. Я хотел бы предоставить более подробную информацию о сбое, если это возможно, и null не очень многословно.
В идеале это обеспечит обработчик onError, но я не думаю, что он есть.
class DownloadAsyncTask extends AsyncTask<String, Void, String> {
/** this would be cool if it existed */
@Override
protected void onError(Exception ex) {
...
}
@Override
protected String doInBackground(String... params) {
try {
... download ...
} catch (IOException e) {
setError(e); // maybe like this?
}
}
}
Ответы
Ответ 1
Вы можете просто сохранить исключение в поле и проверить его в onPostExecute()
(чтобы убедиться, что в потоке пользовательского интерфейса запущен любой код обработки ошибок). Что-то вроде:
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();
}
}
}
}
Ответ 2
Я немного изменил код Николаса, если вы хотите что-то сделать в потоке пользовательского интерфейса в исключении.
Помните, что AsyncTask может выполняться только один раз после создания экземпляра.
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;
}
}
Ответ 3
То, что я всегда делаю, это создать новый объект (вы можете назвать его AsyncTaskResult или что угодно), который может быть возвращен с помощью doInBackground. У этого объекта было бы две вещи:
- Ожидаемый результат (строка в вашем примере)
- Код ошибки или даже если вы хотите, сам объект Exception или его завернутая версия. Все, что в принципе поможет вам справиться с ошибкой, если есть какие-либо события.
Затем я вернул бы этот объект в postExecute(), и пусть этот метод проверяет наличие ошибки, если тогда я обрабатываю его соответствующим образом, в противном случае я беру ожидаемый результат и делаю с ним что-либо.
Объект будет выглядеть примерно так:
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;
}
}
И ваш код будет выглядеть следующим образом:
/** 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
}
}
Ответ 4
Вы можете сделать это довольно легко, создав подкласс AsyncTask
. Возможно, что-то вроде ErrorHandlingAsyncTask
. Сначала создайте абстрактный метод обратного вызова onException(Exception e)
. Ваш метод doInBackground(Generic... params)
должен обернуть весь его код в блок try-catch
. В блоке catch
вызовите onException(Exception e)
, проходящий в вашем исключении.
Теперь, когда вам нужна эта функциональность, просто переопределите новый класс ErrorHandlingAsyncTask.
Быстрый и грязный псевдокод:
class ErrorHandlingAsyncTask extends AsyncTask<..., ..., ...> {
protected abstract void onException(Exception e);
protected abstract ... realDoInBackground(...);
protected ... doInBackground(...) {
try {
return realDoInBackground(...);
} catch(Exception e) {
onException(e);
}
}
}
Ответ 5
Я объединил ответы momo и Dongshengcn и создал свой собственный базовый класс с обработкой исключений фона и переднего плана (в случае, если вы хотите сделать серьезную регистрацию ошибок)
Дело в том, что мой код инкапсулирует все вещи класса ResultOrError и просто позволяет вам вернуть обычный результат или исключить исключение
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;
}
}