android Tarefa em segundo plano, diálogo de progresso, mudança de orientação-existe alguma solução de trabalho 100%?




orientation android-orientation (7)

Eu faço o download de alguns dados da internet no thread de plano de fundo (eu uso o AsyncTask ) e exibo um diálogo de progresso durante o download. Orientação muda, a atividade é reiniciada e, em seguida, meu AsyncTask é concluído - eu quero demitir o diálogo de progresso e iniciar uma nova atividade. Mas chamar dismissDialog às vezes lança uma exceção (provavelmente porque a atividade foi destruída e a nova atividade ainda não foi iniciada).

Qual é a melhor maneira de lidar com esse tipo de problema (atualizando a interface do usuário do thread em segundo plano que funciona mesmo se o usuário alterar a orientação)? Alguém da Google forneceu alguma "solução oficial"?


A resposta aceita foi muito útil, mas não tem um diálogo de progresso.

Felizmente para você, leitor, eu criei um exemplo extremamente abrangente e funcional de um AsyncTask com um diálogo de progresso !

  1. A rotação funciona e o diálogo sobrevive.
  2. Você pode cancelar a tarefa e a caixa de diálogo pressionando o botão Voltar (se desejar esse comportamento).
  3. Usa fragmentos.
  4. O layout do fragmento abaixo da atividade muda adequadamente quando o dispositivo gira.

Alguém da Google forneceu alguma "solução oficial"?

Sim.

A solução é mais uma proposta de arquitetura de aplicativos do que apenas alguns códigos .

Eles propuseram três padrões de projeto que permitem que um aplicativo trabalhe em sincronia com um servidor, independentemente do estado do aplicativo (ele funcionará mesmo se o usuário concluir o aplicativo, o usuário mudar de tela, o aplicativo for finalizado, todos os outros estados possíveis). uma operação de dados em segundo plano pode ser interrumpted, isso cobre isso)

A proposta é explicada no discurso de aplicativos do cliente REST do Android durante o Google I / O 2010 por Virgil Dobjanschi. Tem 1 hora de duração, mas vale a pena assistir.

A base disso é abstrair as operações de rede para um Service que funciona de forma independente para qualquer Activity no aplicativo. Se você estiver trabalhando com bancos de dados, o uso de ContentResolver e Cursor forneceria um padrão Observer pronto para uso, conveniente para atualizar a interface do usuário sem qualquer lógica adicional, depois de atualizar o banco de dados local com os dados remotos obtidos. Qualquer outro código de pós-operação seria executado por meio de um retorno de chamada passado para o Service (eu uso uma subclasse de ResultReceiver para isso).

De qualquer forma, minha explicação é realmente muito vaga, você deve definitivamente assistir ao discurso.


Esta é a minha solução: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog

Basicamente os passos são:

  1. Eu uso onSaveInstanceState para salvar a tarefa se ainda estiver processando.
  2. Em onCreate eu recebo a tarefa se ela foi salva.
  3. Em onPause eu descartar o ProgressDialog se for mostrado.
  4. Em onResume eu mostro o ProgressDialog se a tarefa ainda está sendo processada.

Eu trabalhei por uma semana para encontrar uma solução para esse dilema sem recorrer à edição do arquivo de manifesto. As suposições para essa solução são:

  1. Você sempre precisa usar um diálogo de progresso
  2. Apenas uma tarefa é executada por vez
  3. Você precisa que a tarefa persista quando o telefone for girado e a caixa de diálogo de progresso for descartada automaticamente.

Implementação

Você precisará copiar os dois arquivos encontrados na parte inferior desta postagem em sua área de trabalho. Apenas certifique-se de que:

  1. Todas as suas Activity devem estender a BaseActivity

  2. Em onCreate() , super.onCreate() deve ser chamado após você inicializar qualquer membro que precise ser acessado pelo seu ASyncTask s. Além disso, substitua getContentViewId() para fornecer o ID do layout do formulário.

  3. Substitua onCreateDialog() como de costume para criar diálogos gerenciados pela atividade.

  4. Veja o código abaixo para uma amostra de classe interna estática para fazer seu AsyncTasks. Você pode armazenar seu resultado em mResult para acessar mais tarde.

final static class MyTask extends SuperAsyncTask<Void, Void, Void> {

