Verificando se um aplicativo Android está sendo executado em segundo plano

329

Por plano de fundo, quero dizer que nenhuma das atividades do aplicativo está atualmente visível para o usuário?

cppdev
fonte
7
Estou confuso aqui .. por que o android não pode fornecer uma substituição simples na classe do aplicativo para isso? É muito difícil saber isso no nível da plataforma? @Override protegido void onApplicationSentToBackground () {}
Chuck D
2
@ ChuckD - isso faria sentido, que é o que o SDK do Android parece evitar às vezes. : /
Marque
1
O iOS tem esse problema, não sei por que o Google torna isso tão difícil. É uma necessidade tão óbvia.
Jerry Destremps

Respostas:

388

Existem algumas maneiras de detectar se seu aplicativo está sendo executado em segundo plano, mas apenas uma delas é totalmente confiável:

  1. A solução certa (créditos vão para Dan , CommonsWare e NeTeInStEiN )
    visibilidade da trilha de sua aplicação por si mesmo usando Activity.onPause, Activity.onResumemétodos. Armazene o status de "visibilidade" em outra classe. Boas escolhas são sua própria implementação do Applicationou a Service(também existem algumas variações dessa solução, se você quiser verificar a visibilidade da atividade do serviço).
     
    Exemplo
    Implementar Applicationclasse personalizada (observe o isActivityVisible()método estático):

    public class MyApplication extends Application {
    
      public static boolean isActivityVisible() {
        return activityVisible;
      }  
    
      public static void activityResumed() {
        activityVisible = true;
      }
    
      public static void activityPaused() {
        activityVisible = false;
      }
    
      private static boolean activityVisible;
    }

    Registre sua classe de aplicativo em AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >

    Adicione onPausee onResumea todos Activityno projeto (você pode criar um ancestral comum para suas atividades, se desejar, mas se sua atividade já estiver estendida de MapActivity/ ListActivityetc., você ainda precisará escrever o seguinte manualmente):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }

     
    A atualização
    ActivityLifecycleCallbacks foi adicionada no nível da API 14 (Android 4.0). Você pode usá-los para rastrear se uma atividade do seu aplicativo está atualmente visível para o usuário. Verifique a resposta da Cornstalks abaixo para obter detalhes.

  2. O errado,
    eu costumava sugerir a seguinte solução:

    Você pode detectar atualmente o aplicativo de primeiro plano / plano de fundo com o ActivityManager.getRunningAppProcesses()qual retorna uma lista de RunningAppProcessInforegistros. Para determinar se sua aplicação é no cheque primeiro plano RunningAppProcessInfo.importancecampo de igualdade para RunningAppProcessInfo.IMPORTANCE_FOREGROUNDquando RunningAppProcessInfo.processNameé igual ao seu nome de pacote de aplicativos.

    Além disso, se você ligar a ActivityManager.getRunningAppProcesses()partir do encadeamento da interface do usuário do aplicativo, ele retornará importância IMPORTANCE_FOREGROUNDà sua tarefa, independentemente de estar realmente em primeiro plano ou não. Chame-o no thread de segundo plano (por exemplo, via AsyncTask) e ele retornará os resultados corretos.

    Embora essa solução possa funcionar (e de fato funciona na maioria das vezes), recomendo abster-se de usá-la. E aqui está o porquê. Como Dianne Hackborn escreveu :

    Essas APIs não existem para os aplicativos basearem o fluxo da interface do usuário, mas para fazer coisas como mostrar ao usuário os aplicativos em execução, um gerenciador de tarefas ou algo assim.

    Sim, há uma lista mantida na memória para essas coisas. No entanto, ele está desativado em outro processo, gerenciado por threads em execução separadamente do seu, e não é algo que você possa contar (a) vendo a tempo de tomar a decisão correta ou (b) tenha uma imagem consistente no momento em que você retornar. Além disso, a decisão sobre o que a "próxima" atividade a ser realizada é sempre feita no ponto em que a troca ocorrerá e não é até aquele ponto exato (em que o estado da atividade é bloqueado brevemente para fazer a troca) que nós realmente sei com certeza qual será a próxima coisa.

    E não é garantido que a implementação e o comportamento global aqui permaneçam os mesmos no futuro.

    Eu gostaria de ter lido isso antes de postar uma resposta no SO, mas espero que não seja tarde demais para admitir o meu erro.

  3. Outra solução errada que
    a biblioteca Droid-Fu mencionada em uma das respostas usa ActivityManager.getRunningTaskspara seu isApplicationBroughtToBackgroundmétodo. Veja o comentário de Dianne acima e também não use esse método.

