简体   繁体   English

Android上的AsyncTask和错误处理

[英]AsyncTask and error handling on Android

I'm converting my code from using Handler to AsyncTask . 我正在将我的代码从使用Handler转换为AsyncTask The latter is great at what it does - asynchronous updates and handling of results in the main UI thread. 后者非常擅长它 - 在主UI线程中异步更新和处理结果。 What's unclear to me is how to handle exceptions if something goes haywire in AsyncTask#doInBackground . 我不清楚的是,如果在AsyncTask#doInBackground出现问题,如何处理异常。

The way I do it is to have an error Handler and send messages to it. 我这样做的方法是让错误处理程序并向其发送消息。 It works fine, but is it the "right" approach or is there better alternative? 它工作正常,但它是“正确的”方法还是有更好的选择?

Also I understand that if I define the error Handler as an Activity field, it should execute in the UI thread. 另外我理解如果我将错误处理程序定义为Activity字段,它应该在UI线程中执行。 However, sometimes (very unpredictably) I will get an Exception saying that code triggered from Handler#handleMessage is executing on the wrong thread. 但是,有时(非常不可预测)我会得到一个异常,说从Handler#handleMessage触发的代码正在错误的线程上执行。 Should I initialize error Handler in Activity#onCreate instead? 我应该在Activity#onCreate初始化错误处理程序吗? Placing runOnUiThread into Handler#handleMessage seems redundant but it executes very reliably. runOnUiThread放入Handler#handleMessage似乎是多余的,但它执行非常可靠。

It works fine but is it the "right" approach and is there better alternative? 它工作正常,但它是“正确”的方法,还有更好的选择吗?

I hold onto the Throwable or Exception in the AsyncTask instance itself and then do something with it in onPostExecute() , so my error handling has the option of displaying a dialog on-screen. 我在AsyncTask实例中保留了ThrowableException ,然后在onPostExecute()执行了一些操作,因此我的错误处理可以选择在屏幕上显示一个对话框。

Create an AsyncResult object ( which you can also use in other projects) 创建一个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;
    }
}

Return this object from your AsyncTask doInBackground methods and check it in the postExecute. 从AsyncTask doInBackground方法返回此对象,并在postExecute中检查它。 ( You can use this class as a base class for your other async tasks ) (您可以将此类用作其他异步任务的基类)

Below is a mockup of a task that gets a JSON response from the web server. 下面是一个从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
            }
        };

    }

When I feel the need to handle Exceptions in AsyncTask properly, I use this as super class: 当我觉得需要正确处理AsyncTask Exceptions时,我将它用作超类:

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

}

As normal, you override doInBackground in your subclass to do background work, happily throwing Exceptions where needed. 正常情况下,您在子类中覆盖doInBackground来执行后台工作,愉快地在需要的地方抛出异常。 You are then forced to implement onPostExecute (because it's abstract) and this gently reminds you to handle all types of Exception , which are passed as parameter. 然后你被迫实现onPostExecute (因为它是抽象的),这会轻轻地提醒你处理所有类型的Exception ,它们作为参数传递。 In most cases, Exceptions lead to some type of ui output, so onPostExecute is a perfect place to do that. 在大多数情况下,异常会导致某种类型的ui输出,因此onPostExecute是一个完美的地方。

If you want to use the RoboGuice framework which brings you other benefits you can try the RoboAsyncTask which has an extra Callback onException(). 如果你想使用RoboGuice框架为你带来其他好处,你可以尝试RoboAsyncTask,它有一个额外的Callback onException()。 Works real good and I use it. 工作真的很好,我用它。 http://code.google.com/p/roboguice/wiki/RoboAsyncTask http://code.google.com/p/roboguice/wiki/RoboAsyncTask

I made my own AsyncTask subclass with an interface that defines callbacks for success and failure. 我创建了自己的AsyncTask子类,其接口定义了成功和失败的回调。 So if an exception is thrown in your AsyncTask, the onFailure function gets passed the exception, otherwise the onSuccess callback gets passed your result. 因此,如果在AsyncTask中抛出异常,则onFailure函数会传递异常,否则onSuccess回调会传递您的结果。 Why android doesn't have something better available is beyond me. 为什么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);
    }
}

A more comprehensive solution to Cagatay Kalan 's solution is shown below: Cagatay Kalan解决方案的更全面的解决方案如下所示:

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

Example Task 示例任务

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

Another way that doesn't depend on variable member sharing is to use cancel. 另一种不依赖于变量成员共享的方法是使用cancel。

This is from android docs: 这是来自android文档:

public final boolean cancel (boolean mayInterruptIfRunning) public final boolean cancel(boolean mayInterruptIfRunning)

Attempts to cancel execution of this task. 尝试取消执行此任务。 This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. 如果任务已经完成,已经取消或由于某些其他原因无法取消,则此尝试将失败。 If successful, and this task has not started when cancel is called, this task should never run. 如果成功,并且在调用cancel时此任务尚未启动,则此任务永远不会运行。 If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task. 如果任务已经启动,则mayInterruptIfRunning参数确定执行此任务的线程是否应该在尝试停止任务时被中断。

Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. 调用此方法将导致在doInBackground(Object [])返回后在UI线程上调用onCancelled(Object)。 Calling this method guarantees that onPostExecute(Object) is never invoked. 调用此方法可确保永远不会调用onPostExecute(Object)。 After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible. 调用此方法后,应定期从doInBackground(Object [])检查isCancelled()返回的值,以尽早完成任务。

So you can call cancel in catch statement and be sure that onPostExcute is never called, but instead onCancelled is invoked on UI thread. 因此,您可以在catch语句中调用cancel并确保永远不会调用onPostExcute,而是在UI线程上调用onCancelled。 So you can show the error message. 所以你可以显示错误信息。

This simple class can help you 这个简单的课程可以帮助你

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

Actually, AsyncTask use FutureTask & Executor, FutureTask support exception-chain First let's define a helper class 实际上,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
    }
}

Second, let's use 其次,让我们来使用

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

Another possibility would be to use Object as return type, and in onPostExecute() check for the object type. 另一种可能性是使用Object作为返回类型,并在onPostExecute()检查对象类型。 It is short. 它很短。

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

Personally, I will use this approach. 就个人而言,我将使用这种方法。 You can just catch the exceptions and print out the stack trace if you need the info. 如果需要信息,您可以捕获异常并打印出堆栈跟踪。

make your task in background return a boolean value. 使你的后台任务返回一个布尔值。

it's like this: 就像这样:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

If you know the correct exception then you can call the 如果你知道正确的例外,那么你可以打电话给

Exception e = null;

publishProgress(int ...);

eg: 例如:

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

and go to "onProgressUpdate" and do the folowing 并转到“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());
}

This will be helpful in some cases only. 这仅在某些情况下有用。 Also you can keep a Global Exception variable and access the exception. 您还可以保留Global Exception变量并访问该异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM