Pregunta

Me estoy convirtiendo mi código de usar Handler a AsyncTask. Este último es grande en lo que hace - actualizaciones asíncronas y la manipulación de los resultados en el hilo principal interfaz de usuario. Lo que es claro para mí es cómo manejar excepciones si algo se descontrola en AsyncTask#doInBackground.

La manera de hacerlo es tener un gestor de errores y enviar mensajes a ella. Funciona bien, pero es que el enfoque de "derecha" o hay una mejor alternativa?

También entiendo que si yo defino el gestor de errores como un campo de actividad, se debe ejecutar en el hilo de interfaz de usuario. Sin embargo, a veces (muy impredecible) voy a tener una excepción diciendo que el código disparado desde Handler#handleMessage se ejecuta en el hilo equivocado. ¿Debo inicializar el controlador de errores en Activity#onCreate en su lugar? La colocación de runOnUiThread en Handler#handleMessage parece redundante, pero se ejecuta de manera muy fiable.

¿Fue útil?

Solución

  

Funciona bien, pero es que el "derecho"   aproximación y hay una mejor   alternativa?

Me aferrarse a la Throwable o Exception en la propia instancia AsyncTask y luego hacer algo con él en onPostExecute(), por lo que mi tratamiento de errores tiene la opción de mostrar un cuadro de diálogo en la pantalla.

Otros consejos

Crea un objeto AsyncResult (que también se puede utilizar 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;
    }
}

Vuelta este objeto a partir de sus métodos AsyncTask doInBackground y comprobar que en el postExecute. (Puede utilizar esta clase como clase base para sus otras tareas asíncronas)

A continuación se muestra una maqueta de una tarea que recibe una respuesta JSON desde el 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 adecuadamente, yo uso esto como superclase:

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, se reemplaza doInBackground en la subclase para hacer el trabajo de fondo, que lanza feliz excepciones cuando sea necesario. Usted se ven obligados a poner en práctica onPostExecute (porque es abstracta) y esto suavemente le recuerda que debe manejar todo tipo de Exception, que se pasan como parámetro. En la mayoría de los casos, excepciones conducen a algún tipo de salida de la interfaz de usuario, por lo onPostExecute es un lugar perfecto para hacerlo.

Si desea utilizar el marco RoboGuice que trae otros beneficios que usted puede probar el RoboAsyncTask que tiene una devolución de llamada onException extra (). Funciona muy bien y lo uso. http://code.google.com/p/roboguice/wiki/RoboAsyncTask

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

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

una solución más completa para Cagatay Kalan 's 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);
    }
}

Esta clase simple puede ayudar

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

Otra forma de que no depende de intercambio de miembro de la variable es el uso de cancelar.

Esto es de docs Android:

  

public final boolean cancelar (mayInterruptIfRunning booleano)

     

Los intentos de cancelar la ejecución de esta tarea. Esta   intento fallará si la tarea ya ha finalizado, ya ha sido   cancelado, o no podría ser cancelada por alguna otra razón. Si   éxito, y esta tarea no ha comenzado cuando la cancelación se llama, este   tarea no debe funcionar nunca. Si la tarea ya ha comenzado, entonces el   mayInterruptIfRunning parámetro determina si el hilo   la ejecución de esta tarea debe ser interrumpido en un intento de detener la   tarea.

     

llama a este método dará lugar a onCancelled (Object) se invoca   en el subproceso de interfaz de usuario después de doInBackground (Object []) devuelve. Al llamar a este   método garantiza que onPostExecute (Objeto) no se invoca. Después   la invocación de este método, se debe comprobar el valor devuelto por   isCancelled () periódicamente de doInBackground (Object []) para terminar la   tarea tan pronto como sea posible.

Así que usted puede llamar a cancelar en la declaración de capturas y estar seguros de que onPostExcute nunca es llamado, pero en lugar onCancelled se invoca en hilo de interfaz de usuario. Para que pueda mostrar el mensaje de error.

En realidad, AsyncTask utilizar FutureTask y Ejecutor, FutureTask apoyo excepción de cadena En primer lugar vamos a definir una clase de ayuda

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

En segundo lugar, vamos a usar

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

En lo personal, voy a utilizar este enfoque. Usted puede simplemente coger las excepciones e imprimir el seguimiento de la pila si necesita la información.

hacer su tarea en segundo plano devuelve un valor booleano.

es como sigue:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

Otra posibilidad sería utilizar Object como tipo de retorno, y el registro de entrada onPostExecute() para el tipo de objeto. Es corto.

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

Si conoce la excepción correcta, entonces puede llamar a la

Exception e = null;

publishProgress(int ...);

por ejemplo:

@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 hacer lo folowing

@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 sólo en algunos casos. También se puede mantener una variable Global Exception y acceder a la excepción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top