Idolon
fonte
4
Para saber se você pressionou o botão home ou algum outro aplicativo ganhou o foco: 1) implemente a boa solução . 2) A OnStoppedido de isActivityVisible.
Brais Gabin
28
Infelizmente, sua solução 'correta' não funciona para mim. Considere percorrer as atividades em seu aplicativo. O que acontece então é que o sinalizador 'inForeground' é assim: True, False (Entre a 1ª atividade da onPause e a 2ª atividade da onResume) e depois True novamente, etc. Você precisaria de algum tipo de histerese.
Radu
14
Esta solução não funcionará se você não puder controlar diretamente todas as atividades. Por exemplo, se você tiver uma atividade de um sdk de terceiros ou mesmo iniciar uma intenção ACTION_VIEW.
precisa saber é o seguinte
66
O Android é um naufrágio. Ninguém pensou que alguém poderia querer manter os dados no nível do aplicativo? Dê uma pausa
8
Parece que a resposta real para esta pergunta é "Você não pode verificar corretamente". A chamada solução 'correta' é uma solução alternativa, na melhor das hipóteses, assim como o ActivityLifecycleCallbacks. Você ainda precisa considerar alternar entre atividades que seriam registradas como "não em primeiro plano". Ele sopra minha mente que você não pode verificar uma coisa simples como isso ...
serina
263

NÃO USE ESTA RESPOSTA

A resposta do usuário1269737 é a maneira correta (aprovada pelo Google / Android) de fazer isso . Leia a resposta e dê um +1.

Vou deixar minha resposta original aqui por causa da posteridade. Este foi o melhor disponível em 2012, mas agora o Android tem suporte adequado para isso.

Resposta original

A chave está em uso ActivityLifecycleCallbacks(observe que isso requer o nível 14 da API do Android (Android 4.0)). Basta verificar se o número de atividades interrompidas é igual ao número de atividades iniciadas. Se eles são iguais, seu aplicativo está em segundo plano. Se houver mais atividades iniciadas, seu aplicativo ainda estará visível. Se houver mais atividades retomadas do que pausadas, seu aplicativo não estará apenas visível, mas também em primeiro plano. Existem três estados principais em que sua atividade pode estar: visível e em primeiro plano, visível, mas não em primeiro plano, e não visível e não em primeiro plano (ou seja, em primeiro plano).

O mais interessante desse método é que ele não possui problemas assíncronos getRunningTasks(), mas você também não precisa modificar todos os Activityaplicativos para definir / desabilitar algo em onResumed()/ onPaused(). São apenas algumas linhas de código que são independentes e funcionam em todo o aplicativo. Além disso, também não há permissões funky.

MyLifecycleHandler.java:

public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
    // I use four separate variables here. You can, of course, just use two and
    // increment/decrement them instead of using four and incrementing them all.
    private int resumed;
    private int paused;
    private int started;
    private int stopped;

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        ++paused;
        android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
    }

    @Override
    public void onActivityStopped(Activity activity) {
        ++stopped;
        android.util.Log.w("test", "application is visible: " + (started > stopped));
    }

    // If you want a static function you can use to check if your application is
    // foreground/background, you can use the following:
    /*
    // Replace the four variables above with these four
    private static int resumed;
    private static int paused;
    private static int started;
    private static int stopped;

    // And these two public static functions
    public static boolean isApplicationVisible() {
        return started > stopped;
    }

    public static boolean isApplicationInForeground() {
        return resumed > paused;
    }
    */
}

MyApplication.java:

// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        // Simply add the handler, and that's it! No need to add any code
        // to every activity. Everything is contained in MyLifecycleHandler
        // with just a few lines of code. Now *that's* nice.
        registerActivityLifecycleCallbacks(new MyLifecycleHandler());
    }
}

O @Mewzer fez algumas boas perguntas sobre esse método que eu gostaria de responder nesta resposta para todos:

onStop()não é chamado em situações de pouca memória; isso é um problema aqui?

Não. Os documentos para onStop()dizer:

Observe que esse método nunca pode ser chamado, em situações de pouca memória em que o sistema não possui memória suficiente para manter o processo de sua atividade em execução após a chamada do método onPause ().

A chave aqui é "manter o processo de sua atividade em execução ..." Se essa situação de pouca memória for atingida, seu processo será realmente eliminado (não apenas sua atividade). Isso significa que esse método de verificação do background-ness ainda é válido porque a) você não pode verificar o background de qualquer maneira se o processo for interrompido eb) se o processo iniciar novamente (porque uma nova atividade é criada), o membro variáveis ​​(estáticas ou não) para MyLifecycleHandlerserão redefinidas para0 .

Isso funciona para alterações na configuração?

Por padrão, não. Você deve definir explicitamente configChanges=orientation|screensize( |com qualquer outra coisa que desejar) em seu arquivo de manifesto e manipular as alterações na configuração, caso contrário, sua atividade será destruída e recriada. Se você não definir isso, os métodos de sua atividade será chamado nesta ordem: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. Como você pode ver, não há sobreposição (normalmente, duas atividades se sobrepõem muito rapidamente ao alternar entre as duas, e é assim que esse método de detecção de plano de fundo funciona). Para contornar isso, você deve definir configChangespara que sua atividade não seja destruída. Felizmente, eu tive que definirconfigChangesjá em todos os meus projetos, porque não era desejável que toda a minha atividade fosse destruída na tela girar / redimensionar, então nunca achei isso problemático. (obrigado a dpimka por refrescar minha memória e me corrigir!)

