Como lidar com o código quando o aplicativo é encerrado passando o dedo no Android?

104

Se meu aplicativo estiver em execução e eu pressionar o botão home, o aplicativo entrará em segundo plano. Agora, se um toque longo do botão home e matar o aplicativo passando-o na lista de aplicações recentes, nenhum dos eventos gosta onPause(), onStop()ou onDestroy()é chamado sim o processo é encerrado. Portanto, se eu quiser que meus serviços parem, eliminem notificações e cancelem o registro de ouvintes, como posso fazer isso? Eu li alguns artigos e blogs, mas não obtive nenhuma informação útil e não encontrei nenhuma documentação sobre isso. Qualquer ajuda seria apreciada. Desde já, obrigado.

CodeWarrior
fonte
Eu resolvo meu problema. Verificar
MysticMagicϡ
@MysticMagic Isso funciona para um serviço, que tal cancelar notificações e cancelar o registro de ouvintes?
CodeWarrior de
Você pode cancelar o registro de ouvintes em onTaskRemoved. Você não pode? E o mesmo para notificações de cancelamento
MysticMagicϡ
@ MysticMagicϡ E se eu quiser fazer as mesmas coisas na atualização do aplicativo?
reji

Respostas:

150

Acabei de resolver um problema semelhante.

Aqui está o que você pode fazer se for apenas interromper o serviço quando o aplicativo for encerrado, deslizando da lista de aplicativos recentes.

Dentro de seu arquivo de manifesto, mantenha a bandeira stopWithTaskcomo truepara serviço. Gostar:

<service
    android:name="com.myapp.MyService"
    android:stopWithTask="true" />

Mas como você diz que deseja cancelar o registro de ouvintes e interromper a notificação, etc., sugiro esta abordagem:

  1. Dentro de seu arquivo de manifesto, mantenha a bandeira stopWithTaskcomo falsepara serviço. Gostar:

    <service
        android:name="com.myapp.MyService"
        android:stopWithTask="false" />
  2. Agora a seu MyServiceserviço, substitua o método onTaskRemoved. (Isso será disparado apenas se stopWithTaskestiver definido como false).

    public void onTaskRemoved(Intent rootIntent) {
    
        //unregister listeners
        //do any other cleanup if required
    
        //stop service
        stopSelf();  
    }

Consulte minha pergunta para obter mais detalhes, que também contém outra parte do código.

Espero que isto ajude.

MysticMagicϡ
fonte
Obrigado. Pensar se esse método poderia ser usado para identificar mortes por furto. Serviço em branco simples?
Kiran
Sim @Kiran, você pode tentar. :)
MysticMagicϡ
1
@ MysticMagicϡ oi, uma pergunta, tentei usar sua solução, mas parece que operações longas, como enviar dados para o servidor, análises para o Google, etc., às vezes não chegam ao fim. Será que o processo foi eliminado antes que esse método tivesse a chance de se completar totalmente? Você também experimentou esse método em um dispositivo Huawei? Parece que onTaskRemoved nunca é chamado nesses dispositivos. Alguma ideia por quê?
Alon Minski
2
@ MysticMagicϡ public void onTaskRemoved (..) Método não chamar no Android O. Funcionando bem em outro sistema operacional, mas tendo problemas no Android O.
Jay Patel
2
Este código não está funcionando para o Android O devido à limitação do serviço em segundo plano. Existe alguma maneira de lidar com isso em todos os dispositivos Android?
Aanal Mehta
33

Encontrei uma maneira de fazer isso

faça um serviço como este

public class OnClearFromRecentService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("ClearFromRecentService", "Service Started");
    return START_NOT_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d("ClearFromRecentService", "Service Destroyed");
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.e("ClearFromRecentService", "END");
    //Code here
    stopSelf();
}

}

2) registrar este serviço em manifest.xml

<service android:name="com.example.OnClearFromRecentService" android:stopWithTask="false" />

3) Em seguida, inicie este serviço em sua atividade inicial

startService(new Intent(getBaseContext(), OnClearFromRecentService.class));

E agora, sempre que você limpar seu aplicativo do Android recente, este método onTaskRemoved () será executado.

emilpmp
fonte
1
Copie e cole individualmente desta resposta: stackoverflow.com/questions/21040339
Flot2011
17

Resolvi problema semelhante. Se desejar que, após deslizar da tarefa recente e na próxima inicialização, ela se comporte corretamente, siga as etapas abaixo: -

1) Salve o ID do processo na preferência compartilhada:

SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());

2) Quando o aplicativo é iniciado a partir do iniciador após limpar a partir da tarefa recente, faça:

int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);

int currentProcessID = android.os.Process.myPid();

if ((previousProcessID == currentProcessID)) {
    // This ensures application not killed yet either by clearing recent or anyway
} else {
    // This ensures application killed either by clearing recent or by anyother means
}
Anshul Agarwal
fonte
Confirmado - usado no Android 8 sem service.onTaskRemoved () - muito bem feito
Gal Rom
4
Esta solução detecta apenas se o aplicativo foi reiniciado, mas você não pode detectar o momento exato em que o aplicativo foi encerrado
Vadim Kotov
6

