Android上的AsyncTask和错误处理


147

我正在从使用转换我的代码 HandlerAsyncTask。后者非常擅长-异步更新和主UI线程中结果的处理。我不清楚的是,如果出现问题,如何处理异常AsyncTask#doInBackground

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

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


您为什么要转换代码?有充分的理由吗?
HGPB 2013年

4
@Haraldo,这是一种更好的编码习惯,至少我是这样认为的
波士顿

Answers:


178

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

我在实例本身上按住ThrowableExceptionAsyncTask然后在中对其进行操作onPostExecute(),因此我的错误处理可以选择在屏幕上显示对话框。


8
辉煌!不再需要与处理程序
混为一谈

5
这是我应该坚持Throwable还是Exception的方式吗?“将实例变量添加到您自己的AsyncTask子类中,该变量将保存后台处理的结果。” 当您收到异常时,请将异常(或其他错误字符串/代码)存储在此变量中。调用onPostExecute时,请查看此实例变量是否设置为某些错误。如果是这样,显示错误信息“(从用户‘波士顿的街道’。groups.google.com/group/android-developers/browse_thread/thread/...
OneWorld的

1
@OneWorld:是的,应该没问题。
CommonsWare,2010年

2
嗨,CW,能否请您更详细地说明您的操作方式-也许有一个简短的代码示例?非常感谢!!
Bruiser

18
@Bruiser:github.com/commonsguy/cw-lunchlist/tree/master/15-Internet/…具有AsyncTask我描述的以下模式。
CommonsWare

140

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

    }

1
我喜欢。好的封装。由于这是原始答案的
措辞,

这很好地演示了泛型有多有用。就复杂性而言,它发出了一种怪异的气味,但并不是我能真正表达出来的。
num1

4
好主意,只是一个问题:你为什么叫super()AsyncTaskResult上课的时候不延长东西吗?
donturner

7
“无害”-冗余代码始终对可读性和维护有害。把它拿出来!:)
donturner 2012年

2
真的很喜欢这个解决方案...考虑一下-C#家伙在C#对应的BackgroundTask本机实现中使用了完全相同的方法...
Vova 2013年

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在子类中重写以进行后台工作,并在需要的地方高兴地抛出Exceptions。然后,您被迫实现onPostExecute(因为它是抽象的),这轻轻地提醒您处理所有Exception作为参数传递的类型。在大多数情况下,异常会导致某种类型的ui输出,因此onPostExecute是执行此操作的理想场所。


1
哎呀,为什么不只是params前进,所以它更类似于原始的并且更容易迁移?
TWiStErRob

@TWiStErRob这个想法没有错。我猜这是个人喜好问题,因为我倾向于不使用params。我喜欢new Task("Param").execute()new Task().execute("Param")
sulai 2014年

5

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


您对此有何经验?很稳定吗?
nickaknudson

是否RoboGuice还活着?自2012年以来似乎没有更新?
Dimitry K

不,RoboGuice已死并且已弃用。Dagger2是推荐的替代品,但它只是一个简单的DI库。
阿维·切里

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

3

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

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

ExceptionHandlingAsyncTask

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

我在myActivity.getApplicationContext()。getResources()中得到了getResources()方法
Stephane

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

2

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

这是来自android docs:

public final布尔取消(boolean mayInterruptIfRunning)

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

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

因此,您可以在catch语句中调用cancel,并确保从不调用onPostExcute,而是在UI线程上调用onCancelled。因此,您可以显示错误消息。


您无法正确显示错误消息,因为您不知道问题(异常),因此仍然需要捕获并返回AsyncTaskResult。同样,用户取消也不是错误,这是预期的交互:您如何区分这些?
TWiStErRob

cancel(boolean)onCancelled()从一开始就导致对存在的调用,但onCancelled(Result)已将其添加到API 11中
TWiStErRob

0

实际上,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();
    }

-2

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

使您的任务在后台返回布尔值。

就像这样:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

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

1
完全不相关
Dinu

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


1
拜托,不要这样做。那真的,真的,不好的风格!
JimmyB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.