Uma nota:

Quando eu disse "plano de fundo" aqui nesta resposta, quis dizer "seu aplicativo não está mais visível". As atividades do Android podem estar visíveis, mas não em primeiro plano (por exemplo, se houver uma sobreposição de notificação transparente). É por isso que atualizei esta resposta para refletir isso.

É importante saber que o Android tem um momento estranho de limbo ao alternar atividades onde nada está em primeiro plano . Por esse motivo, se você verificar se seu aplicativo está em primeiro plano ao alternar entre atividades (no mesmo aplicativo), você será informado de que não está em primeiro plano (mesmo que seu aplicativo ainda seja o aplicativo ativo e esteja visível )

Você pode verificar se seu aplicativo estiver em primeiro plano em sua Activity's onPause()método depois super.onPause() . Lembre-se do estranho estado do limbo que acabei de falar.

Você pode verificar se seu aplicativo é visível (ou seja, se ele não está no fundo) em sua Activity's onStop()método depois super.onStop() .

Talos de milho
fonte
1
Isso parece interessante - mas o que acontece em situações de pouca memória? Não é garantido que onStop () seja chamado. Poderíamos entrar na situação em que um onStop () não é chamado e o contador parado não é incrementado - isso significa que a verificação em segundo plano não é mais confiável? Ou isso nunca aconteceria?
Mewzer
1
Além disso, isso ignorará as alterações na configuração? Ou o aplicativo será considerado em segundo plano se uma atividade for recriada como resultado de uma alteração na configuração (por exemplo, mudança na orientação) ?. Desculpe, pelas perguntas, mas acho que você gosta de algo e está interessado em saber se funciona nesses casos extremos.
Mewzer
1
@Mewzer: Eu ia responder como um comentário, mas será necessário digitar um pouco para obter essas respostas, então volte em alguns minutos e eu editarei minha resposta.
Cornstalks
1
@ Mrzer: Você deve encontrar suas respostas agora. Deixe-me saber se existem outras perguntas!
Cornstalks
2
@Mewzer: Acabei de adicionar uma observação que lhe interessa. Especificamente, verifique se há antecedentes onStop()depois super.onStop(). Não verifique se há antecedentes onPause().
precisa
187

SOLUÇÃO DO GOOGLE - não um truque, como as soluções anteriores. Use ProcessLifecycleOwner

Kotlin:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        // App in foreground
    }

}


Java:

public class ArchLifecycleApp extends Application implements LifecycleObserver {

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    public void onAppBackgrounded() {
        //App in background
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    public void onAppForegrounded() {
        // App in foreground
    }
}

no app.gradle

dependencies {
    ...
    implementation "android.arch.lifecycle:extensions:1.1.0"

    //New Android X dependency is this - 
    implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

}

allprojects {
    repositories {
        ...
        google()
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}

Você pode ler mais sobre os componentes da arquitetura relacionados ao Ciclo de vida aqui - https://developer.android.com/topic/libraries/architecture/lifecycle

user1269737
fonte
10
Definitivamente, essa deve ser a resposta correta! Funcionou como um encanto: D
JaviOverflow
2
Isso funciona perfeitamente, eu também modifiquei um pouco para poder acessar o estado de primeiro plano / plano de fundo mais facilmente fora desta classe: companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }então você pode obter o estado de primeiro plano com #ArchLifecycleApp.isForeground()
Jose Jose
2
Oh cara, isso é muito melhor do que minha resposta antiga. Tenha um +1 de mim. Atualizei minha resposta para apontar as pessoas para a sua.
Cornstalks
2
Embora seja uma resposta correta, não há necessidade de implementar retornos de chamada, você pode consultar o ProcessLifecycleOwner sempre que quiser. Verifique stackoverflow.com/a/52678290/6600000
Keivan Esbati
2
Como o doc diz The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. , isso não está funcionando para multiple processesaplicativos. Existe alguma API que podemos alcançar com elegância?
acntwww
23

Iniciando a biblioteca de suporte versão 26, você pode usar ProcessLifecycleOwner , basta adicioná-lo à sua dependência, como descrito aqui , por exemplo:

dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData).
    //     Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

E, em seguida, basta consultar ProcessLifecycleOwnersempre que desejar o estado do aplicativo, exemplos:

//Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;

//Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
Keivan Esbati
fonte
2
Obrigado cara, esta é a melhor e mais fácil maneira que é adequada em qualquer parte do seu código, especialmente ao usar o fcm.
Mihae Kheel
se o aplicativo for fechado completamente, o que o primeiro método retornaria?
Evgeniy Mishustin 04/07/19
@EvgeniyMishustin que depende do estado atual do aplicativo, mas geralmente você vê CREATED, DESTROYED e depois disso, você não recebe novos eventos.
Keivan Esbati 04/04/19
Então, onde está qualquer declaração "SE" para ver se o aplicativo está em segundo plano (primeiro plano) ???
ekashking
@ekashking apenas coloca a declaração inteira na cláusula if. Por exemplo: if (ProcessLifecycleOwner.get (). GetLifecycle (). GetCurrentState (). IsAtLeast (Lifecycle.State.STARTED)) => O aplicativo está em primeiro plano
Keivan Esbati
20

