Como detectar quando um aplicativo Android entra em segundo plano e volta ao primeiro plano

382

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?

iHorse
fonte
2
Pode ser para adicionar um caso de uso à pergunta, porque não parece óbvio, por isso não é abordado nas respostas fornecidas. O aplicativo pode iniciar outro aplicativo (Galeria, por exemplo), que ainda residirá na mesma pilha e aparecerá como uma das telas do aplicativo e, em seguida, pressione o botão Início. Nenhum dos métodos que dependem do ciclo de vida do aplicativo (ou mesmo do gerenciamento de memória) é capaz de detectar isso. Eles acionariam o estado de segundo plano quando a Atividade externa aparecer, não quando você pressionar Início.
Dennis K
Esta é a resposta que você está procurando: stackoverflow.com/a/42679191/2352699
Fred Porciúncula
11
Consulte a Solução do Google: stackoverflow.com/questions/3667022/…
user1269737

Respostas:

98

Os métodos onPause()e onResume()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 onWindowFocusChangede onStop.

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 .

Girish Nair
fonte
174
No entanto, essa abordagem causa falsos positivos, como outros apontaram, porque esses métodos também são chamados ao fazer a transição entre atividades no mesmo aplicativo.
John Lehmann
9
É pior que isso. Eu tentei e às vezes o onResume é chamado enquanto o telefone está bloqueado. Se você vir a definição de onResume na documentação, encontrará: Lembre-se de que onResume não é o melhor indicador de que sua atividade é visível para o usuário; uma janela do sistema como a proteção do teclado pode estar na frente. Use onWindowFocusChanged (booleano) para ter certeza de que sua atividade é visível para o usuário (por exemplo, para retomar um jogo). developer.android.com/reference/android/app/…
J-Rou
2
A solução postada no link não usa onResume / onPause, mas uma combinação de onBackPressed, onStop, onStart e onWindowsFocusChanged. Ele fez um trabalho para mim, e eu tenho uma hierarquia de interface do usuário bastante complexa (com gavetas, viewpagers dinâmicos, etc.)
Martin Marconcini
18
O onPause e onResume são específicos da atividade. Não aplicado. Quando um aplicativo é colocado em segundo plano e depois retomado, ele retoma a atividade específica em que estava antes de entrar em segundo plano. Isso significa que você precisaria implementar o que quiser ao retomar do plano de fundo em todas as atividades do seu aplicativo. Acredito que a pergunta original estava procurando algo como um "onResume" para Aplicativo e não Atividade.
SysHex
4
Não acredito que uma API adequada não seja oferecida para uma necessidade tão comum. Inicialmente eu pensei onUserLeaveHint () iria cortá-la, mas você não pode dizer se o usuário está deixando a aplicação ou não
atsakiridis
197

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 MemoryBossaula 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á uma LOW MEMORYchamada 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 YourApplicatione as MemoryBossclasses no seu class BaseActivity extends Activity(você precisará criar uma se não tiver uma).

@Override
protected void onStart() {
    super.onStart();

    if (mApplication.wasInBackground()) {
        // HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
        mApplication.setWasInBackground(false);
    }
}

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á relatar wasInBackgroundé falseque ele não vai executar o código, até que onMemoryTrimmedé 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 onTrimMemorynão é garantido que a chamada de retorno seja executada:

  1. 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.

  2. 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:

public abstract void onTrimMemory (int level)

O nível é 20 ou mais específico

public static final int TRIM_MEMORY_UI_HIDDEN

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:

Nível para onTrimMemory (int): o processo estava mostrando uma interface com o usuário e não está mais fazendo isso. Alocações grandes com a interface do usuário devem ser liberadas neste momento para permitir que a memória seja melhor gerenciada.

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 onTrimMemorymé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:

public class MemoryBoss implements ComponentCallbacks2 {
    @Override
    public void onConfigurationChanged(final Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // We're in the Background
        }
        // you might as well implement some memory cleanup here and be a nice Android dev.
    }
}

Para usar isso, na implementação de seu aplicativo ( você tem um, certo? ), Faça algo como:

MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
   super.onCreate();
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      mMemoryBoss = new MemoryBoss();
      registerComponentCallbacks(mMemoryBoss);
   } 
}

Se você criar um, Interfacepoderá adicionar um elsea isso ife implementar ComponentCallbacks(sem o 2) usado em qualquer coisa abaixo da API 14. Esse retorno de chamada possui apenas o onLowMemory()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:

/**
 * This method is for use in emulated process environments.  It will
 * never be called on a production Android device, where processes are
 * removed by simply killing them; no user code (including this callback)
 * is executed when doing so.
 */

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:

unregisterComponentCallbacks(mMemoryBoss);

E é isso.

Martin Marconcini
fonte
Ao verificar isso em um serviço, parece disparar apenas quando o botão inicial é pressionado. Pressionar o botão Voltar não dispara isso no KitKat.
Aprenda OpenGL ES
O serviço não possui interface do usuário, portanto, pode estar relacionado a isso. Faça a verificação em sua atividade base, não em um serviço. Você quer saber quando o seu UI está escondido (e talvez dizer o serviço, assim vai Primeiro Plano)
Martin Marconcini
11
Não funciona quando você desliga o telefone. Não é acionado.
Juangcg
2
Usar o ComponentCallbacks2.onTrimMemory () (em combinação com ActivityLifecycleCallbacks) é a única solução confiável que encontrei até agora, graças a Martin! Para os interessados, veja minha resposta fornecida.
rickul
3
Uso esse método há um ano e sempre foi confiável para mim. É bom saber que outras pessoas também o usam. Eu apenas uso o level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDENque 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.
sorianiv
175

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":

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

O aplicativo também fornece dois métodos para iniciar e parar o cronômetro / tarefa:

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            MyApplication.this.wasInBackground = true;
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
                                           MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

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:

@Override
public void onResume()
{
    super.onResume();

    MyApplication myApp = (MyApplication)this.getApplication();
    if (myApp.wasInBackground)
    {
        //Do specific came-here-from-background code
    }

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}

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 .

d60402
fonte
4
Oi d60402, sua resposta é realmente útil .. muito obrigado por esta resposta ... aviso breve .. MyApplication deve mencionar na tag do aplicativo de arquivo Manifest como android: name = "MyApplication", caso contrário, o aplicativo trava ... apenas para ajudar alguém como eu
praveenb
2
marca do grande programador, solução simples para um dos problemas mais complicados que já vi.
Aashish Bhatnagar
2
Solução incrível! Obrigado. Se alguém receber o erro "ClassCastException", você poderá ter deixado de adicioná-lo na tag do aplicativo dentro do seu Manifest.xml <android app: name = "your.package.MyApplication"
Wahib Ul Haq
27
Esta é uma implementação simples e agradável. No entanto, acredito que isso deve ser implementado no onStart / onStop e não no onPause / onResume. A onPause será chamada mesmo se eu iniciar uma caixa de diálogo que cubra parcialmente a atividade. E fechar o diálogo seria realmente chamar onResume fazer parecer como se o aplicativo acaba de chegar ao primeiro plano
Shubhayu
7
Espero usar uma variação desta solução. O ponto sobre os diálogos identificados acima é um problema para mim, então tentei a sugestão de @ Shubhayu (onStart / onStop). No entanto, isso não ajuda, porque quando A-> B, o onStart () da Atividade B é chamado antes do onStop () da Atividade A.
Trevor
150

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 :

class YourApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    registerActivityLifecycleCallbacks(AppLifecycleTracker())
  }

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

  override fun onActivityStarted(activity: Activity?) {
    if (numStarted == 0) {
      // app went to foreground
    }
    numStarted++
  }

  override fun onActivityStopped(activity: Activity?) {
    numStarted--
    if (numStarted == 0) {
      // app went to background
    }
  }

}

Sim. Sei que é difícil acreditar que essa solução simples funcione, pois temos muitas soluções estranhas aqui.

Mas há esperança.

