Limpe toda a pilha de histórico e inicie uma nova atividade no Android

332

É possível iniciar uma atividade na pilha, limpando todo o histórico antes dela?

A situação

Eu tenho uma pilha de atividades que vai A-> B-> C ou B-> C (a tela A seleciona o token de usuário, mas muitos usuários têm apenas um único token).

Na tela C, o usuário pode executar uma ação que invalida a tela B; portanto, o aplicativo deseja levá-los para a tela A, independentemente de já estar na pilha. A tela A deve ser o único item da pilha no meu aplicativo.

Notas

Existem muitas outras perguntas semelhantes, mas não encontrei nada que responda a essa pergunta exata. Tentei ligar getParent().finish()- isso sempre resulta em uma exceção de ponteiro nulo. FLAG_ACTIVITY_CLEAR_TOPsó funciona se a atividade já estiver na pilha.

Casebash
fonte

Respostas:

658

No nível 11 da API, um novo sinalizador de intenção foi adicionado apenas para isso: Intent.FLAG_ACTIVITY_CLEAR_TASK

Apenas para esclarecer, use o seguinte:

Java

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

Kotlin

intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

Infelizmente para a API lvl <= 10, ainda não encontrei uma solução limpa para isso. A solução "DontHackAndroidLikeThis" é de fato pura invasão. Você não deveria fazer isso. :)

Edit: Conforme o comentário de @ Ben Pearson , para a API <= 10 agora é possível usar a classe IntentCompat da mesma forma. Pode-se usar a IntentCompat.FLAG_ACTIVITY_CLEAR_TASKbandeira para limpar a tarefa. Assim, você também pode oferecer suporte ao nível 11 da pré-API.

Akos Cz
fonte
23
Apenas para esclarecer, use o seguinte: intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
user123321
2
sem a Intent.FLAG_ACTIVITY_NEW_TASK o aplicativo às vezes só se fecha no android 4
max4ever
22
O IntentCompat também possui um sinalizador para limpar a tarefa, para que você possa oferecer suporte ao nível 11 da API anterior - developer.android.com/reference/android/support/v4/content/…
Ben Pearson
10
IntentCompat.FLAG_ACTIVITY_CLEAR_TASK é ignorado em dispositivos com nível de API <10. developer.android.com/reference/android/support/v4/content/…
David
7
A bandeira do IntentCompat é apenas para evitar uma falha, mas não faz nada como o @David diz.
Sloy 18/08/14
49

Caso 1: Apenas duas atividades A e B:

Aqui o fluxo da atividade é A-> B. Ao clicar no botão voltar de B, precisamos fechar o aplicativo e, ao iniciar a atividade B a partir de A, basta concluir (), isso impedirá o android de armazenar a atividade A no Backstack.eg para a atividade A Tela Loding / Splash do aplicativo.

Intent newIntent = new Intent(A.this, B.class);
startActivity(newIntent);
finish();

Caso 2: Mais de duas atividades:

Se houver um fluxo como A-> B-> C-> D-> B e clicar no botão Voltar na Atividade B enquanto estiver vindo da Atividade D. Nesse caso, devemos usar.

Intent newIntent = new Intent(D.this,B.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);

Aqui, a Atividade B será iniciada a partir do backstack em vez de uma nova instância por causa do Intent.FLAG_ACTIVITY_CLEAR_TOP e Intent.FLAG_ACTIVITY_NEW_TASK limpa a pilha e a torna a primeira.

monish george
fonte
2
Isso funcionou para mim. Eu coloquei em TODAS as atividades essas bandeiras. Nessas atividades, os botões voltar funcionam perfeitamente indo para a atividade anterior e, na atividade principal com intenção intenção = nova intenção (Intent.ACTION_MAIN); intent.addCategory (Intent.CATEGORY_HOME); intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); startActivity (intenção); terminar(); O aplicativo inteiro está fechado, ainda na memória, mas não ativo, e se u reiniciar o aplicativo vai para a tela inicial :)
Rako
Essa deve ser a melhor resposta. Se alguém tiver um cenário o mesmo comigo: A-> B-> C-> D-> E -> (B) De E-> B deve ter um resultado: A-> B
Shem Alexis Chavez
39