Desde a API do Android 16, existe uma maneira simples de verificar se o aplicativo está em primeiro plano. Pode não ser infalível, mas nenhum método no Android é infalível. Esse método é bom o suficiente para usar quando seu serviço recebe atualização do servidor e precisa decidir se deseja mostrar uma notificação ou não (porque se a interface do usuário estiver em primeiro plano, o usuário notará a atualização sem notificação).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Juozas Kontvainis
fonte
esse código deve estar dentro da classe Service ou outra classe, por exemplo, classe Application? Muito obrigado.
Woppi
..quando você quiser usá-lo, como a linha final é apenas um booleano contra o qual você verificaria.
AO_
Essa é a mesma metodologia do AWS Android SDK para notificações por push.
Spakmad
Esteja ciente de que "A definição de plano de fundo para fins de limitações de serviço é distinta da definição usada pelo gerenciamento de memória; um aplicativo pode estar em segundo plano no que diz respeito ao gerenciamento de memória, mas em primeiro plano no que diz respeito à sua capacidade de iniciar serviços.) " developer.android.com/about/versions/oreo/background.html (
ARLabs
Obrigado, isso funcionou! Consegui usar esse código em um JobServicepara detectar que o serviço está sendo executado em segundo plano.
Michael Osofsky
17

A resposta do ídolo é propensa a erros e muito mais complicada, apesar de repetida aqui. Verifique se o aplicativo Android está em primeiro plano ou não? e aqui Determinando o aplicativo em primeiro plano atual de uma tarefa ou serviço em segundo plano

Existe uma abordagem muito mais simples:

Em uma BaseActivity que todas as Atividades estendem:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }

Sempre que você precisar verificar se alguma de suas atividades de aplicativo está em primeiro plano, verifique isVisible() ;

Para entender essa abordagem, verifique esta resposta do ciclo de vida da atividade lado a lado : Ciclo de vida da atividade lado a lado

Neteinstein
fonte
3
Idolon's answer is error prone- infelizmente tenho que concordar com você. Com base no comentário de Dianne Hackborn nos Grupos do Google, atualizei minha resposta. Verifique-o por favor para os detalhes.
Idolon 8/08
2
Esta também não é uma solução infalível. Um cenário é se o usuário puxado para baixo o painel de notificação, em seguida, nem o onPause, onStopnem o onResumeevento é chamado. Então, o que você faz se nenhum desses eventos for acionado ?!
Isso me levou a esta pergunta: stackoverflow.com/questions/33657102/…
Ruchir Baronia
Infelizmente, mas esse código funciona errado quando uma atividade é iniciada quando uma tela está desligada. Nesse caso, onResume e onPause são chamados tornando isVisible = false.
CoolMind 10/05
@CoolMind Você pode explicar qual é o caso de uso em que você iniciaria uma atividade em segundo plano?
Neteinstein 10/05
11

Tentei a solução recomendada que usa Application.ActivityLifecycleCallbacks e muitos outros, mas eles não funcionaram conforme o esperado. Graças ao Sarge , eu vim com uma solução bastante fácil e direta que estou descrevendo abaixo.

A chave da solução é o fato de entendermos que, se tivermos ActivityA e ActivityB, e chamarmos ActivityB de ActivityA (e não chamar ActivityA.finish), os ActivityB onStart()serão chamados antes de ActivityA onStop().

Essa também é a principal diferença entre onStop()e onPause()que nenhuma mencionou nos artigos que li.

Portanto, com base no comportamento do ciclo de vida dessa atividade, você pode simplesmente contar quantas vezes foi onStart()e onPause()foi chamado em seu programa. Observe que para cada Activity programa, você deve substituir onStart()e onStop(), a fim de aumentar / diminuir a variável estática usada para a contagem. Abaixo está o código que implementa essa lógica. Observe que estou usando uma classe que se estende Application; portanto, não se esqueça de declarar emManifest.xml dentro da tag Application:, android:name=".Utilities"embora possa ser implementada usando uma classe personalizada simples também.

public class Utilities extends Application
{
    private static int stateCounter;

    public void onCreate()
    {
        super.onCreate();
        stateCounter = 0;
    }

    /**
     * @return true if application is on background
     * */
    public static boolean isApplicationOnBackground()
    {
        return stateCounter == 0;
    }

    //to be called on each Activity onStart()
    public static void activityStarted()
    {
        stateCounter++;
    }

    //to be called on each Activity onStop()
    public static void activityStopped()
    {
        stateCounter--;
    }
}

Agora, em cada Atividade do nosso programa, devemos substituir onStart()eonStop() e aumentar / diminuir conforme mostrado abaixo:

@Override
public void onStart()
{
    super.onStart();
    Utilities.activityStarted();
}

@Override
public void onStop()
{
    Utilities.activityStopped();
    if(Utilities.isApplicationOnBackground())
    {
        //you should want to check here if your application is on background
    }
    super.onStop();
}

