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

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

То, как я это делаю, – иметь обработчик ошибок и отправлять ему сообщения. Он отлично работает, но является ли он «правильным» или лучше альтернатива?

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

Solutions Collecting From Web of "AsyncTask и обработка ошибок на Android"

Он отлично работает, но является ли он «правильным» и лучше ли альтернатива?

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

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

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

Другой способ, который не зависит от совместного использования переменных членов, заключается в использовании отмены.

Это от android docs:

Public final boolean cancel (boolean mayInterruptIfRunning)

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

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

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

Ниже представлено более комплексное решение для решения 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); } } 

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

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

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

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

Сделайте свою задачу в фоновом режиме возвратом логического значения.

это вот так:

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

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