studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones android error-handling handler android-asynctask

programacion - AsyncTask y manejo de errores en Android



manual de programacion android pdf (11)

Funciona bien, pero ¿es el enfoque "correcto" y existe una mejor alternativa?

AsyncTask o Exception en la instancia de AsyncTask y luego hago algo con él en onPostExecute() , por lo que mi manejo de errores tiene la opción de mostrar un diálogo en la pantalla.

Estoy convirtiendo mi código de usar Handler a AsyncTask . Este último es excelente en lo que hace: actualizaciones asíncronas y manejo de resultados en el hilo principal de la interfaz de usuario. Lo que no me queda claro es cómo manejar las excepciones si algo AsyncTask#doInBackground en AsyncTask#doInBackground .

La forma en que lo hago es tener un controlador de errores y enviarle mensajes. Funciona bien, pero ¿es el enfoque "correcto" o hay una mejor alternativa?

También entiendo que si defino el controlador de errores como un campo de actividad, debería ejecutarse en el hilo de la interfaz de usuario. Sin embargo, a veces (muy impredeciblemente) obtendré una Excepción que dice que el código desencadenado desde Handler#handleMessage está ejecutando en el hilo equivocado. ¿Debería inicializar Error Handler en Activity#onCreate en Activity#onCreate lugar? Colocar runOnUiThread en Handler#handleMessage parece redundante pero se ejecuta de manera muy confiable.


Crea un objeto AsyncResult (que también puedes usar en otros proyectos)

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

Devuelve este objeto de tus métodos AsInncTask doInBackground y compruébalo en postExecute. (Puede usar esta clase como una clase base para sus otras tareas asincrónicas)

A continuación se muestra una maqueta de una tarea que obtiene una respuesta JSON del servidor web.

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


Cuando siento la necesidad de manejar excepciones en AsyncTask correctamente, uso esto como 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; } }

Como es normal, anulas doInBackground en tu subclase para hacer un trabajo en segundo plano, felizmente lanzando Excepciones donde sea necesario. A continuación, se le obliga a implementar onPostExecute (porque es abstracto) y esto le recuerda suavemente que debe manejar todos los tipos de Exception , que se pasan como parámetro. En la mayoría de los casos, las Excepciones conducen a algún tipo de salida ui, por lo que onPostExecute es un lugar perfecto para hacerlo.


Esta clase simple puede ayudarte

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


Hice mi propia subclase AsyncTask con una interfaz que define devoluciones de llamada para el éxito y el fracaso. Por lo tanto, si se lanza una excepción en su AsyncTask, la función onFailure pasa la excepción, de lo contrario, la devolución de llamada onSuccess pasa su resultado. Por qué Android no tiene algo mejor disponible está más allá de mí.

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


Otra forma que no depende del intercambio de miembros variables es usar cancelar.

Esto es de documentos de Android:

público final boolean cancelar (boolean mayInterruptIfRunning)

Intenta cancelar la ejecución de esta tarea. Este intento fallará si la tarea ya se ha completado, ya se ha cancelado o no se puede cancelar por algún otro motivo. Si tiene éxito, y esta tarea no ha comenzado cuando se invoca cancelar, esta tarea nunca debería ejecutarse. Si la tarea ya ha comenzado, el parámetro mayInterruptIfRunning determina si el hilo que ejecuta esta tarea debe interrumpirse para intentar detener la tarea.

Llamar a este método dará como resultado que se invoque aCancelado (Objeto) en el subproceso de la interfaz de usuario después de que doInBackground (Objeto []) regrese. Llamar a este método garantiza que onPostExecute (Object) nunca se invoque. Después de invocar este método, debe verificar el valor devuelto por isCancelled () periódicamente desde doInBackground (Object []) para finalizar la tarea lo antes posible.

Por lo tanto, puede llamar a cancelar en la declaración catch y asegurarse de que onPostExcute nunca se invoca, sino que onCancelled se invoca en el hilo de UI. Para que pueda mostrar el mensaje de error.


Otra posibilidad sería usar Object como tipo de retorno, y en onPostExecute() verificar el tipo de objeto. Es corta.

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


Personalmente, usaré este enfoque. Solo puede capturar las excepciones e imprimir el seguimiento de la pila si necesita la información.

hacer que su tarea en segundo plano devuelva un valor booleano.

Es como esto:

@Override protected Boolean doInBackground(String... params) { return readXmlFromWeb(params[0]); } @Override protected void onPostExecute(Boolean result) { if(result){ // no error } else{ // error handling } }


Si conoce la excepción correcta, puede llamar al

Exception e = null; publishProgress(int ...);

p.ej:

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

y vaya a "onProgressUpdate" y haga lo siguiente

@Override protected void onProgressUpdate(final Integer... values) { // TODO Auto-generated method stub super.onProgressUpdate(values); mDialog.dismiss(); OptionPane.showMessage(mActivity, "Connection error", e.getMessage()); }

Esto será útil solo en algunos casos. También puede mantener una variable de Exception Global y acceder a la excepción.



Una solución más completa a la solución de Cagatay Kalan se muestra a continuación:

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

Ejemplo de tarea

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