Com essa lógica, existem 2 casos possíveis:

  1. stateCounter = 0 : O número de paradas é igual ao número de atividades iniciadas, o que significa que o aplicativo está sendo executado em segundo plano.
  2. stateCounter > 0 : O número de iniciados é maior que o número de interrompidos, o que significa que o aplicativo está sendo executado em primeiro plano.

Aviso prévio: stateCounter < 0 significaria que há mais atividades interrompidas do que iniciadas, o que é impossível. Se você encontrar esse caso, significa que não está aumentando / diminuindo o contador como deveria.

Você está pronto para ir. Você deve verificar se seu aplicativo está em segundo plano por dentro onStop().

Menelaos Kotsollaris
fonte
Eu me mudaria if(Utilities.isApplicationOnBackground()) …para Utilities. Porque, caso contrário, apenas uma atividade específica reagirá ao evento.
Nome de exibição
10

A menos que você mesmo o rastreie, não há como determinar se alguma de suas atividades é visível ou não. Talvez você deva pensar em fazer uma nova pergunta no StackOverflow, explicando o que você está tentando obter com a experiência do usuário, para que possamos fornecer idéias alternativas de implementação.

CommonsWare
fonte
2
No android, temos uma configuração chamada "Dados em segundo plano". Essa configuração ativa qualquer conexão de dados em segundo plano quando o aplicativo está sendo executado em segundo plano. Desejo implementar a alternância "Dados em segundo plano" para meu aplicativo. Portanto, quando nenhuma de minhas atividades estiver visível para o usuário, gostaria que meu serviço parasse de fazer qualquer transferência de dados, mas, no momento em que uma de minhas atividades for retomada, gostaria de retomar a transferência de dados
cppdev 08/09
1
@cppdev: Felizmente, a "transferência de dados" está sendo conduzida por um Service. Nesse caso, faça com que suas atividades notifiquem o serviço à medida que aparecerem e desaparecerem. Se Servicedeterminar que não há atividades visíveis e permanecer assim por algum tempo, pare a transferência de dados no próximo ponto de parada lógica. Sim, isso exigirá código para cada uma de suas atividades, mas, no momento, isso é inevitável para o AFAIK.
CommonsWare
1
Se você deseja evitar copiar e colar o código comum entre todas as suas atividades, é possível criar uma classe que MyActivityClassherda Activitye implementa os métodos do ciclo de vida e fazer com que todas as suas atividades sejam herdadas MyActivityClass. Este, sem trabalho para PreferenceActivityou MapActivityembora (ver esta questão )
Guillaume Brunerie
@CommonsWare eu tinha tentado com OnPause () onResume () que é ativo ou não, mas se meu aplicativo vista dosen't na tela de visualização se ele é executado em segundo plano como verificar se ele está ativo ou não
Manoj
@CommonsWare eu tinha tentado com OnPause () onResume () que é ativo ou não, mas se meu aplicativo vista dosen't na tela de visualização se ele é executado em segundo plano como verificar se ele está ativo ou não
Manoj
5

Você pode usar o ComponentCallbacks2 para detectar se o aplicativo está em segundo plano. BTW este retorno de chamada está disponível apenas na API Nível 14 (Ice Cream Sandwich) e acima.

Você receberá uma chamada para o método:

public abstract void onTrimMemory (int level)

se o nível é ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN , o aplicativo estará em segundo plano.

Você pode implementar essa interface para um activity, serviceetc.

public class MainActivity extends AppCompatActivity 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) {
        // app is in background
     }
   }
}
Rohit Arya
fonte
1
Tentei sua resposta, mas não é tão confiável. o retorno de chamada onTrimMemory não será acionado quando a tela estiver bloqueada nem ao pressionar o botão "ligar / desligar" para bloquear a tela. Também nem sempre retornará TRIM_MEMORY_UI_HIDDEN se seu aplicativo estiver visível e você abrir outro aplicativo por meio de uma notificação da barra de status. A única solução confiável é implementar os ActivityLifecycleCallbacks e ajustá-los aos casos de uso.
velval
4

Com base no @Cornstalks, responda para incluir alguns recursos úteis.

