AsyncTask и обработка ошибок на Android - PullRequest
144 голосов
/ 16 ноября 2009

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

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

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

Ответы [ 12 ]

173 голосов
/ 16 ноября 2009

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

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

134 голосов
/ 11 июня 2011

Создать объект 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
            }
        };

    }
10 голосов
/ 29 апреля 2013

Когда я чувствую необходимость правильно обработать исключения в 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 - идеальное место для этого.

5 голосов
/ 08 августа 2011

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

3 голосов
/ 11 октября 2014

Более полное решение 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);
    }
}
3 голосов
/ 30 июля 2014

Я создал свой собственный подкласс AsyncTask с интерфейсом, который определяет обратные вызовы для успеха и неудачи. Поэтому, если в AsyncTask выдается исключение, функция onFailure получает исключение, в противном случае обратный вызов 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);
    }
}
2 голосов
/ 10 января 2014

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

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 голосов
/ 24 июля 2013

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

Это из документации для Android:

публичная окончательная логическая отмена (логическое значение mayInterruptIfRunning)

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

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

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

0 голосов
/ 21 мая 2019

На самом деле 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();
    }
0 голосов
/ 13 февраля 2015

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