Fred Porciúncula
fonte
3
Isso funciona perfeitamente! Eu já tentei tantas soluções estranhas que tinham tantas falhas ... muito obrigado! Eu estou procurando isso há um tempo.
Eggakin Baconwalker 23/03
7
Ele funciona para várias atividades, mas para um - onrotate indicará sobre todas as atividades são ido ou no fundo
deadfish
2
@ Shyri, você está correto, mas isso faz parte desta solução, portanto, é preciso se preocupar. Se o firebase depende disso, acho que meu aplicativo medíocre também pode :) Ótima resposta BTW.
ElliotM
3
@deadfish Verifique o link para E / S fornecido na parte superior da resposta. Você pode verificar os intervalos de tempo entre a parada da atividade e começar a determinar se realmente foi para o segundo plano ou não. Esta é uma solução brilhante, na verdade.
Alex Berdnikov
2
Existe uma solução Java? Isso é kotlin.
Giacomo Bartoli
116

ProcessLifecycleOwner parece ser uma solução promissora também.

ProcessLifecycleOwner enviará ON_START, ON_RESUMEeventos, como primeiro move actividade através desses eventos. ON_PAUSE, ON_STOP, Eventos serão enviados com um atraso depois de uma última atividade passou por eles. Esse atraso é longo o suficiente para garantir que ProcessLifecycleOwnernão enviará nenhum evento se as atividades forem destruídas e recriadas devido a uma alteração na configuração.

Uma implementação pode ser tão simples quanto

class AppLifecycleListener : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() { // app moved to foreground
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onMoveToBackground() { // app moved to background
    }
}

// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())

De acordo com o código fonte, o valor atual do atraso é 700ms.

O uso desse recurso também requer dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
vokilam
fonte
10
Observe que você precisa adicionar as dependências do ciclo de vida implementation "android.arch.lifecycle:extensions:1.0.0"e annotationProcessor "android.arch.lifecycle:compiler:1.0.0"do repositório do Google (ou seja google())
Sir Codesalot
11
Isso funcionou muito bem para mim, obrigado. Eu tive que usar api 'android.arch.lifecycle: extensions: 1.1.0' em vez de implementação devido a um erro ao afirmar que a dependência do Android tem uma versão diferente para o caminho de classe de compilação e tempo de execução.
FSUWX2011
Esta é uma ótima solução, pois funciona em módulos sem precisar de uma referência de atividade!
Max
Isso não está funcionando quando o aplicativo falha. Existe alguma solução para chegar aplicativo caiu evento também através desta solução
tejraj
Ótima solução. Salvou o meu dia.
Sunny
69

Com base na resposta de Martín Marconcinis (obrigado!), Finalmente encontrei uma solução confiável (e muito simples).

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
    private static boolean isInBackground = false;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){
            Log.d(TAG, "app went to foreground");
            isInBackground = false;
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

    @Override
    public void onTrimMemory(int i) {
        if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
            Log.d(TAG, "app went to background");
            isInBackground = true;
        }
    }
}

Em seguida, adicione isso ao seu onCreate () da sua classe Application

public class MyApp extends android.app.Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
        registerActivityLifecycleCallbacks(handler);
        registerComponentCallbacks(handler);

    }

}
rickul
fonte
Você pode mostrar como você usa isso em um aplicativo? Eu chamo isso da classe App ou de algum outro lugar?
JPM
isso é perfeito obrigado !! funciona muito bem em testes até agora
aherrick
Este exemplo se incompleto. O que é registerActivityLifecycleCallbacks?
No9 de
o seu método de uma classe em android.app.Application
rickul
11
bem feito +1 para ir em cima, porque ele é perfeito, não olhe para outras respostas, isto é baseado em @reno resposta, mas com verdadeiro exemplo
Stoycho Andreev
63

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).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

    @Override
    protected void onStart() {
        super.onStart();       
        sessionDepth++;
        if(sessionDepth == 1){
        //app came to foreground;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (sessionDepth > 0)
            sessionDepth--;
        if (sessionDepth == 0) {
            // app went to background
        }
    }

}

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.

