Android 上的 AsyncTask 和错误处理

我正在将我的代码从使用 Handler转换为 AsyncTask。后者非常擅长它所做的——异步更新和在主 UI 线程中处理结果。我不清楚的是如何处理异常,如果在 AsyncTask#doInBackground中出现混乱。

我这样做的方式是有一个错误处理程序并向它发送消息。它工作得很好,但它是“正确”的方法,还是有更好的选择?

另外,我知道如果我将错误处理程序定义为一个 Activity 字段,那么它应该在 UI 线程中执行。然而,有时(非常不可预测地)我会得到一个异常,说从 Handler#handleMessage触发的代码在错误的线程上执行。我应该在 Activity#onCreate中初始化错误处理程序吗?将 runOnUiThread放入 Handler#handleMessage似乎是多余的,但它的执行非常可靠。

75763 次浏览

它工作的很好,但它是“正确的” 是否有更好的方法 另一种选择?

我保留 AsyncTask实例本身中的 ThrowableException,然后在 onPostExecute()中对其进行处理,因此我的错误处理可以选择在屏幕上显示一个对话框。

就我个人而言,我会使用这种方法。 如果需要信息,您可以捕获异常并打印出堆栈跟踪。

让你的任务在后台返回一个布尔值。

是这样的:

    @Override
protected Boolean doInBackground(String... params) {
return readXmlFromWeb(params[0]);
}


@Override
protected void onPostExecute(Boolean result) {


if(result){
// no error
}
else{
// error handling
}
}

创建一个 AsyncResult 对象(也可以在其他项目中使用)

public class AsyncTaskResult<T> {
private T result;
private Exception error;


public T getResult() {
return result;
}


public Exception getError() {
return error;
}


public AsyncTaskResult(T result) {
super();
this.result = result;
}


public AsyncTaskResult(Exception error) {
super();
this.error = error;
}
}

从 AsyncTaskdoInBack 方法返回此对象,并在 postExecute 中检查它。(可以将此类用作其他异步任务的基类)

下面是从 Web 服务器获取 JSON 响应的任务的模型。

AsyncTask<Object,String,AsyncTaskResult<JSONObject>> jsonLoader = new AsyncTask<Object, String, AsyncTaskResult<JSONObject>>() {


@Override
protected AsyncTaskResult<JSONObject> doInBackground(
Object... params) {
try {
// get your JSONObject from the server
return new AsyncTaskResult<JSONObject>(your json object);
} catch ( Exception anyError) {
return new AsyncTaskResult<JSONObject>(anyError);
}
}


protected void onPostExecute(AsyncTaskResult<JSONObject> result) {
if ( result.getError() != null ) {
// error handling here
}  else if ( isCancelled()) {
// cancel handling here
} else {


JSONObject realResult = result.getResult();
// result handling here
}
};


}

如果你想使用 RoboGuice 框架,它会给你带来其他的好处,你可以试试 RoboAsyncTask,它有一个额外的 Callback onException ()。效果真的很好,我用它。 Http://code.google.com/p/roboguice/wiki/roboasynctask

如果知道正确的异常,则可以调用

Exception e = null;


publishProgress(int ...);

例如:

@Override
protected Object doInBackground(final String... params) {


// TODO Auto-generated method stub
try {
return mClient.call(params[0], params[1]);
} catch(final XMLRPCException e) {


// TODO Auto-generated catch block
this.e = e;
publishProgress(0);
return null;
}
}

然后转到“ onProgressUpdate”并执行以下操作

@Override
protected void onProgressUpdate(final Integer... values) {


// TODO Auto-generated method stub
super.onProgressUpdate(values);
mDialog.dismiss();
OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}

这将有助于在某些情况下只。也可以保持一个 Global Exception变量,并访问异常。

当我觉得需要正确处理 AsyncTask中的异常时,我使用这个超类:

