Domanda

sto convertendo il mio codice da utilizzare Handler a AsyncTask. Quest'ultimo è grande a quello che fa - aggiornamenti e manipolazione dei risultati nel thread UI principale asincroni. Cosa c'è di poco chiaro per me è come gestire le eccezioni se qualcosa va in tilt in AsyncTask#doInBackground.

Il modo in cui lo faccio è quello di avere un gestore di errori e inviare messaggi ad esso. Funziona bene, ma è l'approccio "giusto" o c'è un'alternativa migliore?

Inoltre ho capito che se io definisco il gestore degli errori come un campo di attività, si deve eseguire nel thread UI. Tuttavia, a volte (molto imprevedibile) mi metterò un'eccezione dicendo che il codice innescato da Handler#handleMessage è in esecuzione sul thread sbagliato. Dovrei inizializzare errore Handler in Activity#onCreate invece? Inserimento runOnUiThread in Handler#handleMessage sembra ridondante ma esegue molto affidabile.

È stato utile?

Soluzione

  

Funziona bene ma è il "diritto"   approccio ed è lì meglio   alternativa?

I trattenere il Throwable o Exception nell'istanza AsyncTask stesso e poi fare qualcosa con esso in onPostExecute(), quindi la mia gestione degli errori ha la possibilità di visualizzare una finestra sullo schermo.

Altri suggerimenti

Crea un oggetto AsyncResult (che si può anche utilizzare in altri progetti)

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

Ritornare questo oggetto dai vostri metodi AsyncTask doInBackground e controllare nel postExecute. (È possibile utilizzare questa classe come classe base per le vostre altre attività asincrone)

Di seguito è riportato un mockup di un compito che ottiene una risposta JSON dal server 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
            }
        };

    }

Quando sento il bisogno di gestire le eccezioni in AsyncTask correttamente, io uso questo come 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;
    }

}

Come di consueto, si ignora doInBackground nella sottoclasse per fare il lavoro di fondo, felicemente gettando eccezioni dove necessario. Si sono quindi costretti a implementare onPostExecute (perché è astratta) e questo vi ricorda delicatamente per gestire tutti i tipi di Exception, che sono passati come parametro. Nella maggior parte dei casi, eccezioni portare a qualche tipo di uscita ui, quindi onPostExecute è un luogo perfetto per farlo.

Se si desidera utilizzare il framework RoboGuice che porta altri benefici si può provare la RoboAsyncTask che ha un onException Callback extra (). Vere e proprie opere buone e lo uso. http://code.google.com/p/roboguice/wiki/RoboAsyncTask

Ho fatto il mio sottoclasse AsyncTask con un'interfaccia che definisce i callback per il successo e il fallimento. Quindi, se viene generata un'eccezione nel vostro AsyncTask, la funzione onFailure viene passato l'eccezione, in caso contrario la richiamata onSuccess viene passato il vostro risultato. Perché Android non ha qualcosa di meglio disponibile è oltre me.

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 soluzione più completa per Cagatay Kalan s 'è la seguente:

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

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

Questa semplice classe può aiutare a

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

Un altro modo che non dipende condivisione membro variabile è quello di utilizzare annullare.

Questo è da Documenti per Android:

  

booleano finale pubblica annullare (mayInterruptIfRunning booleano)

     

I tentativi di annullare l'esecuzione di questa operazione. Questo   tentativo avrà esito negativo se l'attività ha già completato, già stato   annullato, o non poteva essere annullato per qualche altro motivo. Se   successo, e questo compito non è iniziato quando annulla viene chiamato, questo   compito dovrebbe mai eseguito. Se l'attività è già iniziata, quindi il   mayInterruptIfRunning parametro determina se il filo   l'esecuzione di questa operazione deve essere interrotta nel tentativo di fermare la   compito.

     

La chiamata a questo metodo si tradurrà in onCancelled (Object) viene invocata   sul thread UI dopo doInBackground (Object []) restituisce. la chiamata a questo   metodo garantisce che OnPostExecute (Object) non viene mai richiamato. Dopo   invocando questo metodo, si dovrebbe controllare il valore restituito da   isCancelled () periodicamente da doInBackground (Object []) per terminare la   compito il più presto possibile.

Così si può chiamare annullare in catch ed essere sicuri che onPostExcute non viene mai chiamato, ma invece onCancelled viene richiamato sul thread dell'interfaccia utente. Così si può mostrare il messaggio di errore.

In realtà, utilizzare AsyncTask FutureTask & esecutore, FutureTask supporto eccezioni a catena In primo luogo definiamo una classe di supporto

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

In secondo luogo, usiamo

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

Personalmente, io uso questo approccio. Si può solo prendere le eccezioni e stampare l'analisi dello stack se avete bisogno di informazioni.

rendere il vostro compito in background restituisce un valore booleano.

è in questo modo:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

Un'altra possibilità sarebbe quella di utilizzare Object come tipo di ritorno, e sotto controllo onPostExecute() per il tipo di oggetto. E 'breve.

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

Se si conosce l'eccezione corretto allora è possibile chiamare il

Exception e = null;

publishProgress(int ...);

es:

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

e andare a "onProgressUpdate" e fare il 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());
}

Questo sarà utile solo in alcuni casi. Inoltre è possibile mantenere una variabile Global Exception e accedere l'eccezione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top