Nick Frolov
fonte
2
Esta é a resposta mais confiável, embora eu use onStart em vez de onResume.
Greg Ennis
Você deve adicionar chamadas a super.onResume () e super.onStop () nos métodos substituídos. Caso contrário, um android.app.SuperNotCalledException é lançado.
Jan Laussmann 26/11
11
para mim, não funciona ... ou, pelo menos, dispara o evento quando você está girando o dispositivo também (o que é uma espécie de falso positivo).
Noya 13/01
Solução muito simples e eficaz! Mas não tenho certeza de que funcione com atividades parcialmente transparentes que permitam a visibilidade de algumas partes da atividade anterior. Dos documentos onStop is called when the activity is no longer visible to the user,.
Nicolas Buquet
3
o que acontece se o usuário alterar a orientação na primeira atividade? Ele relatará que o aplicativo foi para segundo plano, o que não é verdade. Como você lida com esse cenário?
Nimrod Dayan
54

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:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }

    return false;
}

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:

  • A atividade em pausa não está sendo finalizada (o botão "voltar" foi usado). Isso pode ser feito usando o método activity.isFinishing ()
  • Uma nova atividade (mesmo nome do pacote) não está sendo iniciada. Você pode substituir o método startActivity () para definir uma variável que indica isso e redefini-la em onPostResume (), que é o último método a ser executado quando uma atividade é criada / retomada.

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.

Emil
fonte
18
O Google provavelmente rejeitará um aplicativo que use ActivityManager.getRunningTasks (). A documentação declara que é apenas para fins de desenvolvimento. desenvolvedor.android.com/reference/android/app/…
Sky Kelsey
11
Eu descobri que tinha que usar uma combinação dessas abordagens. onUserLeaveHint () foi chamado ao iniciar uma atividade em 14. `@Override public void onUserLeaveHint () {inBackground = isApplicationBroughtToBackground (); } `
listando o barco
7
Os usuários não ficarão muito felizes em usar uma permissão poderosa android.permission.GET_TASKS.
MSquare
6
getRunningTasks foi descontinuado no nível 21. da API
Noya
33

Crie uma classe que se estenda Application. Então, nele podemos usar seu método de substituição onTrimMemory(),.

Para detectar se o aplicativo foi para segundo plano, usaremos:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }
Harpreet
fonte
11
Para FragmentActivityvocê também pode querer adicionar level == ComponentCallbacks2.TRIM_MEMORY_COMPLETEtambém.
Srujan Simha
2
Muito obrigado por apontar para esse método, preciso mostrar um Pin Dialog sempre que o usuário retomar a atividade para segundo plano, usei esse método para escrever um valor pref e verifiquei esse valor em baseActivity.
Sam
18

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:

public abstract AbstractActivity extends Activity {
    private static boolean inBackground = false;

    @Override
    public void onResume() {
        if (inBackground) {
            // You just came from the background
            inBackground = false;
        }
        else {
            // You just returned from another activity within your own app
        }
    }

    @Override
    public void onUserLeaveHint() {
        inBackground = true;
    }
}

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}
OldSchool4664
fonte
19
onUserLeaveHint também é chamado quando se navega para outra atividade
Jonas Stawski
3
onUserLeaveHint não é chamado quando, por exemplo, uma chamada telefônica é recebida e a atividade de chamada se torna ativa, portanto, isso também tem uma vantagem - também pode haver outros casos, já que você pode adicionar um sinalizador à intenção de suprimir a chamada onUserLeaveHint. developer.android.com/reference/android/content/…
Groxx 8/13
11
Além disso, onResume não funciona bem. Eu tentei e às vezes o onResume é chamado enquanto o telefone está bloqueado. Se você vir a definição de onResume na documentação, encontrará: Lembre-se de que onResume não é o melhor indicador de que sua atividade é visível para o usuário; uma janela do sistema como a proteção do teclado pode estar na frente. Use onWindowFocusChanged (booleano) para ter certeza de que sua atividade é visível para o usuário (por exemplo, para retomar um jogo). developer.android.com/reference/android/app/…
J-Rou
esta solução não ajuda a decidir primeiro plano / fundo se houver vários activities.Plz referem stackoverflow.com/questions/3667022/...
Raj Trivedi
14

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.

