Pergunta

Eu li muito sobre como salvar meu estado de instância ou como lidar com minha atividade sendo destruída durante a rotação da tela.

Parece haver muitas possibilidades, mas não descobri qual é melhor para recuperar os resultados de uma assíncrogem.

Eu tenho algumas assínctas que são simplesmente iniciadas de novo e chamam o isFinishing() Método da atividade e se a atividade estiver concluindo, eles não atualizam nada.

O problema é que tenho uma tarefa que faz uma solicitação a um serviço da Web que pode falhar ou ter sucesso e reiniciar a tarefa resultaria em uma perda financeira para o usuário.

Como você resolveria isso? Quais são as vantagens ou desvantagens das possíveis soluções?

Foi útil?

Solução

Minha primeira sugestão seria garantir que você realmente precise que sua atividade seja redefinida em uma rotação de tela (o comportamento padrão). Toda vez que tive problemas com a rotação, adicionei esse atributo ao meu <activity> Tag no Androidmanifest.xml e esteve bem.

android:configChanges="keyboardHidden|orientation"

Parece estranho, mas o que faz entrega ao seu onConfigurationChanged() Método, se você não fornecer um, ele não faz nada além de re-medir o layout, o que parece ser uma maneira perfeitamente adequada de lidar com a girar a maior parte do tempo.

Outras dicas

Você pode verificar como eu lido AsyncTasks e mudanças de orientação em code.google.com/p/shelves. Existem várias maneiras de fazê -lo, a que eu escolhi neste aplicativo é cancelar qualquer tarefa atualmente em execução, salvar seu estado e iniciar um novo com o estado salvo quando o novo Activity é criado. É fácil de fazer, funciona bem e, como bônus, cuida de interromper suas tarefas quando o usuário sai do aplicativo.

Você também pode usar onRetainNonConfigurationInstance() para passar o seu AsyncTask para o novo Activity (tenha cuidado para não vazar o anterior Activity assim, embora.)

Esta é a pergunta mais interessante que eu já vi sobre o Android !!! Na verdade, já tenho procurado a solução nos últimos meses. Ainda não resolveu.

Tenha cuidado, simplesmente substituindo o

android:configChanges="keyboardHidden|orientation"

as coisas não são suficientes.

Considere o caso quando o usuário receber uma ligação enquanto o seu AsyncTask estiver em execução. Sua solicitação já está sendo processada pelo servidor; portanto, o assíncrono aguarda a resposta. Neste momento, seu aplicativo entra em segundo plano, porque o aplicativo de telefone acabou de chegar em primeiro plano. OS pode matar sua atividade, pois está em segundo plano.

Por que você nem sempre mantém uma referência ao assíncrono atual no singleton fornecido pelo Android?

Sempre que uma tarefa começa, no Preexecute ou no construtor, você define:

((Application) getApplication()).setCurrentTask(asyncTask);

Sempre que termina, você o define como NULL.

Dessa forma, você sempre tem uma referência que permite que você faça algo como, OnCreate ou OnResume, como apropriado para sua lógica específica:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

Se for nulo, você sabe que atualmente não há nenhum funcionamento!

:-)

A maneira mais adequada para isso é usar um fragmento para reter a instância da tarefa assíncrona, sobre rotações.

Aqui está um link para um exemplo muito simples, facilitando o acompanhamento dessa técnica em seus aplicativos.

https://gist.github.com/daichan4649/2480065

Dentro Pro android 4. o autor sugeriu uma boa maneira, que você deve usar weak reference.

Nota de referência fraca

Para meu ponto de vista, é melhor armazenar assínctas onRetainNonConfigurationInstance Desarlitando -o do objeto de atividade atual e ligando -o a um novo objeto de atividade após a mudança de orientação. Aqui Encontrei um exemplo muito bom de como trabalhar com asynctask e progressdialog.

Android: processamento de fundo/operação assíncrona com mudança de configuração

Para manter os estados de operação assíncrona durante o processo de segundo plano: você pode obter uma ajuda de fragmentos.

Veja as seguintes etapas:

Etapa 1: Crie um fragmento sem cabeçalho, deixe dizer a tarefa de fundo e adicione uma classe de tarefa assíncrona privada com ele.

Etapa 2 (Etapa opcional): Se você deseja colocar um cursor de carregamento em cima da sua atividade, use o código:

Etapa 3: Em sua atividade principal, implementar a interface BackgroundTaskCallbacks definida na Etapa 1

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

@Override
public void dismiss() {
    super.dismiss();
}

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}

Uma coisa a considerar é se o resultado do assíncrono deve estar disponível apenas para a atividade que iniciou a tarefa. Se sim, então Resposta do Romain Guy é melhor. Se deve estar disponível para outras atividades do seu aplicativo, então em onPostExecute você pode usar LocalBroadcastManager.

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

Você também precisará garantir que a atividade lida com a situação corretamente quando a transmissão for enviada enquanto a atividade estiver em pausa.

Veja isso publicar. Esta postagem envolve o assíncada, executando a operação de longa execução e vazamento de memória quando a rotação da tela ocorre tanto em um aplicativo de amostra. O aplicativo de amostra está disponível no forge de origem

Minha solução.

No meu caso, eu tenho uma cadeia de assíncas com o mesmo contexto. A atividade teve um acesso apenas ao primeiro. Para cancelar qualquer tarefa em execução, fiz o seguinte:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

Tarefa doInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

Atividade onStop() ou onPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

Você também pode adicionar Android: configchanges = "KeyboardHidden | Orientation | Crensize"

Para o seu exemplo de manifesto, espero que ajude

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top