Estou tentando escrever um aplicativo que faz algo específico quando é trazido de volta ao primeiro plano após algum tempo. Existe uma maneira de detectar quando um aplicativo é enviado para segundo plano ou trazido para o primeiro plano?
android
background
foreground
iHorse
fonte
fonte
Respostas:
Os métodos
onPause()
eonResume()
são chamados quando o aplicativo é trazido para segundo plano e novamente para o primeiro plano. No entanto, eles também são chamados quando o aplicativo é iniciado pela primeira vez e antes de ser morto. Você pode ler mais em Atividade .Não existe uma abordagem direta para obter o status do aplicativo em segundo plano ou em primeiro plano, mas mesmo eu já enfrentei esse problema e encontrei a solução com
onWindowFocusChanged
eonStop
.Para obter mais detalhes, consulte aqui Android: solução para detectar quando um aplicativo Android entra em segundo plano e volta ao primeiro plano sem getRunningTasks ou getRunningAppProcesses .
fonte
2018: o Android suporta isso nativamente por meio de componentes do ciclo de vida.
Março de 2018 ATUALIZAÇÃO : Agora existe uma solução melhor. Consulte ProcessLifecycleOwner . Você precisará usar os novos componentes da arquitetura 1.1.0 (mais recentes no momento), mas ele foi projetado especificamente para isso.
Há uma amostra simples fornecida nesta resposta, mas eu escrevi uma amostra de aplicativo e uma postagem no blog sobre ele.
Desde que escrevi isso em 2014, surgiram diferentes soluções. Alguns funcionaram, outros foram pensados para estar funcionando , mas tinham falhas (incluindo a minha!) E nós, como comunidade (Android), aprendemos a conviver com as consequências e escrevemos soluções alternativas para os casos especiais.
Nunca assuma que um único trecho de código é a solução que você está procurando; é improvável que seja o caso; melhor ainda, tente entender o que faz e por que faz.
A
MemoryBoss
aula nunca foi realmente usada por mim, como foi escrita aqui, era apenas um pedaço de pseudo-código que funcionava.A menos que haja uma razão válida para você não usar os novos componentes da arquitetura (e existem alguns, especialmente se você direcionar APIs super antigas), vá em frente e use-as. Eles estão longe de serem perfeitos, mas nem eram
ComponentCallbacks2
.ATUALIZAÇÃO / NOTAS (novembro de 2015) : As pessoas têm feito dois comentários, o primeiro é que
>=
deve ser usado em vez de==
porque a documentação afirma que você não deve verificar os valores exatos . Isso é bom para a maioria dos casos, mas tenha em mente que se você única preocupam com fazer algo quando o aplicativo foi para o fundo, você terá que usar == e também combiná-lo com outra solução (como chamadas de retorno Atividade do ciclo de vida), ou você pode não obter o efeito desejado. O exemplo (e isso aconteceu comigo) é que, se você deseja bloquearseu aplicativo com uma tela de senha quando estiver em segundo plano (como 1Password, se você estiver familiarizado com ele), poderá bloquear acidentalmente seu aplicativo se estiver com pouca memória e estiver testando repentinamente>= TRIM_MEMORY
, porque o Android acionará umaLOW MEMORY
chamada e isso é mais alto que o seu. Portanto, tenha cuidado como / o que você testa.Além disso, algumas pessoas perguntaram sobre como detectar quando você volta.
A maneira mais simples de pensar é explicada abaixo, mas como algumas pessoas não estão familiarizadas com isso, estou adicionando algum pseudo-código aqui. Supondo que você tenha
YourApplication
e asMemoryBoss
classes no seuclass BaseActivity extends Activity
(você precisará criar uma se não tiver uma).Eu recomendo o onStart porque o Dialogs pode pausar uma atividade, então aposto que você não quer que seu aplicativo pense "foi para segundo plano" se tudo o que você fez foi exibir uma caixa de diálogo em tela cheia, mas sua milhagem pode variar.
E isso é tudo. O código no bloco if vai executados apenas uma vez , mesmo que você vá para outra atividade, o novo (que também
extends BaseActivity
) irá relatarwasInBackground
éfalse
que ele não vai executar o código, até queonMemoryTrimmed
é chamado ea bandeira é definido como verdadeiro novamente .Espero que ajude.
ATUALIZAÇÃO / NOTAS (abril de 2015) : Antes de você copiar e colar este código, observe que encontrei algumas instâncias em que ele pode não ser 100% confiável e deve ser combinado com outros métodos para obter os melhores resultados. Notavelmente, existem duas instâncias conhecidas em que
onTrimMemory
não é garantido que a chamada de retorno seja executada:Se o telefone bloquear a tela enquanto o aplicativo estiver visível (digamos que o dispositivo bloqueie após nn minutos), esse retorno de chamada não será chamado (ou nem sempre) porque a tela de bloqueio está na parte superior, mas o aplicativo ainda está "em execução", embora coberto.
Se o seu dispositivo estiver com pouca memória (e com pouca carga), o sistema operacional parece ignorar esta chamada e passar diretamente para os níveis mais críticos.
Agora, dependendo da importância de você saber quando o aplicativo foi para o segundo plano, você pode ou não precisar estender essa solução, além de acompanhar o ciclo de vida da atividade e outros enfeites.
Lembre-se do exposto acima e tenha uma boa equipe de controle de qualidade;)
FIM DA ATUALIZAÇÃO
Pode ser tarde, mas há um método confiável no Ice Cream Sandwich (API 14) e acima .
Acontece que quando o aplicativo não tem mais interface do usuário visível, um retorno de chamada é acionado. O retorno de chamada, que você pode implementar em uma classe personalizada, é chamado ComponentCallbacks2 (sim, com dois). Esse retorno de chamada está disponível apenas na API nível 14 (sanduíche de sorvete) e acima.
Você basicamente recebe uma chamada para o método:
O nível é 20 ou mais específico
Venho testando isso e sempre funciona, porque o nível 20 é apenas uma "sugestão" de que você pode querer liberar alguns recursos, pois seu aplicativo não está mais visível.
Para citar os documentos oficiais:
Obviamente, você deve implementar isso para realmente fazer o que diz (limpar a memória que não foi usada em um determinado período de tempo, limpar algumas coleções que não foram utilizadas, etc. As possibilidades são infinitas (consulte os documentos oficiais para obter outras informações mais detalhadas) níveis críticos ).
Mas o mais interessante é que o sistema operacional está lhe dizendo: Ei, seu aplicativo foi para o fundo!
Qual é exatamente o que você queria saber em primeiro lugar.
Como você determina quando voltou?
Bem, isso é fácil, tenho certeza que você tem uma "BaseActivity" para poder usar seu onResume () para sinalizar o fato de que você voltou. Porque o único momento em que você estará dizendo que não voltou é quando realmente recebe uma chamada para o
onTrimMemory
método acima .Funciona. Você não recebe falsos positivos. Se uma atividade está sendo retomada, você volta 100% das vezes. Se o usuário voltar atrás, você recebe outra
onTrimMemory()
chamada.Você precisa assinar suas atividades (ou, melhor ainda, uma classe personalizada).
A maneira mais fácil de garantir que você sempre receba isso é criar uma classe simples como esta:
Para usar isso, na implementação de seu aplicativo ( você tem um, certo? ), Faça algo como:
Se você criar um,
Interface
poderá adicionar umelse
a issoif
e implementarComponentCallbacks
(sem o 2) usado em qualquer coisa abaixo da API 14. Esse retorno de chamada possui apenas oonLowMemory()
método e não é chamado quando você passa para o segundo plano , mas deve usá-lo para aparar a memória .Agora inicie seu aplicativo e pressione home. Seu
onTrimMemory(final int level)
método deve ser chamado (dica: adicionar log).A última etapa é cancelar o registro do retorno de chamada. Provavelmente, o melhor lugar é o
onTerminate()
método do seu aplicativo, mas esse método não é chamado em um dispositivo real:Portanto, a menos que você realmente tenha uma situação em que não deseja mais se registrar, é possível ignorá-la com segurança, pois seu processo está acabando no nível do sistema operacional.
Se você decidir cancelar o registro em algum momento (se você, por exemplo, fornecer um mecanismo de desligamento para que seu aplicativo limpe e morra), você pode:
E é isso.
fonte
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
que evita o problema na sua atualização, ponto 2. Em relação ao ponto 1, não é uma preocupação para mim, já que o aplicativo realmente não foi para o segundo plano, então é assim que ele deve funcionar.Aqui está como eu consegui resolver isso. Ele trabalha com a premissa de que o uso de uma referência de tempo entre transições de atividade provavelmente fornecerá evidência adequada de que um aplicativo foi "em segundo plano" ou não.
Primeiro, usei uma instância android.app.Application (vamos chamar de MyApplication) que possui um Timer, um TimerTask, uma constante para representar o número máximo de milissegundos que a transição de uma atividade para outra poderia levar razoavelmente (eu fui com um valor de 2s) e um booleano para indicar se o aplicativo estava ou não "em segundo plano":
O aplicativo também fornece dois métodos para iniciar e parar o cronômetro / tarefa:
A última parte desta solução é adicionar uma chamada a cada um desses métodos a partir dos eventos onResume () e onPause () de todas as atividades ou, de preferência, em uma atividade base da qual todas as suas atividades concretas herdam:
Portanto, no caso em que o usuário está simplesmente navegando entre as atividades do seu aplicativo, a onPause () da atividade de partida inicia o cronômetro, mas quase imediatamente a nova atividade inserida cancela o cronômetro antes que ele atinja o tempo máximo de transição. E o wasInBackground também seria falso .
Por outro lado, quando uma Atividade chega em primeiro plano a partir do Iniciador, a ativação do dispositivo, a ligação telefônica final, etc., é mais provável que a tarefa do timer seja executada antes desse evento e, portanto, wasInBackground foi definido como verdadeiro .
fonte
Edit: os novos componentes da arquitetura trouxeram algo promissor: ProcessLifecycleOwner , veja a resposta de @ vokilam
A solução real de acordo com uma palestra do Google I / O :
Sim. Sei que é difícil acreditar que essa solução simples funcione, pois temos muitas soluções estranhas aqui.
Mas há esperança.
fonte
ProcessLifecycleOwner
parece ser uma solução promissora também.Uma implementação pode ser tão simples quanto
De acordo com o código fonte, o valor atual do atraso é
700ms
.O uso desse recurso também requer
dependencies
:fonte
implementation "android.arch.lifecycle:extensions:1.0.0"
eannotationProcessor "android.arch.lifecycle:compiler:1.0.0"
do repositório do Google (ou sejagoogle()
)Com base na resposta de Martín Marconcinis (obrigado!), Finalmente encontrei uma solução confiável (e muito simples).
Em seguida, adicione isso ao seu onCreate () da sua classe Application
fonte
Nós usamos esse método. Parece muito simples de trabalhar, mas foi bem testado em nosso aplicativo e, de fato, funciona surpreendentemente bem em todos os casos, incluindo ir para a tela inicial pelo botão "home", pelo botão "return" ou após o bloqueio da tela. De uma chance.
A ideia é que, quando em primeiro plano, o Android sempre inicia uma nova atividade antes de parar a anterior. Isso não é garantido, mas é assim que funciona. BTW, Flurry parece usar a mesma lógica (apenas um palpite, eu não verifiquei isso, mas é viciado nos mesmos eventos).
Editar: conforme os comentários, também mudamos para onStart () nas versões posteriores do código. Além disso, estou adicionando super chamadas, que estavam faltando na minha postagem inicial, porque isso era mais um conceito do que um código funcional.
fonte
onStop is called when the activity is no longer visible to the user
,.Se o seu aplicativo consistir em várias atividades e / ou atividades empilhadas, como um widget da barra de guias, substituir onPause () e onResume () não funcionará. Ou seja, ao iniciar uma nova atividade, as atividades atuais serão pausadas antes da criação da nova. O mesmo se aplica ao concluir (usando o botão "voltar") uma atividade.
Eu encontrei dois métodos que parecem funcionar como desejado.
O primeiro requer a permissão GET_TASKS e consiste em um método simples que verifica se a principal atividade em execução no dispositivo pertence ao aplicativo, comparando os nomes dos pacotes:
Este método foi encontrado na estrutura Droid-Fu (agora denominada Ignition).
O segundo método que eu implementei não requer a permissão GET_TASKS, o que é bom. Em vez disso, é um pouco mais complicado de implementar.
Na classe MainApplication, você tem uma variável que rastreia o número de atividades em execução no seu aplicativo. Em onResume () para cada atividade você aumenta a variável e em onPause () você a diminui.
Quando o número de atividades em execução atinge 0, o aplicativo é colocado em segundo plano se as seguintes condições forem verdadeiras:
Quando você pode detectar que o aplicativo renunciou ao plano de fundo, é fácil detectar também quando ele é trazido de volta ao primeiro plano.
fonte
getRunnintTasks()
Crie uma classe que se estenda
Application
. Então, nele podemos usar seu método de substituiçãoonTrimMemory()
,.Para detectar se o aplicativo foi para segundo plano, usaremos:
fonte
FragmentActivity
você também pode querer adicionarlevel == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
também.Considere usar onUserLeaveHint. Isso só será chamado quando o aplicativo entrar em segundo plano. O onPause terá casos de canto para lidar, pois pode ser chamado por outros motivos; por exemplo, se o usuário abrir outra atividade no seu aplicativo, como a página de configurações, o método onPause da sua atividade principal será chamado, mesmo que ele ainda esteja no seu aplicativo; rastrear o que está acontecendo levará a erros quando você pode simplesmente usar o retorno de chamada onUserLeaveHint, que faz o que você está pedindo.
Quando em UserLeaveHint é chamado, você pode definir um sinalizador inBackground booleano como true. Quando onResume for chamado, suponha que você voltou ao primeiro plano se o sinalizador inBackground estiver definido. Isso ocorre porque onResume também será chamado em sua atividade principal se o usuário estiver apenas no menu de configurações e nunca sair do aplicativo.
Lembre-se de que, se o usuário pressionar o botão home na tela de configurações, onUserLeaveHint será chamado em sua atividade de configurações e, quando retornar em Resume, será chamado em sua atividade de configurações. Se você tiver apenas esse código de detecção em sua atividade principal, perderá este caso de uso. Para ter esse código em todas as suas atividades sem duplicar o código, tenha uma classe de atividade abstrata que estenda Activity e insira seu código comum. Então, cada atividade que você possui pode estender essa atividade abstrata.
Por exemplo:
fonte
ActivityLifecycleCallbacks pode ser interessante, mas não está bem documentado.
Porém, se você chamar registerActivityLifecycleCallbacks (), poderá obter retornos de chamada para quando as Atividades forem criadas, destruídas, etc. Você pode chamar getComponentName () para a Atividade.
fonte
O pacote android.arch.lifecycle fornece classes e interfaces que permitem criar componentes com reconhecimento do ciclo de vida
Seu aplicativo deve implementar a interface LifecycleObserver:
Para fazer isso, você precisa adicionar essa dependência ao seu arquivo build.gradle:
Conforme recomendado pelo Google, você deve minimizar o código executado nos métodos de atividades do ciclo de vida:
Você pode ler mais aqui: https://developer.android.com/topic/libraries/architecture/lifecycle
fonte
No seu aplicativo, adicione o retorno de chamada e verifique a atividade raiz da seguinte maneira:
fonte
Criei um projeto no Github app-foreground-background-listen
Crie uma BaseActivity para todas as atividades no seu aplicativo.
Agora use esta BaseActivity como uma superclasse de toda a sua Atividade, como MainActivity estende BaseActivity e onAppStart será chamado quando você iniciar o aplicativo e onAppPause () será chamado quando o aplicativo for em segundo plano em qualquer tela.
fonte
Isso é muito fácil com o ProcessLifecycleOwner
Adicione essas dependências
Em Kotlin :
Então, na sua atividade base:
Consulte meu artigo sobre este tópico: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48
fonte
Você pode usar o ProcessLifecycleOwner conectando um observador de ciclo de vida a ele.
então, na
onCreate()
classe Application, você chama isso:com isso, você poderá capturar os eventos de
ON_PAUSE
eON_STOP
de seu aplicativo que acontecem quando são exibidos em segundo plano.fonte
Não há métodos simples de ciclo de vida para informar quando todo o aplicativo fica em segundo plano / em primeiro plano.
Eu fiz isso de maneira simples. Siga as instruções abaixo para detectar a fase de segundo plano / primeiro plano do aplicativo.
Com um pouco de solução alternativa, é possível. Aqui, ActivityLifecycleCallbacks vem em socorro. Deixe-me passar passo a passo.
Primeiro, crie uma classe que estenda o android.app.Application e implemente a interface ActivityLifecycleCallbacks . No Application.onCreate (), registre o retorno de chamada.
Registre a classe "App" no manifesto como abaixo
<application android:name=".App"
,.Haverá pelo menos uma atividade no estado iniciado quando o aplicativo estiver em primeiro plano e não haverá atividade no estado iniciado quando o aplicativo estiver em segundo plano.
Declare 2 variáveis como abaixo na classe "App".
activityReferences
manterá a contagem do número de atividades no estado iniciado .isActivityChangingConfigurations
é um sinalizador para indicar se a Atividade atual está passando por alterações na configuração como uma opção de orientação.Usando o código a seguir, você pode detectar se o aplicativo vem em primeiro plano.
Isto é como detectar se o aplicativo fica em segundo plano.
Como funciona:
Este é um pequeno truque feito com a maneira como os métodos do Ciclo de Vida são chamados em sequência. Deixe-me mostrar um cenário.
Suponha que o usuário inicie o aplicativo e a Atividade A do iniciador A seja iniciada. As chamadas do Ciclo de vida serão,
Agora a Atividade A inicia a Atividade B.
Em seguida, o usuário volta da Atividade B,
Em seguida, o usuário pressiona o botão Início,
Caso o usuário pressione o botão Início da Atividade B em vez do botão Voltar, continuará sendo o mesmo e o item ActivityReferences
0
. Portanto, podemos detectar como o aplicativo entrando em segundo plano.Então, qual é o papel
isActivityChangingConfigurations
? No cenário acima, suponha que a atividade B altere a orientação. A sequência de retorno de chamada será,É por isso que temos uma verificação adicional
isActivityChangingConfigurations
para evitar o cenário quando a Atividade estiver passando pelas mudanças na Configuração.fonte
Encontrei um bom método para detectar aplicativos, seja em primeiro plano ou em segundo plano. Aqui está o meu código . Espero que isso ajude você.
}
fonte
Você pode usar:
Para diferenciar entre novas partidas e reinicializações.
fonte
Edição 2: O que escrevi abaixo não funcionará realmente. O Google rejeitou um aplicativo que inclui uma chamada para ActivityManager.getRunningTasks (). A partir da documentação , é aparente que essa API é apenas para fins de depuração e desenvolvimento. Estarei atualizando este post assim que tiver tempo para atualizar o projeto GitHub abaixo com um novo esquema que usa temporizadores e é quase tão bom.
Edit 1: Eu escrevi uma postagem no blog e criei um repositório simples do GitHub para tornar isso realmente fácil.
A resposta aceita e a melhor avaliada não são realmente a melhor abordagem. A implementação da resposta mais bem avaliada de isApplicationBroughtToBackground () não lida com a situação em que a Atividade principal do Aplicativo está cedendo a uma Atividade definida no mesmo Aplicativo, mas possui um pacote Java diferente. Eu vim com uma maneira de fazer isso que funcionará nesse caso.
Ligue para onPause () e ele informará se seu aplicativo está entrando em segundo plano porque outro aplicativo foi iniciado ou o usuário pressionou o botão home.
fonte
Resposta correta aqui
Crie uma classe com o nome MyApp, como abaixo:
Em qualquer lugar que você quiser (melhor primeira atividade lançada no aplicativo), adicione o código abaixo:
Feito! Agora, quando o aplicativo está em segundo plano, obtemos log
status : we are out
e, quando entramos no aplicativo, obtemos logstatus : we are out
fonte
Minha solução foi inspirada na resposta de @ d60402 e também conta com uma janela de tempo, mas sem usar o
Timer
:onde
SingletonApplication
é uma extensão daApplication
classe:fonte
Eu estava usando isso com o Google Analytics EasyTracker, e funcionou. Pode ser estendido para fazer o que você procura usando um número inteiro simples.
fonte
Eu sei que é um pouco tarde, mas acho que todas essas respostas têm alguns problemas enquanto eu fazia isso abaixo e isso funciona perfeitamente.
crie um retorno de chamada do ciclo de vida da atividade como este:
e apenas registre-o na sua classe de aplicativo, como abaixo:
fonte
Essa parece ser uma das perguntas mais complicadas do Android, já que (no momento em que este artigo foi escrito) o Android não possui equivalentes
applicationDidEnterBackground()
ouapplicationWillEnterForeground()
retornos de chamada do iOS . Eu usei uma AppState Library que foi montada por @jenzz .Aconteceu que era exatamente isso que eu precisava, especialmente porque meu aplicativo tinha várias atividades; portanto, simplesmente verificar
onStart()
ou ativaronStop()
uma atividade não seria suficiente.Primeiro, adicionei essas dependências para classificar:
Depois, era simples adicionar essas linhas a um local apropriado no seu código:
Dependendo de como você assina o observável, pode ser necessário cancelar o registro para evitar vazamentos de memória. Novamente, mais informações na página do github .
fonte
Esta é a versão modificada da resposta da @ d60402: https://stackoverflow.com/a/15573121/4747587
Faça tudo mencionado lá. Mas, em vez de ter um
Base Activity
e torná-lo um pai para todas as atividades e substituir oonResume()
eonPause
, faça o seguinte:Na sua classe de aplicativo, adicione a linha:
registerActivityLifecycleCallbacks (retorno de chamada Application.ActivityLifecycleCallbacks);
Isso
callback
possui todos os métodos de ciclo de vida da atividade e agora você pode substituironActivityResumed()
eonActivityPaused()
.Dê uma olhada neste Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
fonte
Você pode conseguir isso facilmente com a ajuda de
ActivityLifecycleCallbacks
eComponentCallbacks2
algo como abaixo.Crie uma classe
AppLifeCycleHandler
implementada acima das interfaces.Na sua classe, que estende o
Application
implementoAppLifeCycleCallback
para obter retornos de chamada quando o aplicativo alterna entre primeiro e segundo plano. Algo como abaixo.Espero que isto ajude.
EDIT Como alternativa, agora você pode usar o componente de arquitetura compatível com o ciclo de vida.
fonte
Como não encontrei nenhuma abordagem, que também lida com a rotação sem verificar os carimbos de data e hora, pensei em compartilhar como agora o fazemos em nosso aplicativo. A única adição a esta resposta https://stackoverflow.com/a/42679191/5119746 é que também levamos em consideração a orientação.
Em seguida, para os retornos de chamada, primeiro temos o resumo:
E onActivityStopped:
E então, aqui vem a adição: Verificando mudanças de orientação:
É isso aí. Espero que isso ajude alguém :)
fonte
Podemos expandir esta solução usando
LiveData
:Agora podemos assinar este LiveData e capturar os eventos necessários. Por exemplo:
fonte
Essas respostas não parecem estar corretas. Esses métodos também são chamados quando outra atividade inicia e termina. O que você pode fazer é manter uma bandeira global (sim, globais são ruins :) e defina isso como verdadeiro sempre que você iniciar uma nova atividade. Defina-o como false no onCreate de cada atividade. Em seguida, na onPause você verifica esse sinalizador. Se for falso, seu aplicativo está entrando em segundo plano ou está sendo morto.
fonte