Reno
fonte
11
Desde api nível 14 = \
imort 29/11
Parece que este é limpo e funciona para mim. Graças
duanbo1983
Como isso difere da resposta aceita, ambas dependem do mesmo ciclo de vida da atividade?
Saitama
13

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:

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

Para fazer isso, você precisa adicionar essa dependência ao seu arquivo build.gradle:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

Conforme recomendado pelo Google, você deve minimizar o código executado nos métodos de atividades do ciclo de vida:

Um padrão comum é implementar as ações dos componentes dependentes nos métodos de ciclo de vida de atividades e fragmentos. No entanto, esse padrão leva a uma organização ruim do código e à proliferação de erros. Usando componentes com reconhecimento do ciclo de vida, é possível mover o código dos componentes dependentes para fora dos métodos do ciclo de vida e para os próprios componentes.

Você pode ler mais aqui: https://developer.android.com/topic/libraries/architecture/lifecycle

matdev
fonte
e adicione isso para se manifestar como: <aplicativo android: name = ". AnotherApp">
Dan Alboteanu
9

No seu aplicativo, adicione o retorno de chamada e verifique a atividade raiz da seguinte maneira:

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityStopped(Activity activity) {
        }

        @Override
        public void onActivityStarted(Activity activity) {
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}
Cynichniy Bandera
fonte
Eu consideraria usar esse modo de implementação. A transição de uma atividade para outra leva apenas alguns milissegundos. Com base no momento em que a última atividade desaparece, pode-se considerar o login novamente do usuário por uma estratégia específica.
22416 drindt #
6

Criei um projeto no Github app-foreground-background-listen

Crie uma BaseActivity para todas as atividades no seu aplicativo.

public class BaseActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    public static boolean isAppInFg = false;
    public static boolean isScrInFg = false;
    public static boolean isChangeScrFg = false;

    @Override
    protected void onStart() {
        if (!isAppInFg) {
            isAppInFg = true;
            isChangeScrFg = false;
            onAppStart();
        }
        else {
            isChangeScrFg = true;
        }
        isScrInFg = true;

        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (!isScrInFg || !isChangeScrFg) {
            isAppInFg = false;
            onAppPause();
        }
        isScrInFg = false;
    }

    public void onAppStart() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in foreground",    Toast.LENGTH_LONG).show();

        // Your code
    }

    public void onAppPause() {

        // Remove this toast
        Toast.makeText(getApplicationContext(), "App in background",  Toast.LENGTH_LONG).show();

        // Your code
    }
}

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.

Kiran Boghra
fonte
@kiran boghra: Há algum falso positivo em sua solução?
Harish Vishwakarma
Resposta perfeita: a função onStart () e onStop () pode ser usada neste caso. que informa sobre o seu aplicativo
Pir Fahim Shah
6

Isso é muito fácil com o ProcessLifecycleOwner

Adicione essas dependências

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"

Em Kotlin :

class ForegroundBackgroundListener : LifecycleObserver {


    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun startSomething() {
        Log.v("ProcessLog", "APP IS ON FOREGROUND")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopSomething() {
        Log.v("ProcessLog", "APP IS IN BACKGROUND")
    }
}

Então, na sua atividade base:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }

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

Ege Kuzubasioglu
fonte
5

Você pode usar o ProcessLifecycleOwner conectando um observador de ciclo de vida a ele.

  public class ForegroundLifecycleObserver implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    public void onAppCreated() {
        Timber.d("onAppCreated() called");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppStarted() {
        Timber.d("onAppStarted() called");
    }

    @OnLifecycleEvent(Event.ON_RESUME)
    public void onAppResumed() {
        Timber.d("onAppResumed() called");
    }

    @OnLifecycleEvent(Event.ON_PAUSE)
    public void onAppPaused() {
        Timber.d("onAppPaused() called");
    }

    @OnLifecycleEvent(Event.ON_STOP)
    public void onAppStopped() {
        Timber.d("onAppStopped() called");
    }
}

então, na onCreate()classe Application, você chama isso:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());

