Como simular o Android matando meu processo

174

O Android interromperá um processo se estiver em segundo plano e o sistema operacional decidir que precisa dos recursos (RAM, CPU, etc.). Eu preciso ser capaz de simular esse comportamento durante o teste para garantir que meu aplicativo esteja se comportando corretamente. Quero poder fazer isso de maneira automatizada, para poder testar se o aplicativo se comporta corretamente sempre que isso acontece, o que significa que terei que testá-lo em todas as atividades, etc.

Eu sei como matar meu processo. Esse não é o problema. O problema é que quando eu matar meu processo (usando DDMS, adb shell kill, Process.killProcess(), etc.) Android não reiniciá-lo da mesma forma que seria se o sistema operacional Android tinha matado ele próprio.

Se o sistema operacional Android concluir o processo (devido a requisitos de recursos), quando o usuário retornar ao aplicativo, o Android recriará o processo e, em seguida, recriará a atividade principal na pilha de atividades (chamada onCreate()).

Por outro lado, se eu matar o processo, o Android assume que a atividade na parte superior da pilha de atividades foi mal comportada ; portanto, ela recria automaticamente o processo e remove a atividade principal da pilha de atividades e recria a atividade que estava por baixo a atividade principal (chamando onCreate () `). Este não é o comportamento que eu quero. Eu quero o mesmo comportamento de quando o Android mata o processo.

Apenas para explicar pictoricamente, se minha pilha de atividades estiver assim:

    ActivityA -> ActivityB -> ActivityC -> ActivityD

Se o Android matar o processo e o usuário retornar ao aplicativo, o Android recria o processo e cria o ActivityD.

Se eu matar o processo, o Android recria o processo e cria o ActivityC.

David Wasser
fonte
4
Você não poderia apenas criar a quantidade de processos necessários para matar o seu em segundo plano?
Alex W
2
@Ang Eu acho que você está perdendo o ponto. Eu sei como detectar que o Android matou o processo. Eu tenho um código que lida com essas condições. O que eu quero fazer é testar adequadamente (e de maneira automatizada) esse código . Para fazer isso, preciso de alguma maneira de provocar o Android para matar meu processo exatamente da mesma maneira que normalmente faria sob pressão de recursos. A questão vinculada, embora interessante, não agrega valor aqui.
precisa
@IgorGanapolsky Obrigado pelos links, mas na verdade nenhum desses links tem soluções para o problema.
David Wasser

Respostas:

127

A melhor maneira de testar isso para mim foi fazer o seguinte:

  • Abra ActivityD no seu aplicativo
  • Pressione o botão Início
  • Pressione Terminate Applicationna janela Logcat no Android Studio (isso interromperá o processo do aplicativo, selecione o dispositivo e o processo nas listas suspensas do Logcat na parte superior)
  • Volte ao aplicativo pressionando a tecla Início ou aplicativos abertos (depende do dispositivo)
  • O aplicativo iniciará no ActivityD recriado (ActivityA, ActivityB, ActivityC estará morto e será recriado quando você voltar a eles)

Em alguns dispositivos, você também pode voltar ao aplicativo (ActivityD) com Aplicativos -> ícone do iniciador, mas em outros dispositivos, ele iniciará o ActivityA.

Isto é o que os documentos do Android estão dizendo sobre isso:

Normalmente, o sistema limpa uma tarefa (remove todas as atividades da pilha acima da atividade raiz) em determinadas situações quando o usuário seleciona novamente a tarefa na tela inicial. Normalmente, isso é feito se o usuário não tiver visitado a tarefa por um determinado período de tempo, como 30 minutos.

Marca
fonte
2
Obrigado pela sua resposta, mas não é útil para mim porque preciso de uma maneira automatizada de fazer isso, para testes automatizados.
precisa saber é o seguinte
1
Isso foi realmente útil para mim.
John Roberts
6
Isso não é automatizado, mas funcionou perfeitamente para meus propósitos. É muito importante não pular a etapa 2 . Você deve enviar o aplicativo para o plano de fundo antes de interromper o processo no DDMS para que isso funcione. Para aqueles que se perguntam de onde vieram as citações do documento, está aqui . Embora eu não tenha certeza de que eles estão realmente relacionados ao tópico, pois trata-se da <activity>tag no manifesto.
Tony Chan
1
Exatamente o que eu precisava. Obrigado. Para aqueles que não sabem, o DDMS está no Eclipse, vá para Janela -> Abrir Perspectiva e você deve encontrá-lo lá.
Richard Richard
4
Como alternativa, você pode ir para "Opções de desenvolvimento" e definir o limite de segundo plano como "sem processos em segundo plano", e toda vez que pressionar o botão inicial, o processo morrerá.
João Gavazzi
56

Isso parece funcionar para mim:

adb shell am kill <package_name>

Isso é diferente do adb shell killmencionado pelo OP.

Observe que a ajuda para o am killcomando diz:

am kill: Kill all processes associated with <PACKAGE>.  Only kills.
  processes that are safe to kill -- that is, will not impact the user
  experience.

Portanto, não matará o processo se estiver em primeiro plano. Isso parece funcionar como o OP queria, pois se eu sair do meu aplicativo, executá- adb shell am kill <package_name>lo matará o aplicativo (confirmei isso usando pso dispositivo). Então, se eu voltar ao aplicativo, volto à atividade em que estava anteriormente - ou seja, no exemplo do OP, o processo é recriado e cria ActivityD (em vez de ActivityC como a maioria dos outros métodos de matar parece desencadear).

Desculpe, estou dois anos atrasado para o OP, mas espero que outros achem isso útil.

HexAndBugs
fonte
Obrigado! era isso que eu estava procurando! Quero especificar que esse comando se comporte de maneira diferente adb shell am force-stop. O último também removerá qualquer Intendente pendente relacionado ao seu aplicativo (como Notificações), enquanto o primeiro não.
bonnyz
Aponta para quem investiga o código fonte do SO para ver qual código ele executa quando mata um processo para recuperar memória - minha aposta é que é o mesmo código am kill.
Androidguy
não funciona no Android 7.1 Samsung J5. psmostra meu aplicativo
Valgaal 13/02/19
17

Outro método, provavelmente um que pode ser script, pois não requer DDMS:

Configuração única: vá para Opções do desenvolvedor, selecione Configuração do limite do processo em segundo plano, altere o valor de 'Limite padrão' para 'Nenhum processo em segundo plano'.

Quando você precisar reiniciar o processo, pressione o botão home. O processo será interrompido (você pode verificar no logcat / Android Monitor no studio - o processo será marcado como [MORTO]). Em seguida, volte ao aplicativo usando o alternador de tarefas.

Merk
fonte
2
Interessante. Eu acho que isso não afeta os Foreground Services, certo?
IgorGanapolsky
Preciso fazer isso em dispositivos reais com Android tão antigo quanto 2.3.3, portanto, isso não ajuda em nada.
David Wasser
13

Esta pergunta é antiga, mas existe uma resposta para essa pergunta que não requer adb, Android Studio etc. O único requisito é a API 23 ou mais recente.

Para simular a reinicialização do aplicativo pelo SO, vá para as configurações do aplicativo enquanto o aplicativo estiver em execução, desative (então você pode habilitar) uma permissão e retorne o aplicativo a partir de aplicativos recentes. Quando a permissão está desativada, o sistema operacional mata o aplicativo, mas mantém os estados da instância salvos. Quando o usuário retorna o aplicativo, ele e a última atividade (com estado salvo) são recriados.

O método 'Nenhum processo em segundo plano' às vezes causa o mesmo comportamento, mas nem sempre. Por exemplo, se o aplicativo estiver executando um serviço em segundo plano, "Nenhum processo em segundo plano" não fará nada. Mas o aplicativo pode ser morto pelo sistema, incluindo seus serviços. O método de permissão funciona mesmo se o aplicativo tiver um serviço.

Exemplo:

Nosso aplicativo tem duas atividades. ActivityA é a atividade principal iniciada no iniciador. ActivityB é iniciado a partir de ActivityA. Mostrarei apenas os métodos onCreate, onStart, onStop, onDestroy. O Android chama onSaveInstanceState sempre antes de chamar onStop, porque uma atividade que está no estado de parada pode ser eliminada pelo sistema. [ https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle]

Método de permissão:

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop (the order is like this, it is stopped after new one is started)
<go settings>
ActivityB onStop
<disable a permission>
//Application is killed, but onDestroy methods are not called.
//Android does not call onDestroy methods if app will be killed.
<return app by recent apps>
Application onCreate (this is the important part. All static variables are reset.)
ActivityB onCreate WITH savedInstance (user does not notice activity is recreated)
//Note that ActivityA is not created yet, do not try to access it.
ActivityB onStart
<return ActivityA by back>
ActivityA onCreate WITH savedInstance (user does not notice activity is recreated)
ActivityA onStart
ActivityB onStop
ActivityB onDestroy
<press back again, return launcher>
ActivityA onStop
ActivityA onDestroy
<open app again>
//does not call Application onCreate, app was not killed
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart

Eu quero comparar outros métodos mencionados nas outras respostas.

Não mantenha atividades: isso não mata o aplicativo.

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop
ActivityA onDestroy (do not keep)
<return launcher by home button>
ActivityB onStop
ActivityB onDestroy (do not keep) 
<retun app from recent apps>
// NO Application onCreate
ActivityB onCreate WITH savedInstance (user does not notice activity recreated)
ActivityB onStart
<return ActivityA by back>
ActivityA onCreate WITH savedInstance (user does not notice activity recreated)
ActivityA onStart
ActivityB onStop
ActivityB onDestroy
<press back again, return launcher>
ActivityA onStop
ActivityA onDestroy
<open app again>
//does not call Application onCreate, app was not killed
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart

Forçar método de parada: Não armazena estados de instância salvos

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop
<go settings>
ActivityB onStop
<force stop, return app from recent apps>
Application onCreate
ActivityA onCreate WITHOUT savedInstance 
//This is important part, app is destroyed by user.
//Root activity of the task is started, not the top activity.
//Also there is no savedInstance.
fthdgn
fonte
~ "o aplicativo pode ser morto pelo sistema, incluindo seu serviço ". Não Serviço Primeiro Plano ...
IgorGanapolsky
@DavidWasser Leia as especificações! developer.android.com/guide/components/services.html#Foreground Um serviço em primeiro plano é um serviço que o usuário conhece ativamente e não é candidato ao sistema para eliminar quando estiver com pouca memória.
IgorGanapolsky
3
@IgorGanapolsky A documentação é boa, especialmente se estiver completa e correta (o que infelizmente não é), mas geralmente confio mais em observações pessoais reais. Eu já vi um primeiro plano Servicemorto várias vezes. Mesmo se o sistema não estiver com pouca memória. A maioria dos fabricantes de dispositivos criou suas próprias "otimizações" e "melhorias" no sistema operacional Android para economizar energia da bateria. Muitos dispositivos têm "assassinos" muito mais agressivos do que o Android padrão.
David Wasser
@DavidWasser Fair observação. Então, depois de todos esses anos, você encontrou uma solução?
IgorGanapolsky
@IgorGanapolsky Não. Não encontrei solução para este problema. É por isso que a questão ainda está em aberto.
David Wasser
7

Estou muito atrasado para a festa e vários antes de mim deram a mesma resposta correta, mas para simplificar para quem vem depois de mim, basta pressionar o botão home e executar este comando:

adb shell ps | grep <package name> | awk '{print $2}' | xargs adb shell run-as <package name again> kill

O aplicativo não perde o estado e, por minha própria experiência, isso funciona da mesma maneira que o sistema operacional matou o aplicativo em segundo plano. Isso funciona apenas para aplicativos de depuração criados

Hirschen
fonte
Eu recebo'grep' is not recognized as an internal or external command, operable program or batch file.
Dale
Mas eu fiz isso no shell run-as <package name> kill 7379, mas me colocou na atividade anterior, não na atividade em que estava quando pressionei o botão home.
Dale
6

É assim que você faz no Android Studio.

  1. Tenha seu dispositivo no modo de depuração conectado ao seu computador.
  2. Abra o aplicativo no seu dispositivo e vá para a atividade em que você deseja testar o "Retorno a ele da morte".
  3. Pressione o botão Início no seu dispositivo.
  4. No Android Studio, acesse Android Monitor -> Monitors e pressione o ícone Terminate Application.
  5. Agora você pode voltar ao seu aplicativo através dos aplicativos recentes ou clicando no ícone do iniciador, o comportamento foi o mesmo nos meus testes.
dbar
fonte
2
Isso não ajuda em nada. Eu preciso fazer isso programaticamente, dentro de um conjunto de testes. Mas obrigada mesmo assim.
David Wasser
Além disso, esta resposta é praticamente a mesma que a resposta de Mark.
David Wasser
Alguma idéia de como fazer isso via UIAutomator ou Espresso?
IgorGanapolsky
Não consigo encontrar Android Monitor - Monitors. Deve ser algo de que eles se livraram. Estou no v 3.2.1
Dale
1
O @Dale Android Device Monitor foi descontinuado no Android Studio 3.1 e removido do Android Studio 3.2.
Valgaal 13/02/19
5

Coloque o aplicativo em segundo plano com o botão HOME

Selecione seu processo no modo "Logcat" no Android Studio e clique em Finalizar aplicativo no canto inferior esquerdo

botão terminar

Agora inicie seu aplicativo a partir do iniciador no dispositivo Android


EDIT: De acordo com a internet, o seguinte também funciona:

 adb shell am kill [my-package-name]

EDITAR no futuro: algo a ser observado, houve uma alteração no Android Studio 4.0, se você usar Rundo AS, Terminateemitirá a Force Stop.

No entanto, se você iniciar a partir do iniciador posteriormente, e então tentar simulá-lo dessa maneira, obterá os resultados desejados (comportamento de pouca memória).

EpicPandaForce
fonte
Esta é uma resposta duplicada
Onik
@Onik Todos os outros têm um monte de penugem desnecessário como ddms e o que seja. Embora tecnicamente sim, o stackoverflow.com/a/41975750/2413303 diz a mesma coisa. Talvez eu deva adicionar fotos.
EpicPandaForce
Isso não ajuda. Eu tenho um equipamento de teste e não posso executar essas ações no equipamento. Além disso, o comportamento não é o mesmo que acontece quando o Android mata o processo.
David Wasser
the behaviour is not the same as what happens when Android kills the processsim, é
EpicPandaForce
Esse processo mostra a atividade anterior, não a atividade de quando o botão home é pressionado.
Dale
2

Você pode executar as próximas etapas para reproduzir o comportamento procurado:

  1. Abra seu aplicativo, navegue até a atividade principal
  2. Use o painel de notificação para navegar para qualquer outro aplicativo de tela cheia (por exemplo, para configurações do sistema - no canto superior direito)
  3. Mate seu processo de inscrição
  4. Pressione o botão Voltar
Igor Kostomin
fonte
1
Obrigado pela sua resposta, mas não é útil para mim porque preciso de uma maneira automatizada de fazer isso, para testes automatizados.
David Wasser
Portanto, este é o único método que eu encontrei que simula a limpeza da memória do Android e acaba com seu aplicativo (meu nível de API é 19 e, portanto, não posso usar o comando send-trim-memory). Outros comandos, como adb shell e force-stop com.my.app.package ou kill, não reproduzem o mesmo processo exato que, seguindo o procedimento acima!
Mark Garcia
2

Nas opções do desenvolvedor em Configurações, selecione 'Não manter atividades', que destruirá as atividades assim que você as afastar.

Nota: De acordo com um comentário útil abaixo, use-o somente se você não se importar com a remoção de valores estáticos.

MSpeed
fonte
É efetivamente o mesmo que a solução já postada - e rejeitada - há um ano. A única diferença parece ser o aplicativo usado para configurá-lo em um emulador, em comparação com os telefones mais recentes que o suportam.
Chris Stratton
1
Desculpe, como Chris Stratton diz, essa é praticamente a mesma sugestão que a outra resposta. Não se trata de atividades de finalização do Android. Trata-se do Android matar todo o processo (o que ocorre com bastante regularidade e eficácia, especialmente em dispositivos HTC executando o Android 4.x).
David Wasser
6
Este é quase bom, mas não mata o processo, apenas destrói a atividade. O que isso significa? Sua atividade será aberta com savedInstanceState, mas todas as variáveis ​​estáticas ainda estão em processo. Após o processo, todas as variáveis ​​estáticas também são limpas.
Mark
1

Pressione o botão Início e coloque o aplicativo em segundo plano primeiro. Em seguida, pare ou interrompa o processo do DDMS ou ADB.

Monstieur
fonte
Obrigado pela sua resposta, mas não é útil para mim porque preciso de uma maneira automatizada de fazer isso, para testes automatizados.
David Wasser
O Android nunca interromperá seu processo se sua atividade estiver em primeiro plano. Você está tentando testar uma condição que nunca ocorrerá. Se sua atividade estiver em segundo plano, seu estado já foi salvo e não há diferença entre você matá-lo manualmente e o Android matá-lo com pouca memória, ou seja, o processo acabou; não há nada de especial em uma morte com pouca memória. Quando a memória estiver disponível novamente, seus serviços persistentes serão reiniciados (exceto no 4.4) e quando você tocar no ícone ou na tarefa recente, a pilha e o estado da atividade serão restaurados.
Monstieur
3
No seu exemplo, ele voltará à Atividade C porque você interrompeu o processo enquanto a Atividade D estava visível na tela (isso nunca ocorrerá mesmo com pouca memória) e o estado da Atividade C foi salvo, pois está em segundo plano. Seu processo será morto apenas se não estiver em primeiro plano, ou seja, o estado da Atividade D teria sido salvo quando foi para o segundo plano e, portanto, será restaurado mesmo que seu processo seja morto. Para realizar seus testes em cada atividade, você PRECISA enviar o aplicativo em segundo plano antes de matá-lo.
Monstieur
O que você quis dizer com ~ " e colocou o aplicativo em segundo plano primeiro "?
IgorGanapolsky
1

Você também pode conectar-se ao seu dispositivo / emulador a partir do terminal com adb shell, em seguida, obter ps | grep <your_package_namee executar o PID do seu processo kill -9 <pid>. Em seguida, abra seu aplicativo minimizado no seletor de aplicativos recentes e ele reiniciará a última atividade

qbasso
fonte
É o mesmo que o Android que mata um processo em condições de pouca memória? Acho OP queria especificamente que ...
IgorGanapolsky
1
Teoricamente, o @IgorGanapolsky é, embora você não possa fazer isso em um dispositivo real se não estiver enraizado.
Fran Marzoa
0

A raiz do seu problema parece ser que você Activityestá em primeiro plano quando você mata o processo.

Você pode observar isso pressionando stop no DDMS quando Activityestiver visível (acontece exatamente o que você descreve) e comparando com pressionando stop depois de casa e depois retornando ao aplicativo.

Apenas certifique-se de moveTaskToBack(true)alguma forma em seus testes.

MaciejGórski
fonte
0

Não sei se essa é a resposta que você está procurando, é mais como um pensamento lógico.

Eu não acho que você possa realmente fazer um teste totalmente automatizado, a única maneira de simulá-lo, será recriá-lo, o AKA tem tantas atividades que o Android matará seu aplicativo.

Portanto, minha ideia ou sugestão é criar outro aplicativo pequeno, que continue aparecendo novas atividades, até o Android ficar sem memória e começar a matar o processo em segundo plano.

Algo na linha:

Iniciar atividade i -> Verifique o processo em execução se o aplicativo estiver na lista, incremente i e reinicie o loop sem fechar a atividade atual, caso contrário -> diminua ie feche a atividade atual, volte para a anterior e verifique novamente ...

Emil Borconi
fonte
0

Quando o processo do aplicativo morre, o Android passa pelos registros de atividades (as entradas representam atividades na pilha do histórico) e decide quais manter no histórico e quais remover dele.

Um dos pontos principais aqui é o ActivityRecordcampo chamado haveState, que os engenheiros do Android Framework descrevem como "chegamos ao último estado de atividade?".

Por padrão, o Android considera que a atividade tem um estado. A atividade se torna sem estado quando o aplicativo relata ao serviço do gerenciador de tarefas que a atividade foi retomada e isso é válida até que o aplicativo notifique a estrutura de que a atividade entrou no estado Parado. Em palavras simples, o haveStatevalor falseentre atividade onResume()é chamado eonStop() ou onSaveInstanceState()é chamado, dependendo da versão de destino do aplicativo.

Se eu matar o processo, o Android recria o processo e cria o ActivityC.

Nesse caso, ActivityD não possui android:stateNotNeeded="true" atributo no manifesto do aplicativo e está em execução no primeiro plano; portanto, o Android o remove do histórico, pois o sistema não obteve seu último estado.

Como simular o Android matando meu processo

Como foi mencionado várias vezes, você pode simplesmente mover o aplicativo para o segundo plano, para que a atividade principal na pilha traseira da atividade salve seu estado e, depois disso, você pode eliminar o processo do aplicativo através do Android Debug Bridge, Android Studio ou usando o Propriedade Limite de Processos em Segundo Plano nas Opções do Desenvolvedor. Depois disso, sua atividade recente será recriada com sucesso.

Apesar disso, há também outra maneira simples de testar o cenário de morte do processo de aplicativo. Sabendo tudo o que foi descrito acima e o fato de que, se você iniciar a nova ActivityE a partir da ActivityD atualmente em execução, o onStop()retorno de chamada da ActivityD será invocado somente após o onResume()método ActivityE , você poderá executar o seguinte truque.

class TerminatorActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val isPrePie = applicationInfo.targetSdkVersion < Build.VERSION_CODES.P
        val callbacks = TerminatorLifecycleCallbacks(isPrePie)
        (applicationContext as Application).registerActivityLifecycleCallbacks(callbacks)
    }

    private class TerminatorLifecycleCallbacks(
        // Before P onSaveInstanceState() was called before onStop(), starting with P it's
        // called after
        // Used to schedule the death as app reports server that activity has stopped
        // after the latest of these was invoked
        private val isPrePie: Boolean
    ) : ActivityLifecycleCallbacksDefault {

        private val handler = Handler(Looper.getMainLooper())

        override fun onActivityPostStopped(activity: Activity) {
            if (isPrePie) {
                terminate()
            }
        }

        override fun onActivityPostSaveInstanceState(activity: Activity, outState: Bundle) {
            if (!isPrePie) {
                terminate()
            }
        }

        fun terminate() {
            handler.postDelayed(
                {
                    Process.killProcess(Process.myPid()) // This is the end... 
                },
                LAST_MILLIS
            )
        }

        companion object {
            // Let's wait for a while, so app can report and server can handle the update
            const val LAST_MILLIS = 100L
        }

    }

    private interface ActivityLifecycleCallbacksDefault : Application.ActivityLifecycleCallbacks {
        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
        override fun onActivityStarted(activity: Activity) {}
        override fun onActivityResumed(activity: Activity) {}
        override fun onActivityPaused(activity: Activity) {}
        override fun onActivityStopped(activity: Activity) {}
        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
        override fun onActivityDestroyed(activity: Activity) {}
    }
}

Em seguida, basta iniciar TerminatorActivityquando você deseja matar o aplicativo.

No final, existe uma ferramenta leve que simplifica o teste da morte do processo de aplicação, chamada Venom .

ivkil
fonte