Quando você pressiona home - onPausee onStopsua atividade está sendo chamada, então, neste momento, você deve fazer todas as economias e limpar, porque a plataforma Android não garante mais que onDestroyou qualquer outro método de ciclo de vida seria invocado, então o processo poderia ser encerrado sem qualquer notificação.

Deserto
fonte
12
Obrigado pela sugestão, mas eu não quero encerrar minhas notificações, serviços e ouvintes, onPause()e onstop()eles precisam ser encerrados apenas quando o usuário sair do aplicativo, ou seja, efetuar logout.
CodeWarrior
1

Você precisa salvar seus dados quando on onPause()é chamado. Veja este diagrama de ciclo de vida: Desenvolvedor Android

Você pode ver que um aplicativo pode ser encerrado após onPause()ou onStop().

Manuseie seus dados lá e recupere-os em onRestart()\ onCreate().

boa sorte!

Fashizel
fonte
1

Você não pode lidar com o deslize, porque o sistema apenas remove seu processo da memória sem chamar nenhum retorno de chamada.

Eu verifiquei que antes de o usuário chamar a tela de "aplicativos recentes", onPause () será sempre chamado. Portanto, você precisa salvar todos os dados no método onPause sem marcar isFinishing ().

Para verificar o botão Voltar, use o método onBackPressed.

Ahmad
fonte
1
Acredito que este seja o único método seguro para todas as versões do Android.
Sergio
@Sergio sim, é o método mais seguro e testei em um aplicativo em maior escala.
Ahmad
1

ViewModel.onCleared () pode ser útil, se o objetivo for liberar algum recurso (talvez um sistema em execução em algum outro lugar da rede) quando o usuário executa uma saída surpresa deslizando o dedo, em vez de pressionar o botão ou "parar". [Foi assim que cheguei originalmente a esta questão].

O aplicativo não recebe uma notificação e Activity.onDestroy () é chamado para mudanças de configuração, como mudanças na orientação, então a resposta não está lá. Mas ViewModel.onCleared é chamado quando o aplicativo é retirado (bem como quando o usuário sai da atividade). Se o recurso que você deseja usar está associado a mais de uma atividade na pilha, você pode adicionar contagens de referência ou algum outro mecanismo para decidir se ViewModel.onClear deve liberar o recurso.

Este é mais um dos muitos bons motivos para usar ViewModel.

- Bob

Bob Cram
fonte
1

Eu realmente não sei por que as abordagens acima não estão funcionando no meu caso, mesmo eu definindo android:stopWithTask="false"que onTaskRemoved()não chamado.

Outra boa abordagem seria usar AndroidViewModel. Este até funciona no caso em que o usuário sai do aplicativo pressionando o botão Voltar.

Apenas vincule a ViewModelclasse ao seu MainActivitye faça o onCleared()callback da tarefa .

Exemplo:

public class MainViewModel extends AndroidViewModel {
    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        // Do your task here
        Log.e("MainViewModel", "OnCleared mainViewModel");
        super.onCleared();
    }
}

em seguida, vincule-o à sua MainActivity:

MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);

~ Voila!

droidhs
fonte
pode não estar funcionando com a abordagem de serviço por causa do Android O e acima, conforme mencionado por outros comentadores. Esta parece ser uma solução interessante para utilizar modelos de visualização para isso. Isso ainda é acionado se o aplicativo for encerrado à força (deslizado na tela do alternador de aplicativos)? editar: a resposta de Bob sugere que funciona nesses casos
William Reed
sim, funciona, esse é o caso que encontrei com
droidhs
0

Isso funcionou para mim no Android 6,7,8,9.

Faça um serviço como este:

 public class OnClearFromRecentService extends Service {

 @Override public IBinder onBind(Intent intent) {
     return null; }

 @Override public int onStartCommand(Intent intent, int flags, int
 startId) {
     Log.d("ClearFromRecentService", "Service Started");
     return START_NOT_STICKY; }

 @Override public void onDestroy() {
     super.onDestroy();
     Log.d("ClearFromRecentService", "Service Destroyed"); }

 @Override public void onTaskRemoved(Intent rootIntent) {
     Log.e("ClearFromRecentService", "END");
     //Code here
     stopSelf(); } }

2) Registre este serviço em manifest.xml:

<service android:name="com.example.OnClearFromRecentService"
 android:stopWithTask="false" />

3) Em seguida, inicie este serviço em sua atividade inicial

 startService(new Intent(getBaseContext(),
 OnClearFromRecentService.class));
Alexis Osorio
fonte
1
E por que seu serviço continuará em execução no Android 8 e superior? Você percebe que há restrição para os serviços iniciados após o Android 8, certo?
rahil008
0

Como Bob Cram mencionou em sua resposta, o método onCleared () do View Model é a resposta. Funciona em ambos os casos:

  1. Quando o usuário remove o aplicativo deslizando-o do fundo.
  2. Quando o usuário limpa todo o aplicativo usando o botão Limpar lista.

O onTaskRemoved () do serviço funcionará quando o usuário deslizar o dedo sobre o aplicativo em segundo plano, mas não funcionará quando os aplicativos forem limpos usando o botão matar todos.

Mas o método onCleared () do viewModel funciona em ambos os casos. Você pode usar if para interromper qualquer processo em andamento ou limpar qualquer tarefa no servidor remoto.

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
Utkarsh Singh
fonte