Recursos extras:

  • introduziu o padrão singleton para que você possa fazer isso em qualquer lugar do aplicativo: AppLifecycleHandler.isApplicationVisible () e AppLifecycleHandler.isApplicationInForeground ()
  • manipulação adicional de eventos duplicados (consulte os comentários // execute alguma ação sobre alteração de visibilidade e // execute alguma ação sobre alteração de primeiro plano)

App.java

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
    }
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
    private int resumed;
    private int started;

    private final String DebugName = "AppLifecycleHandler";

    private boolean isVisible = false;
    private boolean isInForeground = false;

    private static AppLifecycleHandler instance;

    public static AppLifecycleHandler getInstance() {
        if (instance == null) {
            instance = new AppLifecycleHandler();
        }

        return instance;
    }

    private AppLifecycleHandler() {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {
        ++resumed;
        android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

    @Override
    public void onActivityPaused(Activity activity) {
        --resumed;
        android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
        setForeground((resumed > 0));
    }

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

    @Override
    public void onActivityStarted(Activity activity) {
        ++started;
        android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    @Override
    public void onActivityStopped(Activity activity) {
        --started;
        android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
        setVisible((started > 0));
    }

    private void setVisible(boolean visible) {
        if (isVisible == visible) {
            // no change
            return;
        }

        // visibility changed
        isVisible = visible;
        android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);

        // take some action on change of visibility
    }

    private void setForeground(boolean inForeground) {
        if (isInForeground == inForeground) {
            // no change
            return;
        }

        // in foreground changed
        isInForeground = inForeground;
        android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);

        // take some action on change of in foreground

    }

    public static boolean isApplicationVisible() {
        return AppLifecycleHandler.getInstance().started > 0;
    }

    public static boolean isApplicationInForeground() {
        return AppLifecycleHandler.getInstance().resumed > 0;
    }
}
seb
fonte
3

A melhor solução que eu criei usa temporizadores.

Você iniciou um timer em onPause () e cancelou o mesmo timer em onResume (); há 1 instância do Timer (geralmente definida na classe Application). O cronômetro propriamente dito está definido para executar um Runnable após 2 segundos (ou qualquer intervalo que você julgar apropriado), quando o cronômetro é acionado, você define um sinalizador que marca o aplicativo como em segundo plano.

No método onResume () antes de cancelar o timer, você pode consultar o sinalizador em segundo plano para executar qualquer operação de inicialização (por exemplo, iniciar downloads ou ativar os serviços de localização).

Esta solução permite que você tenha várias atividades na pilha de trás e não requer nenhuma permissão para implementar.

Essa solução funciona bem se você também usar um barramento de eventos, pois o timer pode simplesmente disparar um evento e várias partes do seu aplicativo podem responder de acordo.

Andrew Kelly
fonte
Eu estou começando a pensar que este é o melhor (embora infeliz) solução
dhaag23
Sim, esta é a melhor solução que eu também consegui. Eu precisava interromper a verificação do bluetooth quando o aplicativo não estava em primeiro plano, mas não podia simplesmente usar a pausa, parar ou destruir, porque não queria parar e iniciar constantemente quando o usuário navegava pelo aplicativo.
CaptRespect
3

Se você ativar as configurações do desenvolvedor "Não mantenha atividades" - verifique se apenas a contagem de atividades criadas não é suficiente. Você também deve verificar isSaveInstanceState . Minha verificação personalizada do método isApplicationRunning () está executando o aplicativo Android:

Aqui meu código de trabalho:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ++created;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}
a_subscriber
fonte
3

A única solução correta:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

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

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}
Dmitry
fonte
Não vejo como essa solução pode me dar uma resposta sobre uma pergunta simples em uma declaração IF sobre minha atividade (ou fragmento), se meu aplicativo está em segundo plano ou em primeiro plano. Onde está a instrução "SE" ???
ekashking
2

Para pegar o que o CommonsWare e Key disseram, talvez você possa estender a classe Application e fazer com que todas as suas atividades chamem isso pelos métodos onPause / onResume. Isso permitiria que você soubesse quais atividades estão visíveis, mas isso provavelmente poderia ser tratado melhor.

Você pode elaborar exatamente o que tem em mente? Quando você diz que está executando em segundo plano, quer dizer simplesmente manter seu aplicativo na memória, mesmo que ele não esteja atualmente na tela? Você já pensou em usar os Serviços como uma maneira mais persistente de gerenciar seu aplicativo quando ele não está em foco?

Dan
fonte
No android, temos uma configuração chamada "Dados em segundo plano". Essa configuração ativa qualquer conexão de dados em segundo plano quando o aplicativo está sendo executado em segundo plano. Desejo implementar a alternância "Dados em segundo plano" para meu aplicativo. Portanto, quando nenhuma de minhas atividades estiver visível para o usuário, gostaria que meu serviço parasse de fazer qualquer transferência de dados, mas, no momento em que uma de minhas atividades for retomada, gostaria de retomar a transferência de dados
cppdev 08/09
1
Applicationnão tem onPause()ou onResume().
CommonsWare
1
@CommonsWare Você está certo, eu estava me referindo a cada atividade individual que entra em contato com o aplicativo em sua pausa / resumo. Essa é basicamente a idéia que você acabou de compartilhar no comentário à sua resposta, embora tenha usado os Serviços que, imagino, são uma jogada mais inteligente.
Dan
2

Fiz minha própria implementação do ActivityLifecycleCallbacks. Estou usando SherlockActivity, mas para a classe Activity normal pode funcionar.

Primeiro, estou criando uma interface que possui todos os métodos para rastrear o ciclo de vida das atividades:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

Segundo, implementei essa interface na classe do meu aplicativo:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

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

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

Terceiro, estou criando uma classe que se estende do SherlockActivity:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

Quarto, todas as classes que se estendem do SherlockActivity, substituí pelo MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

Agora, no logcat, você verá os logs programados na implementação da interface feita no MyApplication.

