Question

Je convertir mon code d'utiliser Handler à AsyncTask. Ce dernier est grande à ce qu'il fait - les mises à jour asynchrones et la manipulation des résultats dans le thread principal de l'interface utilisateur. Ce qui est clair pour moi est de savoir comment gérer les exceptions si quelque chose se détraque dans AsyncTask#doInBackground.

La façon dont je le fais est d'avoir un gestionnaire d'erreur et d'envoyer des messages. Il fonctionne très bien, mais est-ce l'approche « droit » ou est-il une meilleure alternative?

Je comprends également que si je définis le gestionnaire d'erreur comme un champ d'activité, il doit exécuter dans le thread d'interface utilisateur. Cependant, parfois (très imprévisiblement) Je vais faire une exception en disant que le code déclenché à partir Handler#handleMessage est exécuté sur le mauvais fil. Dois-je initialiser gestionnaire d'erreurs dans Activity#onCreate à la place? Mise en place runOnUiThread en Handler#handleMessage semble redondant, mais il exécute de manière très fiable.

Était-ce utile?

La solution

  

Il fonctionne très bien, mais est-ce le « droit »   approche et est-il mieux   alternative?

J'agripper la Throwable ou Exception dans l'instance de AsyncTask lui-même et faire quelque chose avec elle dans onPostExecute(), donc ma gestion des erreurs a la possibilité d'afficher une boîte de dialogue à l'écran.

Autres conseils

Créer un objet AsyncResult (que vous pouvez également utiliser dans d'autres projets)

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

Retour cet objet de vos méthodes AsyncTask doInBackground et vérifier dans le postExecute. (Vous pouvez utiliser cette classe comme une classe de base pour vos autres tâches async)

Voici une maquette d'une tâche qui obtient une réponse JSON à partir du serveur 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
            }
        };

    }

Quand je sens la nécessité de gérer les exceptions dans AsyncTask correctement, je l'utiliser comme super-classe:

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

}

Comme d'habitude, vous substituez doInBackground dans votre sous-classe pour faire un travail de fond, jetant heureusement des exceptions en cas de besoin. Vous êtes alors obligé de mettre en œuvre onPostExecute (parce qu'il est abstrait) et cela vous rappelle doucement pour traiter tous les types de Exception, qui sont passés en paramètre. Dans la plupart des cas, des exceptions conduisent à un certain type de sortie ui, donc onPostExecute est un endroit idéal pour le faire.

Si vous souhaitez utiliser le cadre de RoboGuice qui vous apporte d'autres avantages que vous pouvez essayer le RoboAsyncTask qui a un onException de rappel supplémentaire (). Fonctionne vraiment bien et je l'utilise. http://code.google.com/p/roboguice/wiki/RoboAsyncTask

J'ai fait ma propre sous-classe AsyncTask avec une interface qui définit les callbacks pour le succès et l'échec. Donc, si une exception est levée dans votre AsyncTask, la fonction onFailure est transmis à l'exception, sinon le rappel onSuccess est transmis votre résultat. Pourquoi android ne pas quelque chose de mieux disponible est au-delà de moi.

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

Une solution plus complète Cagatay solution de Kalan est indiqué ci-dessous:

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

Exemple Task

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

Cette classe simple peut vous aider

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

Une autre façon qui ne dépend pas de partage de membre variable est d'utiliser annuler.

Ceci est de docs Android:

  

boolean public final annuler (mayInterruptIfRunning booléenne)

     

Les tentatives d'annuler l'exécution de cette tâche. Cette   tentative échouera si la tâche a déjà terminé, déjà été   annulé, ou ne peut pas être annulée pour une autre raison. Si   avec succès, et cette tâche n'a pas commencé quand annulation est appelée, cette   tâche ne doit jamais fonctionner. Si la tâche a déjà commencé, le   mayInterruptIfRunning paramètre détermine si le fil   l'exécution de cette tâche doit être interrompue pour tenter d'arrêter la   tâche.

     

L'appel de cette méthode se traduira par onCancelled (Object) invoquée   sur le thread d'interface utilisateur après doInBackground (Object []) les retours. L'appel de cette   méthode garantit que onPostExecute (Object) est jamais invoqué. Après   l'invocation de cette méthode, vous devez vérifier la valeur retournée par   isCancelled () périodiquement à partir de doInBackground (Object []) pour terminer le   tâche le plus tôt possible.

Vous pouvez appeler annuler dans la déclaration de capture et assurez-vous que onPostExcute est jamais appelé, mais onCancelled est appelée sur thread d'interface utilisateur. Ainsi, vous pouvez afficher le message d'erreur.

En fait, AsyncTask utiliser FutureTask & Exécuteur, exception chaîne de soutien FutureTask Tout d'abord nous allons définir une classe d'aide

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 second lieu, nous allons utiliser

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

Personnellement, je vais utiliser cette approche. Vous pouvez juste attraper les exceptions et imprimer la trace de la pile si vous avez besoin d'informations.

rendre votre tâche en arrière-plan renvoie une valeur booléenne.

il est comme ceci:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

Une autre possibilité serait d'utiliser Object comme type de retour, et dans le contrôle de onPostExecute() pour le type d'objet. Il est court.

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 vous connaissez l'exception correcte, vous pouvez appeler le

Exception e = null;

publishProgress(int ...);

par exemple:

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

et aller à "onProgressUpdate" et faire le 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());
}

Cela sera utile dans certains cas seulement. vous pouvez également conserver une variable Global de Exception et accéder à l'exception.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top