com isso, você poderá capturar os eventos de ON_PAUSEe ON_STOPde seu aplicativo que acontecem quando são exibidos em segundo plano.

Alécio Carvalho
fonte
4

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.

  1. Primeiro, crie uma classe que estenda o android.app.Application e implemente a interface ActivityLifecycleCallbacks . No Application.onCreate (), registre o retorno de chamada.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. Registre a classe "App" no manifesto como abaixo <application android:name=".App",.

  3. 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".

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;

    activityReferencesmanterá 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.

  4. Usando o código a seguir, você pode detectar se o aplicativo vem em primeiro plano.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. Isto é como detectar se o aplicativo fica em segundo plano.

    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }

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,

A.onCreate ()

A.onStart () (++ activityReferences == 1) (o aplicativo entra em primeiro plano)

A.onResume ()

Agora a Atividade A inicia a Atividade B.

A.onPause ()

B.onCreate ()

B.onStart () (++ activityReferences == 2)

B.onResume ()

A.onStop () (--activityReferences == 1)

Em seguida, o usuário volta da Atividade B,

B.onPause ()

A.onStart () (++ activityReferences == 2)

A.onResume ()

B.onStop () (--activityReferences == 1)

B.onDestroy ()

Em seguida, o usuário pressiona o botão Início,

A.onPause ()

A.onStop () (--activityReferences == 0) (o aplicativo entra em segundo plano)

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á,

B.onPause ()

B.onStop () (--activityReferences == 0) (o aplicativo entra em segundo plano?)

B.onDestroy ()

B.onCreate ()

B.onStart () (++ activityReferences == 1) (o aplicativo entra em primeiro plano ??)

B.onResume ()

É por isso que temos uma verificação adicional isActivityChangingConfigurationspara evitar o cenário quando a Atividade estiver passando pelas mudanças na Configuração.

Komal Nikhare
fonte
3

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ê.

/**
 * Custom Application which can detect application state of whether it enter
 * background or enter foreground.
 *
 * @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
 */
 public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {

public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;

private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;

private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;

@Override
public void onCreate() {
    super.onCreate();
    mCurrentState = STATE_UNKNOWN;
    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    // mCurrentState = STATE_CREATED;
}

@Override
public void onActivityStarted(Activity activity) {
    if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
        if (mStateFlag == FLAG_STATE_BACKGROUND) {
            applicationWillEnterForeground();
            mStateFlag = FLAG_STATE_FOREGROUND;
        }
    }
    mCurrentState = STATE_STARTED;

}

@Override
public void onActivityResumed(Activity activity) {
    mCurrentState = STATE_RESUMED;

}

@Override
public void onActivityPaused(Activity activity) {
    mCurrentState = STATE_PAUSED;

}

@Override
public void onActivityStopped(Activity activity) {
    mCurrentState = STATE_STOPPED;

}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {
    mCurrentState = STATE_DESTROYED;
}

@Override
public void onTrimMemory(int level) {
    super.onTrimMemory(level);
    if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidEnterBackground();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
        if (mStateFlag == FLAG_STATE_FOREGROUND) {
            applicationDidDestroyed();
            mStateFlag = FLAG_STATE_BACKGROUND;
        }
    }
}

/**
 * The method be called when the application been destroyed. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidDestroyed();

/**
 * The method be called when the application enter background. But when the
 * device screen off,this method will not invoked.
 */
protected abstract void applicationDidEnterBackground();

/**
 * The method be called when the application enter foreground.
 */
protected abstract void applicationWillEnterForeground();

}

Folyd
fonte
3

Você pode usar:

void protegido onRestart ()

Para diferenciar entre novas partidas e reinicializações.

insira a descrição da imagem aqui

AYBABTU
fonte
3

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.

public static boolean isApplicationBroughtToBackground(final Activity activity) {
  ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}
Sky Kelsey
fonte
Para sua informação, chamar onStart () evita que seja chamado quando uma caixa de diálogo simples é exibida, por exemplo, por um alarme disparando.
Sky Kelsey
2

Resposta correta aqui