Com a versão mais recente do Android> = API 16, use finishAffinity()

abordagem é adequada para> = API 16.

Intent mIntent = new Intent(mContext,MainActivity.class);
finishAffinity();
startActivity(mIntent);
  • É o mesmo que iniciar uma nova atividade e limpar toda a pilha.
  • OU Reinicie em MainActivity / FirstActivity.
karan
fonte
1
Isso fez o truque, as bandeiras não estavam funcionando no 4.xx para mim e isso funcionou perfeitamente! Obrigado
Jonathan Aste
1
Essa parece ser a resposta correta se seu objetivo é concluir todas as atividades abaixo e incluindo a atividade atual e iniciar uma nova atividade em sua própria tarefa.
ToBe
24

Também passei algumas horas nisso ... e concordo que FLAG_ACTIVITY_CLEAR_TOP soa como o que você deseja: limpe toda a pilha, exceto a atividade que está sendo iniciada, para que o botão Voltar saia do aplicativo. No entanto, como Mike Repass mencionou, FLAG_ACTIVITY_CLEAR_TOP só funciona quando a atividade que você está iniciando já está na pilha; quando a atividade não está lá, a bandeira não faz nada.

O que fazer? Coloque a atividade sendo iniciada na pilha com FLAG_ACTIVITY_NEW_TASK, que torna essa atividade o início de uma nova tarefa na pilha de histórico. Em seguida, adicione o sinalizador FLAG_ACTIVITY_CLEAR_TOP.

Agora, quando FLAG_ACTIVITY_CLEAR_TOP for encontrar a nova atividade na pilha, ela estará lá e será exibida antes que todo o resto seja limpo.

Aqui está a minha função de logout; o parâmetro View é o botão ao qual a função está conectada.

public void onLogoutClick(final View view) {
    Intent i = new Intent(this, Splash.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(i);
    finish();
}
user2895402
fonte
1
você quer dizer CLEAR_TASK em vez de CLEAR_TOP?
Andy
14

Você não deve mudar a pilha. O botão Voltar do Android deve funcionar como em um navegador da web.

Eu posso pensar em uma maneira de fazer isso, mas é um truque.

  • Faça suas atividades singleTaskadicionando-as ao AndroidManifest exemplo:

    <activity android:name=".activities.A"
              android:label="@string/A_title"
              android:launchMode="singleTask"/>
    
    <activity android:name=".activities.B"
              android:label="@string/B_title"
              android:launchMode="singleTask"/>
  • Estenda o Applicationque manterá a lógica de onde ir.

Exemplo:

public class DontHackAndroidLikeThis extends Application {

  private Stack<Activity> classes = new Stack<Activity>();

  public Activity getBackActivity() {
    return classes.pop();
  }

  public void addBackActivity(Activity activity) {
    classes.push(activity);
  }
}

De a para B:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(A.class); 
startActivity(this, B.class);

De B ​​a C:

DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
app.addBackActivity(B.class); 
startActivity(this, C.class);

Em C:

If ( shouldNotGoBackToB() ) {
  DontHackAndroidLikeThis app = (DontHackAndroidLikeThis) getApplication();
  app.pop();
}

e segure o botão voltar para pop() da pilha.

Mais uma vez, você não deve fazer isso :)