public abstract class ExceptionAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {


private Exception exception=null;
private Params[] params;


@Override
final protected Result doInBackground(Params... params) {
try {
this.params = params;
return doInBackground();
}
catch (Exception e) {
exception = e;
return null;
}
}


abstract protected Result doInBackground() throws Exception;


@Override
final protected void onPostExecute(Result result) {
super.onPostExecute(result);
onPostExecute(exception, result);
}


abstract protected void onPostExecute(Exception exception, Result result);


public Params[] getParams() {
return params;
}


}

正常情况下,在子类中重写 doInBackground以执行后台工作,并在需要时抛出异常。然后,您被迫实现 onPostExecute(因为它是抽象的) ,这温和地提醒您处理所有类型的 Exception,它们作为参数传递。在大多数情况下,Exception 会导致某种类型的 ui 输出,因此 onPostExecute是完美的地方。

另一种不依赖于变量成员共享的方法是使用取消。

这是来自 android 文档:

Public final 布尔取消(布尔可能中断运行)

试图取消此任务的执行 如果任务已经完成,则尝试将失败 取消,或由于其他原因不能取消。如果 取消时,此任务尚未启动,则此 任务永远不应该运行。如果任务已经启动,则 参数确定线程是否 执行此任务时应中断,以尝试停止 任务。

调用此方法将导致调用 onCancels (Object) 调用这个函数 方法保证从不调用 onPostExecute (Object) 调用此方法时,应该检查 从 doInBack (Object [])周期性地取消() ,以完成 尽早完成任务。

因此,可以在 catch 语句中调用 Cancel,并确保从不调用 onPostExcute,而是在 UI 线程上调用 onCancell。这样就可以显示错误消息。

这个简单的类可以帮助您

public abstract class ExceptionAsyncTask<Param, Progress, Result, Except extends Throwable> extends AsyncTask<Param, Progress, Result> {
private Except thrown;


@SuppressWarnings("unchecked")
@Override
/**
* Do not override this method, override doInBackgroundWithException instead
*/
protected Result doInBackground(Param... params) {
Result res = null;
try {
res = doInBackgroundWithException(params);
} catch (Throwable e) {
thrown = (Except) e;
}
return res;
}


protected abstract Result doInBackgroundWithException(Param... params) throws Except;


@Override
/**
* Don not override this method, override void onPostExecute(Result result, Except exception) instead
*/
protected void onPostExecute(Result result) {
onPostExecute(result, thrown);
super.onPostExecute(result);
}


protected abstract void onPostExecute(Result result, Except exception);
}

我使用定义成功和失败回调的接口创建了自己的 AsyncTask 子类。因此,如果在 AsyncTask 中引发了异常,则会传递该异常,否则传递 onSuccess 回调。我不明白为什么机器人没有更好的选择。

public class SafeAsyncTask<inBackgroundType, progressType, resultType>
extends AsyncTask<inBackgroundType, progressType, resultType>  {
protected Exception cancelledForEx = null;
protected SafeAsyncTaskInterface callbackInterface;


public interface SafeAsyncTaskInterface <cbInBackgroundType, cbResultType> {
public Object backgroundTask(cbInBackgroundType[] params) throws Exception;
public void onCancel(cbResultType result);
public void onFailure(Exception ex);
public void onSuccess(cbResultType result);
}


@Override
protected void onPreExecute() {
this.callbackInterface = (SafeAsyncTaskInterface) this;
}


@Override
protected resultType doInBackground(inBackgroundType... params) {
try {
return (resultType) this.callbackInterface.backgroundTask(params);
} catch (Exception ex) {
this.cancelledForEx = ex;
this.cancel(false);
return null;
}
}


@Override
protected void onCancelled(resultType result) {
if(this.cancelledForEx != null) {
this.callbackInterface.onFailure(this.cancelledForEx);
} else {
this.callbackInterface.onCancel(result);
}
}


@Override
protected void onPostExecute(resultType result) {
this.callbackInterface.onSuccess(result);
}
}

卡加泰卡兰解决方案的一个更全面的解决方案如下:

AsyncTaskResult 异步任务结果

public class AsyncTaskResult<T>
{
private T result;
private Exception error;


public T getResult()
{
return result;
}


public Exception getError()
{
return error;
}


public AsyncTaskResult(T result)
{
super();
this.result = result;
}


public AsyncTaskResult(Exception error) {
super();
this.error = error;
}
}