Crie uma classe com o nome MyApp, como abaixo:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

    private Context context;
    public void setContext(Context context)
    {
        this.context = context;
    }

    private boolean isInBackground = false;

    @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {


            isInBackground = true;
            Log.d("status = ","we are out");
        }
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

            isInBackground = false;
            Log.d("status = ","we are in");
        }

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

Em qualquer lugar que você quiser (melhor primeira atividade lançada no aplicativo), adicione o código abaixo:

MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);

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

erfan
fonte
1

Minha solução foi inspirada na resposta de @ d60402 e também conta com uma janela de tempo, mas sem usar o Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

  @Override
  protected void onStart() {
    super.onStart();
    wasInBackground = getApp().isInBackground;
    getApp().isInBackground = false;
    getApp().lastForegroundTransition = System.currentTimeMillis();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
      getApp().isInBackground = true;
  }

  protected SingletonApplication getApp(){
    return (SingletonApplication)getApplication();
  }
}

onde SingletonApplicationé uma extensão da Applicationclasse:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}
injetado
fonte
1

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.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

    @Override
    public void onCreate() {
        super.onCreate();
        appBackgroundedDetector();
    }

    private void appBackgroundedDetector() {
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStart(activity);
            }

            @Override
            public void onActivityResumed(Activity activity) {
                isAppBackgrounded++;
                if (isAppBackgrounded > 0) {
                    // Do something here
                }
            }

            @Override
            public void onActivityPaused(Activity activity) {
                isAppBackgrounded--;
            }

            @Override
            public void onActivityStopped(Activity activity) {
                EasyTracker.getInstance(MainApplication.this).activityStop(activity);
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}
Bill Mote
fonte
1

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:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    Activity lastActivity;
    @Override
    public void onActivityResumed(Activity activity) {
        //if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when  app has been killed or started for the first time
        if (activity != null && activity == lastActivity) 
        {
            Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
        }

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

e apenas registre-o na sua classe de aplicativo, como abaixo:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
Amir Ziarati
fonte
Isso é chamado o tempo todo em cada atividade. Como posso usar isso se, por exemplo, quero detectar o status on-line do usuário?
Maksim Kniazev 11/17/17
isso é o que a pergunta quer. ele só é chamado quando você vai para a tela inicial e retorna a qualquer atividade.
Amir Ziarati
se você quer dizer conectividade com a Internet, acho melhor verificar quando você precisar. se você precisar ligar para uma API, verifique a conexão com a Internet antes de ligar.
Amir Ziarati
1

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()ou applicationWillEnterForeground()retornos de chamada do iOS . Eu usei uma AppState Library que foi montada por @jenzz .

[AppState é] uma biblioteca Android simples e reativa baseada em RxJava que monitora as alterações de estado do aplicativo. Ele notifica os assinantes toda vez que o aplicativo entra em segundo plano e volta ao primeiro plano.

Aconteceu que era exatamente isso que eu precisava, especialmente porque meu aplicativo tinha várias atividades; portanto, simplesmente verificar onStart()ou ativar onStop()uma atividade não seria suficiente.

Primeiro, adicionei essas dependências para classificar:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

Depois, era simples adicionar essas linhas a um local apropriado no seu código:

//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
    @Override
    public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
        switch (appState) {
            case FOREGROUND:
                Log.i("info","App entered foreground");
                break;
            case BACKGROUND:
                Log.i("info","App entered background");
                break;
        }
    }
});

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 .

deniz
fonte
1

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 Activitye torná-lo um pai para todas as atividades e substituir o onResume()e onPause, faça o seguinte:

Na sua classe de aplicativo, adicione a linha:

registerActivityLifecycleCallbacks (retorno de chamada Application.ActivityLifecycleCallbacks);

Isso callbackpossui todos os métodos de ciclo de vida da atividade e agora você pode substituir onActivityResumed()e onActivityPaused().

Dê uma olhada neste Gist: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b

Henry
fonte
1

Você pode conseguir isso facilmente com a ajuda de ActivityLifecycleCallbackse ComponentCallbacks2algo como abaixo.