    public OpenDatabaseTask(BaseActivity activity) {
        super(activity, MY_DIALOG_ID); // change your dialog ID here...
                                       // and your dialog will be managed automatically!
    }

    @Override
    protected Void doInBackground(Void... params) {

        // your task code

        return null;
    }

    @Override
    public boolean onAfterExecute() {
        // your after execute code
    }
}

E finalmente, para lançar sua nova tarefa:

mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();

É isso aí! Espero que esta solução robusta ajude alguém.

BaseActivity.java (organize as importações você mesmo)

protected abstract int getContentViewId();

public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getContentViewId());

        mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
        if (mCurrentTask != null) {
            mCurrentTask.attach(this);
            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
        mCurrentTask.postExecution();
            }
        }
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);

        mDialogMap.put(id, true);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mCurrentTask != null) {
            mCurrentTask.detach();

            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
                return mCurrentTask;
            }
        }

        return super.onRetainNonConfigurationInstance();
    }

    public void cleanupTask() {
        if (mCurrentTask != null) {
            mCurrentTask = null;
            System.gc();
        }
    }
}

SuperAsyncTask.java

public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;

    protected abstract void onAfterExecute();

    public SuperAsyncTask(BaseActivity activity, int dialogId) {
        super();
        this.dialogId = dialogId;
        attach(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mActivity.showDialog(dialogId); // go polymorphism!
    }    

    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        mResult = result;

        if (mActivity != null &&
                mActivity.mDialogMap.get((Integer) dialogId) != null
                && mActivity.mDialogMap.get((Integer) dialogId)) {
            postExecution();
        }
    };

    public void attach(BaseActivity activity) {
        this.mActivity = activity;
    }

    public void detach() {
        this.mActivity = null;
    }

    public synchronized boolean postExecution() {
        Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
        if (dialogExists != null || dialogExists) {
            onAfterExecute();
            cleanUp();
    }

    public boolean cleanUp() {
        mActivity.removeDialog(dialogId);
        mActivity.mDialogMap.remove((Integer) dialogId);
        mActivity.cleanupTask();
        detach();
        return true;
    }
}

Etapa 1: faça do AsyncTask uma classe aninhada static ou uma classe totalmente separada, mas não uma classe interna (aninhada não estática).

Passo # 2: AsyncTask o AsyncTask na Activity por meio de um membro de dados, definido através do construtor e de um setter.

Passo # 3: Ao criar o AsyncTask , forneça a Activity atual para o construtor.

Passo # 4: Em onRetainNonConfigurationInstance() , retorne o AsyncTask , depois de separá-lo da atividade original, agora em fuga.

Passo # 5: Em onCreate() , se getLastNonConfigurationInstance() não for null , AsyncTask -o em sua classe AsyncTask e chame seu setter para associar sua nova atividade à tarefa.

Etapa 6: não se referir ao membro de dados de atividade do doInBackground() .

Se você seguir a receita acima, tudo vai funcionar. onProgressUpdate() e onPostExecute() estão suspensos entre o início de onRetainNonConfigurationInstance() e o final do onCreate() subseqüente.

Aqui está um exemplo de projeto demonstrando a técnica.

Outra abordagem é AsyncTask o AsyncTask e mover seu trabalho para um IntentService . Isso é particularmente útil se o trabalho a ser feito pode ser longo e deve continuar independentemente do que o usuário faz em termos de atividades (por exemplo, baixar um arquivo grande). Você pode usar uma Intent transmissão ordenada para que a atividade responda ao trabalho que está sendo feito (se ainda estiver em primeiro plano) ou apresente uma Notification para informar ao usuário se o trabalho foi concluído. Aqui está uma postagem no blog com mais sobre esse padrão.


você deve chamar todas as ações de atividade usando o manipulador de atividade. Então, se você está em algum tópico, você deve criar um Runnable e postar usando o Manipulador do Activitie. Caso contrário, seu aplicativo falhará às vezes com exceção fatal.


Embora a resposta de Mark (CommonsWare) realmente funcione para mudanças de orientação, ela falhará se a atividade for destruída diretamente (como no caso de uma ligação telefônica).

Você pode manipular as alterações de orientação e os raros eventos de atividade destruídos usando um objeto Aplicativo para fazer referência ao seu ASyncTask.

Há uma excelente explicação do problema e da solução here :

O crédito vai completamente para Ryan por descobrir isso.





android-orientation