ClarkXP
fonte
1

A atividade é pausada quando um diálogo fica acima dele, portanto todas as soluções recomendadas são meias soluções. Você precisa criar ganchos para diálogos também.

konmik
fonte
1

Documentos oficiais:

O sistema distingue entre aplicativos em primeiro plano e em segundo plano. (A definição de plano de fundo para fins de limitações de serviço é distinta da definição usada pelo gerenciamento de memória; um aplicativo pode estar em segundo plano no que diz respeito ao gerenciamento de memória , mas em primeiro plano no que diz respeito à sua capacidade de iniciar serviços.) considerado em primeiro plano se alguma das seguintes situações for verdadeira:

  1. Tem uma atividade visível, seja a atividade iniciada ou pausada.
  2. Tem um serviço em primeiro plano.
  3. Outro aplicativo em primeiro plano está conectado ao aplicativo, vinculando-se a um de seus serviços ou utilizando um de seus provedores de conteúdo. Por exemplo, o aplicativo está em primeiro plano se outro aplicativo se vincular a:
    • IME
    • Serviço de papel de parede
    • Ouvinte de notificação
    • Serviço de voz ou texto

Se nenhuma dessas condições for verdadeira, o aplicativo será considerado em segundo plano.

Uddhav Gautam
fonte
0

Outra solução para este post antigo (para aqueles que podem ajudar):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}
alex
fonte
0

Veja o comentário na função onActivityDestroyed.

Funciona com o destino SDK versão 14>:

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}
ramden
fonte
0

Você deve usar uma preferência compartilhada para armazenar a propriedade e agir de acordo com ela usando a ligação de serviço de suas atividades. Se você usar apenas a ligação (que nunca usa startService), seu serviço será executado apenas quando você se vincular a ele (bind onResume e unbind onPause) que o executaria apenas em primeiro plano e se desejar trabalhar em Em segundo plano, você pode usar o serviço de parada de partida regular.

Ofek Ron
fonte
0

Eu acho que essa pergunta deveria ser mais clara. Quando? Onde? Qual é a sua situação específica que você deseja saber se o aplicativo estiver em segundo plano?

Acabei de apresentar minha solução do meu jeito.
Consigo fazer isso usando o campo "importância" da RunningAppProcessInfoclasse no onStopmétodo de todas as atividades do meu aplicativo, o que pode ser alcançado simplesmente fornecendo uma BaseActivityextensão para outras atividades que implementam o onStopmétodo para verificar o valor de "importância". Aqui está o código:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}
wxf04125
fonte
Esta não é uma solução recomendada, como indicado na resposta do @ Idolon.
CoolMind
0

Eu recomendo a leitura desta página: http://developer.android.com/reference/android/app/Activity.html

Em suma, sua atividade não é mais visível após a onStop()chamada.

Chave
fonte
3
Eu tenho cerca de 10 atividades no meu aplicativo. Então, eu quero saber se nenhum deles está visível para o usuário. Em todos, eu quero saber se meu aplicativo como um todo está sendo executado em segundo plano
cppdev
Então, você acompanha todos os 10-ish. Ou, como o CommonsWare sugeriu, explique o que você está tentando fazer.
Key
3
Isto não está correto. Sua atividade é visível até onStop; entre onPausee onStopé visível , mas não em primeiro plano .
nickgrim
@ Rickgrim: o que não está correto? Afirmei que uma atividade não é mais visível depois que onStop()é chamada, alinhada com o que você escreveu.
Key
@ Key: Você disse originalmente até onPauseé chamado: uma edição recente o corrigiu.
nickgrim 20/05
0

Que tal usar getApplicationState (). IsInForeground ()?

Jonathan
fonte
0

Na minha opinião, muitas respostas introduzem uma carga pesada de código e traz muita complexidade e falta de legibilidade.

Quando as pessoas perguntam sobre como se comunicar entre a Servicee a Activity, geralmente aconselho usar o LocalBroadcastManager .


Por quê?

Bem, citando os documentos:

  • Você sabe que os dados que você está transmitindo não sairão do aplicativo, portanto, não se preocupe com o vazamento de dados particulares.

  • Não é possível que outros aplicativos enviem essas difusões para seu aplicativo, portanto, você não precisa se preocupar em ter falhas de segurança que eles possam explorar.

  • É mais eficiente do que enviar uma transmissão global através do sistema.

Não está nos documentos:

  • Não requer bibliotecas externas
  • O código é mínimo
  • É rápido de implementar e entender
  • Nenhum retorno de chamada personalizado auto-implementado / ultra-singleton / padrão intra-processo, qualquer que seja ...
  • Há fortes referências sobre Activity, Application...

Descrição

Portanto, você deseja verificar se algum dos itens Activityestá atualmente em primeiro plano. Você costuma fazer isso em uma aula Serviceou na sua Applicationclasse.

Isso significa que seus Activityobjetos se tornam o remetente de um sinal (estou ligado / desligado). Seu Service, por outro lado, se torna o Receiver.

