Pergunta

Estou convertendo meu código usando Handler para AsyncTask.O último é ótimo no que faz - atualizações assíncronas e manipulação de resultados no thread principal da UI.O que não está claro para mim é como lidar com exceções se algo der errado AsyncTask#doInBackground.

A maneira como faço isso é ter um manipulador de erros e enviar mensagens para ele.Funciona bem, mas é a abordagem "certa" ou existe uma alternativa melhor?

Também entendo que se eu definir o manipulador de erros como um campo de atividade, ele deverá ser executado no thread da interface do usuário.No entanto, às vezes (de forma muito imprevisível) recebo uma exceção dizendo que o código foi acionado por Handler#handleMessage está sendo executado no thread errado.Devo inicializar o manipulador de erros em Activity#onCreate em vez de?Colocação runOnUiThread em Handler#handleMessage parece redundante, mas é executado de maneira muito confiável.

Foi útil?

Solução

Funciona bem, mas é a abordagem "certa" e há uma alternativa melhor?

Eu me apego ao Throwable ou Exception no AsyncTask instância em si e depois faça algo com isso em onPostExecute(), então meu manuseio de erros tem a opção de exibir uma caixa de diálogo na tela.

Outras dicas

Crie um objeto assíncrono (que você também pode usar em outros projetos)

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

Retorne este objeto dos seus métodos AsyncTask Doinbackground e verifique -o no PostExecute. (Você pode usar esta classe como uma classe base para suas outras tarefas assíncronas)

Abaixo está uma maquete de uma tarefa que obtém uma resposta JSON do servidor da 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 sinto necessidade de lidar com exceções em AsyncTask corretamente, eu uso isso como superclasse:

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 de costume, você substitui doInBackground em sua subclasse para fazer trabalho em segundo plano, lançando exceções quando necessário.Você é então forçado a implementar onPostExecute (porque é abstrato) e isso gentilmente lembra você de lidar com todos os tipos de Exception, que são passados ​​como parâmetro.Na maioria dos casos, as exceções levam a algum tipo de saída de interface do usuário, então onPostExecute é um lugar perfeito para fazer isso.

Se você deseja usar a estrutura Roboguice, que traz outros benefícios, você pode experimentar o RoboSyncTask, que possui um retorno de chamada extra Onexception (). Funciona muito bem e eu o uso.http://code.google.com/p/roboguice/wiki/roboasynctask

Fiz minha própria subclasse de assíncrogem com uma interface que define retornos de chamada para sucesso e fracasso. Portanto, se uma exceção for lançada em sua assíncada, a função OnFailure será aprovada pela exceção; caso contrário, o retorno de chamada do Onsuccess será aprovado no seu resultado. Por que o Android não tem algo melhor disponível está além de mim.

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

Uma solução mais abrangente para CAGATAY KALANA solução é mostrada abaixo:

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

Tarefa de exemplo

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 aula simples pode ajudá -lo

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

Outra maneira que não depende do compartilhamento de membros da variável é usar o Cancel.

Isto é da Android Docs:

cancelamento booleano final público (booleano MayInterruptifrunning)

Tentativas de cancelar a execução desta tarefa. Essa tentativa falhará se a tarefa já tiver concluída, já foi cancelada ou não puder ser cancelada por algum outro motivo. Se for bem -sucedido, e essa tarefa não foi iniciada quando o cancelamento é chamado, essa tarefa nunca deve ser executada. Se a tarefa já foi iniciada, o parâmetro MayInterruptifrunning determina se o thread que executa essa tarefa deve ser interrompido na tentativa de interromper a tarefa.

Chamar esse método resultará na invocada (objeto) oncelado (objeto) no thread da interface do usuário após o retorno doinbackground (objeto []). Chamar esse método garante que o OnPostexecute (objeto) nunca seja invocado. Depois de invocar esse método, você deve verificar o valor retornado por iscancelled () periodicamente do Doinbackground (objeto []) para concluir a tarefa o mais cedo possível.

Assim, você pode ligar para cancelar a declaração de captura e verifique se o OnPostexCute nunca é chamado, mas, em vez disso, está chamado no encadeamento da interface do usuário. Então você pode mostrar a mensagem de erro.

Na verdade, assínctask Use FutureTask & Executor, FutureTask Support Chain de exceção primeiro vamos definir uma classe auxiliar

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

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

Pessoalmente, usarei essa abordagem. Você pode simplesmente pegar as exceções e imprimir o rastreamento da pilha, se precisar das informações.

Faça sua tarefa em segundo plano retorne um valor booleano.

É tipo isso:

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

        @Override
                protected void onPostExecute(Boolean result) {

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

Outra possibilidade seria usar Object como tipo de retorno, e em onPostExecute() Verifique o tipo de objeto. É curto.

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 você souber a exceção correta, pode ligar para o

Exception e = null;

publishProgress(int ...);

por exemplo:

@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 vá para "OnProgressUpdate" e faça o seguinte

@Override
protected void onProgressUpdate(final Integer... values) {

    // TODO Auto-generated method stub
    super.onProgressUpdate(values);
    mDialog.dismiss();
    OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}

Isso será útil apenas em alguns casos. Também você pode manter um Global Exception variável e acesse a exceção.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top