148

我正在将我的代码从 using 转换HandlerAsyncTask. 后者非常擅长它的工作 - 在主 UI 线程中异步更新和处理结果。我不清楚的是,如果AsyncTask#doInBackground.

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

我也明白,如果我将错误处理程序定义为一个活动字段,它应该在 UI 线程中执行。但是,有时(非常不可预测)我会收到一个异常,说明从中触发的代码Handler#handleMessage正在错误的线程上执行。我应该初始化错误处理程序Activity#onCreate吗?放置似乎是多余的,但它执行非常可靠runOnUiThreadHandler#handleMessage

4

12 回答 12

179

它工作正常,但它是“正确”的方法吗?还有更好的选择吗?

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

于 2009-11-16T02:05:05.610 回答
141

创建一个 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;
    }
}

从您的 AsyncTask doInBackground 方法中返回此对象并在 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
            }
        };

    }
于 2011-06-10T21:58:18.837 回答
11

当我觉得需要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作为参数传递的所有类型。在大多数情况下,异常会导致某种类型的 ui 输出,所以这onPostExecute是一个完美的地方。

于 2013-04-29T15:37:45.393 回答
5

如果您想使用为您带来其他好处的 RoboGuice 框架,您可以尝试 RoboAsyncTask,它有一个额外的回调 onException()。效果很好,我使用它。 http://code.google.com/p/roboguice/wiki/RoboAsyncTask

于 2011-08-08T17:35:02.860 回答
3

我使用定义成功和失败回调的接口创建了自己的 AsyncTask 子类。因此,如果在您的 AsyncTask 中引发异常,则 onFailure 函数将传递异常,否则 onSuccess 回调将传递您的结果。为什么android没有更好的东西可用,这超出了我的理解。

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);
    }
}
于 2014-07-30T16:50:21.233 回答
3

Cagatay Kalan的解决方案更全面的解决方案如下所示:

异步任务结果

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;
    }
}

异常处理异步任务

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);
    }
}
于 2014-10-11T14:08:20.540 回答
2

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

这是来自 android 文档:

public final boolean cancel (boolean mayInterruptIfRunning)

尝试取消此任务的执行。如果任务已完成、已被取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用取消时此任务尚未启动,则此任务不应该运行。如果任务已经开始,则 mayInterruptIfRunning 参数确定是否应该中断执行该任务的线程以尝试停止该任务。

调用此方法将导致在 doInBackground(Object[]) 返回后在 UI 线程上调用 onCancelled(Object)。调用此方法可确保永远不会调用 onPostExecute(Object)。调用此方法后,应定期从 doInBackground(Object[]) 中检查 isCancelled() 返回的值,以尽早完成任务。

因此,您可以在 catch 语句中调用取消,并确保永远不会调用 onPostExcute,而是在 UI 线程上调用 onCancelled。所以你可以显示错误信息。

于 2013-07-24T13:22:43.133 回答
2

这个简单的类可以帮助你

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);
}
于 2014-01-10T13:28:42.427 回答
1

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

首先让我们定义一个辅助类

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);
    }

logcat 如下 在此处输入图像描述

于 2019-05-21T07:40:22.570 回答
-2

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

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

就像这样:

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

        @Override
                protected void onPostExecute(Boolean result) {

              if(result){
              // no error
               }
              else{
                // error handling
               }
}
于 2011-01-20T05:40:02.977 回答
-2

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

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变量并访问异常。

于 2012-02-04T09:40:54.280 回答
-2

另一种可能性是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();
    }
}
于 2015-02-13T07:35:38.723 回答