异常处理 AsyncTask

public abstract class ExceptionHandlingAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, AsyncTaskResult<Result>>
{
private Context context;


public ExceptionHandlingAsyncTask(Context context)
{
this.context = context;
}


public Context getContext()
{
return context;
}


@Override
protected AsyncTaskResult<Result> doInBackground(Params... params)
{
try
{
return new AsyncTaskResult<Result>(doInBackground2(params));
}
catch (Exception e)
{
return new AsyncTaskResult<Result>(e);
}
}


@Override
protected void onPostExecute(AsyncTaskResult<Result> result)
{
if (result.getError() != null)
{
onPostException(result.getError());
}
else
{
onPostExecute2(result.getResult());
}
super.onPostExecute(result);
}


protected abstract Result doInBackground2(Params... params);


protected abstract void onPostExecute2(Result result);


protected void onPostException(Exception exception)
{
new AlertDialog.Builder(context).setTitle(R.string.dialog_title_generic_error).setMessage(exception.getMessage())
.setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
//Nothing to do
}
}).show();
}
}

任务示例

public class ExampleTask extends ExceptionHandlingAsyncTask<String, Void, Result>
{
private ProgressDialog  dialog;


public ExampleTask(Context ctx)
{
super(ctx);
dialog = new ProgressDialog(ctx);
}


@Override
protected void onPreExecute()
{
dialog.setMessage(getResources().getString(R.string.dialog_logging_in));
dialog.show();
}


@Override
protected Result doInBackground2(String... params)
{
return new Result();
}


@Override
protected void onPostExecute2(Result result)
{
if (dialog.isShowing())
dialog.dismiss();
//handle result
}


@Override
protected void onPostException(Exception exception)
{
if (dialog.isShowing())
dialog.dismiss();
super.onPostException(exception);
}
}

另一种可能性是使用 Object作为返回类型,并在 onPostExecute()中检查对象类型。很短。

class MyAsyncTask extends AsyncTask<MyInObject, Void, Object> {


@Override
protected AsyncTaskResult<JSONObject> doInBackground(MyInObject... myInObjects) {
try {
MyOutObject result;
// ... do something that produces the result
return result;
} catch (Exception e) {
return e;
}
}


protected void onPostExecute(AsyncTaskResult<JSONObject> outcome) {
if (outcome instanceof MyOutObject) {
MyOutObject result = (MyOutObject) outcome;
// use the result
} else if (outcome instanceof Exception) {
Exception e = (Exception) outcome;
// show error message
} else throw new IllegalStateException();
}
}

实际上,AsyncTask 使用 FutureTask & Execator,FutureTask 支持异常链

首先让我们定义一个 helper 类

public static class AsyncFutureTask<T> extends FutureTask<T> {


public AsyncFutureTask(@NonNull Callable<T> callable) {
super(callable);
}


public AsyncFutureTask<T> execute(@NonNull Executor executor) {
executor.execute(this);
return this;
}


public AsyncFutureTask<T> execute() {
return execute(AsyncTask.THREAD_POOL_EXECUTOR);
}


@Override
protected void done() {
super.done();
//work done, complete or abort or any exception happen
}
}

其次,让我们利用

    try {
Log.d(TAG, new AsyncFutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
//throw Exception in worker thread
throw new Exception("TEST");
}
}).execute().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
//catch the exception throw by worker thread in main thread
e.printStackTrace();
}

或者像下面这样直接使用 FutureTask

    FutureTask<?> futureTask = new FutureTask(() -> {throw new RuntimeException("Exception in TaskRunnable");}) {
@Override
protected void done() {
super.done();
//do something
Log.d(TAG,"FutureTask done");
}
};


AsyncTask.THREAD_POOL_EXECUTOR.execute(futureTask);


try {
futureTask.get();
} catch (ExecutionException | InterruptedException e) {
Log.d(TAG, "Detect exception in futureTask", e);
}

日志记录如下 enter image description here