Em primeiro lugar, eu sei que não se deve realmente matar / reiniciar um aplicativo no Android. No meu caso de uso, desejo redefinir o aplicativo de fábrica em um caso específico em que um servidor envia uma informação específica ao cliente.
O usuário pode fazer login apenas no servidor com UMA instância do aplicativo (ou seja, vários dispositivos não são permitidos). Se outra instância obtiver o bloqueio "logado", todas as outras instâncias desse usuário terão que excluir seus dados (redefinição de fábrica), para manter a consistência.
É possível obter o bloqueio à força, porque o usuário pode excluir o aplicativo e reinstalá-lo, o que resultaria em um ID de instância diferente e o usuário não conseguiria mais liberar o bloqueio. Portanto, é possível obter o bloqueio à força.
Por causa dessa possibilidade de força, precisamos sempre verificar, em um caso concreto, se ela possui a trava. Isso é feito em (quase) cada solicitação ao servidor. O servidor pode enviar um "ID de bloqueio errado". Se isso for detectado, o aplicativo cliente deve excluir tudo.
Esse foi o caso de uso.
Eu tenho um Activity
A que inicia o Login Activity
L ou o Activity
B principal do aplicativo, dependendo de um valor sharedPrefs. Depois de iniciar L ou B, ele se fecha para que apenas L ou B esteja em execução. Portanto, no caso em que o usuário está logado, B já está sendo executado agora.
B inicia C. C chama startService
o IntentService
D. Isso resulta nessa pilha:
(A)> B> C> D
No método onHandleIntent de D, um evento é enviado para um ResultReceiver R.
R agora lida com esse evento, fornecendo ao usuário uma caixa de diálogo onde ele pode optar por redefinir o aplicativo de fábrica (excluir o banco de dados, sharedPrefs etc.)
Após a redefinição de fábrica, desejo reiniciar o aplicativo (para fechar todas as atividades) e iniciar somente A novamente, que inicia o logon Activity
L e termina:
(A)> L
O método onClick do Dialog se parece com o seguinte:
@Override
public void onClick(DialogInterface dialog, int which) {
// Will call onCancelListener
MyApplication.factoryReset(); // (Deletes the database, clears sharedPrefs, etc.)
Intent i = new Intent(MyApp.getContext(), A.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyApp.getContext().startActivity(i);
}
E essa é a MyApp
classe:
public class MyApp extends Application {
private static Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
public static void factoryReset() {
// ...
}
}
O problema é que se eu usar FLAG_ACTIVITY_NEW_TASK
as atividades B e C ainda estiverem em execução. Se eu apertar o botão voltar no login Activity
, vejo C, mas quero voltar à tela inicial.
Se eu não definir FLAG_ACTIVITY_NEW_TASK
, recebo o erro:
07-07 12:27:12.272: ERROR/AndroidRuntime(9512): android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Não posso usar as atividades ' Context
, porque o ServiceIntent
D também pode ser chamado a partir de uma tarefa em segundo plano iniciada pelo AlarmManager
.
Então, como eu poderia resolver isso para a pilha de atividades se tornar (A)> L?
fonte
Você pode simplesmente ligar para:
Que é usado na biblioteca ProcessPhoenix
Como uma alternativa:
Aqui está uma versão um pouco melhorada da resposta do @Oleg Koshkin.
Se você realmente deseja reiniciar sua atividade, incluindo uma interrupção do processo atual, tente seguir o código. Coloque-o em uma HelperClass ou onde você precisar.
Isso também reinicializará as classes jni e todas as instâncias estáticas.
fonte
AlarmManager
e se comportam mal ao usar esta solução. Alguma abordagem melhor?Jake Wharton publicou recentemente sua biblioteca ProcessPhoenix , que faz isso de uma maneira confiável. Você basicamente só precisa ligar para:
A biblioteca concluirá automaticamente a atividade de chamada, interromperá o processo do aplicativo e reiniciará a atividade padrão do aplicativo posteriormente.
fonte
<category android:name="android.intent.category.DEFAULT" />
à atividade padrão <intent-filter> no manifesto do aplicativo.Modifiquei ligeiramente a resposta Ilya_Gazman para usar novas APIs (o IntentCompat foi descontinuado ao iniciar a API 26). Runtime.getRuntime (). Exit (0) parece ser melhor que System.exit (0).
fonte
System.exit(n)
é efetivamente equivalente à chamada:Runtime.getRuntime().exit(n)
". Internamente,System.exit()
apenas vira e ligaRuntime.getRuntime().exit()
. Não há nada "melhor" em um ou no outro (a menos que se preocupe com a digitação de um ou com uma camada extra de chamadas de método).Runtime.getRuntime().exit(0)
(ouSystem.exit(0)
) provavelmente funcionará. Alguns dos meus comentários "não bons" são para respostas (como a de Ilya Gazman, que foi editada para incorporar tal chamada.IntentCompat.makeRestartActivityTask
A nova maneira de fazer isso é usando IntentCompat.makeRestartActivityTask
fonte
Application
objeto. Portanto, quaisquerstatic
dados, dados inicializados durante a criação dasApplication
classes, ou jni permanecem em seu estado atual e não são reinicializados.IntentCompat.makeRestartActivityTask
agora está obsoleto . Se você inspecionar o código fonte , é tão simples quanto simplesmente adicionar os sinalizadoresIntent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
.Existe um truque muito bom. Meu problema era que algumas bibliotecas jni C ++ realmente antigas vazavam recursos. Em algum momento, ele parou de funcionar. O usuário tentou sair do aplicativo e iniciá-lo novamente - sem resultado, porque terminar uma atividade não é o mesmo que terminar (ou matar) o processo. (A propósito, o usuário pode ir para a lista de aplicativos em execução e parar a partir daí - isso funcionaria, mas os usuários simplesmente não sabem como finalizar aplicativos.)
Se você quiser observar o efeito desse recurso, adicione uma
static
variável à sua atividade e aumente cada uma, digamos, pressione o botão. Se você sair da atividade do aplicativo e depois invocá-lo novamente, essa variável estática manterá seu valor. (Se o aplicativo realmente fosse encerrado, a variável receberia o valor inicial.)(E eu tenho que comentar por que eu não quis corrigir o bug. A biblioteca foi escrita décadas atrás e vazou recursos desde então. A gerência acredita que sempre funcionou . O custo de fornecer uma correção em vez de uma solução alternativa ... Eu acho que você entendeu.)
Agora, como eu poderia redefinir uma biblioteca compartilhada jni (também conhecida como .so) para o estado inicial? Eu escolhi reiniciar o aplicativo como um novo processo.
O truque é que System.exit () fecha a atividade atual e o Android recria o aplicativo com uma atividade a menos.
Então o código é:
A atividade de chamada apenas executa o código
MagicAppRestart.doRestart(this);
, a atividade de chamadaonPause()
é executada e, em seguida, o processo é recriado. E não se esqueça de mencionar esta atividade no AndroidManifest.xmlA vantagem deste método é que não há atrasos.
UPD: funcionou no Android 2.x, mas no Android 4 algo mudou.
fonte
System.exit(0)
porandroid.os.Process.killProcess(android.os.Process.myPid());
? 2) um loop infinito provavelmente significa que eles não removem a atividade superior quando reiniciam um aplicativo. Em princípio, você pode adicionar uma variável booleana estática, configurá-la como true antes de chamar a atividade de reinicialização e, após a reinicialização, ela será falsa. Assim, a atividade pode descobrir se o reinício já ocorreu (e se ocorreu, basta finalizar () ). OTOH, seu relatório significa que o truque não funciona de forma idêntica em todos os dispositivos.Minha solução não reinicia o processo / aplicativo. Ele permite apenas que o aplicativo "reinicie" a atividade em casa (e descarte todas as outras atividades). Parece uma reinicialização para os usuários, mas o processo é o mesmo. Eu acho que em alguns casos as pessoas querem alcançar esse efeito, então apenas deixo aqui para a sua informação.
fonte
Ok, refatorei meu aplicativo e não terminarei A automaticamente. Eu deixo isso correr sempre e termino no
onActivityResult
evento. Dessa forma, posso usar os sinalizadoresFLAG_ACTIVITY_CLEAR_TOP
+FLAG_ACTIVITY_NEW_TASK
para obter o que quero:E no
ResultReceiver
Obrigado mesmo assim!
fonte
fonte
O único código que não acionou "Seu aplicativo foi fechado inesperadamente" é o seguinte. Também é um código não obsoleto que não requer uma biblioteca externa. Também não requer um temporizador.
fonte
Descobri que isso funciona na API 29 e posterior - com o objetivo de matar e reiniciar o aplicativo como se o usuário o tivesse lançado quando não estava em execução.
Isso foi feito quando a atividade do iniciador no aplicativo tem o seguinte:
Vi comentários alegando que uma categoria de PADRÃO é necessária, mas não achei que fosse esse o caso. Confirmei que o objeto Aplicativo no meu aplicativo foi recriado, por isso acredito que o processo realmente foi morto e reiniciado.
O único objetivo para o qual eu uso isso é reiniciar o aplicativo depois que o usuário tiver ativado ou desativado o relatório de falhas do Firebase Crashlytics. De acordo com os documentos, o aplicativo precisa ser reiniciado (processo eliminado e recriado) para que essa alteração entre em vigor.
fonte
A melhor maneira de reiniciar completamente um aplicativo é reiniciá-lo, não apenas para ir para uma atividade com
FLAG_ACTIVITY_CLEAR_TOP
eFLAG_ACTIVITY_NEW_TASK
. Portanto, minha solução é fazer isso no seu aplicativo ou mesmo em outro aplicativo, a única condição é saber o nome do pacote do aplicativo (exemplo: ' com.example.myProject ')Exemplo de uso reinicie ou inicie o appA do appB :
Você pode verificar se o aplicativo está sendo executado:
Nota : Eu sei que esta resposta está um pouco fora de tópico, mas pode ser realmente útil para alguém.
fonte
Minha melhor maneira de reiniciar o aplicativo é usar o
finishAffinity();
Since, que
finishAffinity();
pode ser usado apenas nas versões JELLY BEAN, para que possamos usarActivityCompat.finishAffinity(YourCurrentActivity.this);
nas versões inferiores.Em seguida, use
Intent
para iniciar a primeira atividade, para que o código fique assim:Espero que ajude.
fonte
Tente usar
FLAG_ACTIVITY_CLEAR_TASK
fonte
Aqui está um exemplo para reiniciar seu aplicativo de maneira genérica usando o PackageManager:
fonte
Application
objeto. Portanto, quaisquer dados estáticos, dados inicializados durante a criação dasApplication
classes, ou jni permanecem em seu estado atual e não são reinicializados.tente isto:
fonte
Inicie diretamente a tela inicial com
FLAG_ACTIVITY_CLEAR_TASK
eFLAG_ACTIVITY_NEW_TASK
.fonte
Eu tive que adicionar um manipulador para atrasar a saída:
fonte
Usar:
Funciona a partir do nível 16 da API (4.1), acredito.
fonte
Você pode usar o
startInstrumentation
método deActivity
. Você precisa implementar vazioInstrumentation
e apontado no manifesto. Depois disso, você pode chamar esse método para reiniciar seu aplicativo. Como isso:Recebo o nome da classe Instrumentation dinamicamente, mas você pode codificá-lo. Alguns assim:
Ligue para
startInstrumentation
fazer a recarga do seu aplicativo. Leia a descrição deste método. Mas pode não ser seguro se agir como um aplicativo matador.fonte
O aplicativo em que estou trabalhando tem que dar ao usuário a possibilidade de escolher quais fragmentos exibir (os fragmentos são alterados dinamicamente no tempo de execução). A melhor solução para mim foi reiniciar completamente o aplicativo.
Então, eu tentei muitas soluções e nenhuma delas funcionou para mim, mas isso:
Esperando que isso ajude alguém!
fonte
tente isto:
fonte
Com a biblioteca Process Phoenix . A atividade que você deseja relançar é denominada "A".
Sabor Java
Sabor Kotlin
fonte
Você pode reiniciar sua atividade atual assim:
Fragmento:
Atividade:
fonte