Existem dois momentos em que seuActivity você diz se está indo em primeiro plano ou em segundo plano (sim, apenas dois ... não 6).

Quando Activityentra em primeiro plano, o onResume()método é acionado (também chamado apósonCreate() ).

Quando Activityvai pelas costas,onPause() é chamado.

Estes são os momentos em que você Activitydeve enviar o sinal para o seuService para descrever seu estado.

No caso de múltiplos Activity, lembre-se doActivity se de que o primeiro entra em segundo plano e depois outro entra em primeiro plano.

Então a situação seria: *

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

O Service/ Applicationcontinuará ouvindo esses sinais e agirá de acordo.


Código (TLDR)

Você Servicedeve implementar um BroadcastReceiverpara ouvir sinais.

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Registre o ReceiveremService::onCreate()

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Cancele o registro em Service::onDestroy()

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

Agora o seu Activitydeve comunicar o estado deles.

No Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

No Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Uma situação muito, muito comum

Desenvolvedor: desejo enviar dados meus Servicee atualizar o Activity. Como verifico se o Activityestá em primeiro plano?

Geralmente, não é necessário verificar se Activityo item está em primeiro plano ou não. Basta enviar os dados via LocalBroadcastManagerdo seu Service. Se Activityestiver ativado, ele responderá e agirá.

Para esta situação muito comum, o Servicetorna-se o remetente e o Activityimplementa o BroadcastReceiver.

Então, crie um Receiverno seu Activity. Registre-o onResume()e cancele o registro onPause(). Não há necessidade de usar os outros métodos de ciclo de vida .

Defina o Receivercomportamento em onReceive()(atualize o ListView, faça isso, faça aquilo, ...).

Dessa forma, o Activityouvirá apenas se estiver em primeiro plano e nada acontecerá se estiver nas costas ou for destruído.

No caso de múltiplos Activity, o que Activityestiver ativado responderá (se eles também implementarem o Receiver).

Se tudo estiver em segundo plano, ninguém responderá e o sinal simplesmente se perderá.

Envie os dados da Servicevia Intent(consulte o código acima) especificando o ID do sinal.


Marko Pacak
fonte
0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}
ayoub laaziz
fonte
0

Nenhuma das respostas se encaixava perfeitamente no caso específico se você procurasse saber se uma atividade específica está em andamento e se você é um SDK sem acesso direto ao Aplicativo. Para mim, eu estava no segmento de segundo plano, tendo recebido apenas uma notificação por push para uma nova mensagem de bate-papo e só quero exibir uma notificação do sistema se a tela de bate-papo não estiver em primeiro plano.

Usando o ActivityLifecycleCallbacksque foi recomendado em outras respostas, criei uma pequena classe util que abriga a lógica para MyActivityestar ou não em primeiro plano.

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}

scottyab
fonte
-1

Nas minhas atividades onResume e onPause, escrevo um booleano isVisible em SharedPrefences.

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

E leia em outro lugar quando necessário via,

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

Talvez não seja elegante, mas funciona para mim ...

mtwagner
fonte
-1

Talvez seja tarde demais para responder, mas se alguém vier visitar, aqui está a solução que eu sugiro: o (s) motivo (s) pelo qual um aplicativo deseja saber que está em segundo plano ou que está em primeiro plano pode ser muitos, alguns são 1. Para mostrar brindes e notificações quando o usuário estiver no BG. 2.Para executar algumas tarefas pela primeira vez, o usuário vem do BG, como uma enquete, redesenho etc.

A solução de Idolon e outros cuida da primeira parte, mas não da segunda. Se houver várias atividades no seu aplicativo e o usuário estiver alternando entre elas, quando você estiver na segunda atividade, o sinalizador visível será falso. Portanto, não pode ser usado deterministicamente.

Fiz algo que foi sugerido pelo CommonsWare: "Se o Serviço determinar que não há atividades visíveis e permanecer assim por algum tempo , pare a transferência de dados no próximo ponto de parada lógica".

A linha em negrito é importante e isso pode ser usado para alcançar o segundo item. Portanto, o que eu faço é quando obtenho o onActivityPaused (), não altere o visible para false diretamente, em vez disso, tenho um temporizador de 3 segundos (é o máximo que a próxima atividade deve ser iniciada) e, se não houver onActivityResumed ( ) nos próximos 3 segundos, mude de visível para falso. Da mesma forma em onActivityResumed () se houver um timer, eu o cancelo. Para resumir, o visível se torna isAppInBackground.

Desculpe, não é possível copiar e colar o código ...

Sriram
fonte
-3

Eu gostaria de recomendar que você use outra maneira de fazer isso.

Eu acho que você deseja mostrar a tela de inicialização enquanto o programa está sendo iniciado, se já estiver sendo executado no back-end, não o mostre.

Seu aplicativo pode gravar continuamente a hora atual em um arquivo específico. Enquanto o aplicativo está sendo iniciado, verifique o último registro de data e hora, se current_time-last_time> o período especificado para a gravação do horário mais recente, significa que o aplicativo foi parado, interrompido pelo sistema ou pelo próprio usuário.

user2322866
fonte