Crie uma classe AppLifeCycleHandlerimplementada acima das interfaces.

package com.sample.app;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;

/**
 * Created by Naveen on 17/04/18
 */
public class AppLifeCycleHandler
    implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

  AppLifeCycleCallback appLifeCycleCallback;

  boolean appInForeground;

  public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
    this.appLifeCycleCallback = appLifeCycleCallback;
  }

  @Override
  public void onActivityResumed(Activity activity) {
    if (!appInForeground) {
      appInForeground = true;
      appLifeCycleCallback.onAppForeground();
    }
  }

  @Override
  public void onTrimMemory(int i) {
    if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
      appInForeground = false;
      appLifeCycleCallback.onAppBackground();
    }
  }

  @Override
  public void onActivityCreated(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityStarted(Activity activity) {

  }

  @Override
  public void onActivityPaused(Activity activity) {

  }

  @Override
  public void onActivityStopped(Activity activity) {

  }

  @Override
  public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

  }

  @Override
  public void onActivityDestroyed(Activity activity) {

  }

  @Override
  public void onConfigurationChanged(Configuration configuration) {

  }

  @Override
  public void onLowMemory() {

  }

  interface AppLifeCycleCallback {

    void onAppBackground();

    void onAppForeground();
  }
}

Na sua classe, que estende o Applicationimplemento AppLifeCycleCallbackpara obter retornos de chamada quando o aplicativo alterna entre primeiro e segundo plano. Algo como abaixo.

public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

    @Override
    public void onCreate() {
        super.onCreate();
        AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
        registerActivityLifecycleCallbacks(appLifeCycleHandler);
        registerComponentCallbacks(appLifeCycleHandler);
    }

    @Override
    public void onAppBackground() {
        Log.d("LifecycleEvent", "onAppBackground");
    }

    @Override
    public void onAppForeground() {
        Log.d("LifecycleEvent", "onAppForeground");
    }
}

Espero que isto ajude.

EDIT Como alternativa, agora você pode usar o componente de arquitetura compatível com o ciclo de vida.

Naveen TP
fonte
1

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.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

   private var mAppIsInBackground = false
   private var mCurrentOrientation: Int? = null
   private var mOrientationWasChanged = false
   private var mResumed = 0
   private var mPaused = 0

Em seguida, para os retornos de chamada, primeiro temos o resumo:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

         // !!! App came from background !!! Insert code

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

E onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

       if (mResumed == mPaused && !mOrientationWasChanged) {

       // !!! App moved to background !!! Insert code

        mAppIsInBackground = true
    }

E então, aqui vem a adição: Verificando mudanças de orientação:

   override fun onConfigurationChanged(newConfig: Configuration) {

       if (newConfig.orientation != mCurrentOrientation) {
           mCurrentOrientation = newConfig.orientation
           mOrientationWasChanged = true
       }
       super.onConfigurationChanged(newConfig)
   }

É isso aí. Espero que isso ajude alguém :)

Julian Horst
fonte
1

Podemos expandir esta solução usando LiveData:

class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

    private var lifecycleListener: LifecycleObserver? = null

    override fun onActive() {
        super.onActive()
        lifecycleListener = AppLifecycleListener().also {
            ProcessLifecycleOwner.get().lifecycle.addObserver(it)
        }
    }

    override fun onInactive() {
        super.onInactive()
        lifecycleListener?.let {
            this.lifecycleListener = null
            ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
        }
    }

    internal inner class AppLifecycleListener : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onMoveToForeground() {
            value = State.FOREGROUND
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onMoveToBackground() {
            value = State.BACKGROUND
        }
    }

    enum class State {
        FOREGROUND, BACKGROUND
    }
}

Agora podemos assinar este LiveData e capturar os eventos necessários. Por exemplo:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}
Alex Kisel
fonte
0

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.

Joris Weimar
fonte
Eu não falei sobre um banco de dados ... o que você quer dizer?
Joris Weimar
estou apoiando sua resposta. mesmo que nós podemos salvar o valor de sinalizador no banco de dados quando no atendimento de pausa não é a solução bom ..
Sandeep P