AsyncTask и обработка ошибок на Android


Я конвертирую свой код с помощью Handler до AsyncTask. Последний отлично справляется с тем, что он делает - асинхронные обновления и обработка результатов в основном потоке пользовательского интерфейса. Мне непонятно, как обрабатывать исключения, если что-то идет не так в AsyncTask#doInBackground.

способ, которым я это делаю, - это иметь обработчик ошибок и отправлять ему сообщения. Он отлично работает, но это "правильный" подход или есть лучше альтернатива?

также я понимаю, что если я определяю обработчик ошибок в качестве поля действия он должен выполняться в потоке пользовательского интерфейса. Однако иногда (очень непредсказуемо) я получаю исключение, говоря, что код запускается из Handler#handleMessage выполняется в неправильном потоке. Я должен инициализировать обработчик ошибок в Activity#onCreate вместо этого? Размещение runOnUiThread на Handler#handleMessage кажется избыточным, но он выполняет очень надежно.

11 141

11 ответов:

он отлично работает, но это " право" подходите и там лучше альтернатива?

Я держусь за Throwable или Exception на AsyncTask сам экземпляр, а затем сделать что-то с ним в onPostExecute(), поэтому моя обработка ошибок имеет возможность отображения диалогового окна на экране.

создать объект 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. ( Вы можете использовать этот класс в качестве базового класса для других асинхронных задач )

Ниже приведен макет задачи, которая получает ответ 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
            }
        };

    }

когда я чувствую необходимость обрабатывать исключения в 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, которые передаются в качестве параметра. В большинстве случаев исключения приводят к некоторому типу вывода пользовательского интерфейса, поэтому onPostExecute - Это идеальное место, чтобы сделать что.

Если вы хотите использовать RoboGuice framework, который приносит вам другие преимущества, вы можете попробовать RoboAsyncTask, который имеет дополнительный обратный вызов onException(). Работает очень хорошо, и я использую его. http://code.google.com/p/roboguice/wiki/RoboAsyncTask

Я сделал свой собственный подкласс 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);
    }
}

более комплексное решение Кагатай Калан'S решение показано ниже:

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

этот простой класс может помочь вам

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

другой способ, который не зависит от общего доступа к переменным членам, - использовать cancel.

Это из android docs:

public final boolean cancel (boolean mayInterruptIfRunning)

пытается отменить выполнение этой задачи. Этот попытка завершится неудачей, если задача уже выполнена, уже была отменено или не может быть отменено по какой-либо другой причине. Если успешно, и эта задача не началась, когда отмена называется этот задача никогда не должна выполняться. Если задача уже запущена, то параметр mayInterruptIfRunning определяет, является ли поток выполнение этой задачи должно быть прервано при попытке остановить задача.

вызов этого метода приведет к вызову onCancelled(Object в потоке пользовательского интерфейса после doInBackground (Object[]) возвращается. Называя это метод гарантирует, что onPostExecute(Object) никогда не вызывается. После вызывая этот метод, вы должны проверить возвращаемое значение iscancelled () периодически из doInBackground (Object[]) для завершения задача как можно раньше.

таким образом, вы можете вызвать cancel в операторе catch и убедиться, что onPostExcute никогда не вызывается, но вместо этого onCancelled вызывается в потоке пользовательского интерфейса. Так что вы можете показать сообщение об ошибке.

лично я буду использовать этот подход. Вы можете просто поймать исключения и распечатать трассировку стека, если вам нужна информация.

сделайте так, чтобы ваша задача в фоновом режиме возвращала логическое значение.

это так:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

другой возможностью было бы использовать 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();
    }
}

если вы знаете правильное исключение, то вы можете вызвать

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

это будет полезно в некоторых случаях. Также вы можете сохранить GlobalException переменная и доступ к исключению.