Macarse
fonte
No final, decido deixar a pilha intacta e apenas informar ao usuário que sua tela atual é inválida.
Casebash 16/08/10
1
Muito frustrante que o Android ainda não nos permita gerenciar a pilha de atividades dessa maneira. Eu ficaria tentado a usar esta solução em meus futuros aplicativos para Android.
Cephron
4
Só para esclarecer por que isso não deve ser usado: é uma boa maneira de criar vazamentos de memória. Em algum momento, o sistema operacional pode decidir interromper as atividades em segundo plano, mas, como Applicationocorre em suas instâncias, o sistema operacional não poderá liberar a RAM restante das atividades destruídas.
Vit Khudenko
@Arhimed Existem outros problemas? O vazamento de memória pode ser corrigido mantendo apenas referências fracas.
Navin
1
@ Navin sim, vazamentos podem ser evitados com referências fracas, mas se após o GC não houver uma ref de atividade ao vivo, toda a abordagem será inútil. Mais uma vez - não faça isso, esta é uma abordagem errada para o Android.
Vit Khudenko
12

Imediatamente após iniciar uma nova atividade, use startActivity, certifique-se de ligar finish()para que a atividade atual não fique empilhada atrás da nova.

Keith Maurino
fonte
+1 Solução agradável para impedir que exatamente uma atividade em uma determinada situação seja colocada na pilha de histórico.
Marsbear #
27
não funciona se você tiver mais de uma atividade na pilha o acabamento só vai limpar a atividade anterior, mas não os outros ....
Necronet
5

Tente o seguinte:

Intent logout_intent = new Intent(DashboardActivity.this, LoginActivity.class);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
logout_intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(logout_intent);
finish();
Mohammad
fonte
4

Kotlin reutilizável avançado:

Você pode definir o sinalizador diretamente usando o método setter. No Kotlin oré o substituto para o Java bit a bit ou |.

intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK

Se você planeja usá-lo regularmente, crie uma função de extensão Intent

fun Intent.clearStack() {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}

Você pode chamar diretamente essa função antes de iniciar a intenção

intent.clearStack()

Se você precisar adicionar outros sinalizadores em outras situações, adicione um parâmetro opcional à função de extensão.

fun Intent.clearStack(additionalFlags: Int = 0) {
    flags = additionalFlags or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
Gibolt
fonte
2
Intent i = new Intent(MainPoliticalLogin.this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
Neeraj Gupta
fonte
2

Experimente o código abaixo,

Intent intent = new Intent(ManageProfileActivity.this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|
                Intent.FLAG_ACTIVITY_CLEAR_TASK| 
                Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
shashikant yadav
fonte
se estou usando como esta activityis atualizado uma vez api novamente chamada, mas anteriormente existente tudo statck é apagada
Harsha
2

Para mim, nenhum dos métodos acima não funciona.

Faça isso para limpar todas as atividades anteriores :

finishAffinity() // if you are in fragment use activity.finishAffinity()
Intent intent = new Intent(this, DestActivity.class); // with all flags you want
startActivity(intent)
Amir Hossein Ghasemi
fonte
-1

Às vezes, o emulador do Android pode não conseguir conectar a ferramenta DDMS do eclipse e solicitar que o adb seja iniciado manualmente. Nesse caso, você pode iniciar ou parar o adb usando o prompt de comando.

RajeshkumarG
fonte
1
Às vezes, seu emulador Android pode não conseguir conectar a ferramenta DDMS do eclipse e solicitar que o adb seja iniciado manualmente. Nesse caso, você pode iniciar ou parar o adb usando o prompt de comando. Intenção i = nova Intenção (OldActivity.this, NewActivity.class); // defina a nova tarefa e limpe os sinalizadores i.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) startActivity (i);
RajeshkumarG
-2

Eu achei um hack muito simples, basta fazer isso, adicionar um novo elemento AndroidManifestcomo: -

<activity android:name=".activityName"
          android:label="@string/app_name"
          android:noHistory="true"/>

o android:noHistorylimpará sua atividade indesejada do Stack.

Tauseef
fonte
2
Essa abordagem pode causar problemas no Android 6.0 ou superior, se você solicitar permissões nesta Atividade.
Vitaliy Um