Por plano de fundo, quero dizer que nenhuma das atividades do aplicativo está atualmente visível para o usuário?
Por plano de fundo, quero dizer que nenhuma das atividades do aplicativo está atualmente visível para o usuário?
Respostas:
Existem algumas maneiras de detectar se seu aplicativo está sendo executado em segundo plano, mas apenas uma delas é totalmente confiável:
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.
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 deRunningAppProcessInforegistros. Para determinar se sua aplicação é no cheque primeiro planoRunningAppProcessInfo.importancecampo de igualdade paraRunningAppProcessInfo.IMPORTANCE_FOREGROUNDquandoRunningAppProcessInfo.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ânciaIMPORTANCE_FOREGROUNDà sua tarefa, independentemente de estar realmente em primeiro plano ou não. Chame-o no thread de segundo plano (por exemplo, viaAsyncTask) 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.
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.
OnStoppedido de isActivityVisible.
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.
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() .
onStop()depois super.onStop(). Não verifique se há antecedentes onPause().
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
companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }então você pode obter o estado de primeiro plano com #ArchLifecycleApp.isForeground()
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?
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);
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;
JobServicepara detectar que o serviço está sendo executado em segundo plano.
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
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.
onPause, onStopnem o onResumeevento é chamado. Então, o que você faz se nenhum desses eventos for acionado ?!
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 ActivityBonStart()serão chamados antes de ActivityAonStop().
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:
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.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().
if(Utilities.isApplicationOnBackground()) …para Utilities. Porque, caso contrário, apenas uma atividade específica reagirá ao evento.
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.
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.
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 )
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
}
}
}
Com base no @Cornstalks, responda para incluir alguns recursos úteis.
Recursos extras:
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;
}
}
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.
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 única solução correta:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
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) {
...
}
}
}
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?
Applicationnão tem onPause()ou onResume().
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.
Como ainda não foi mencionado, vou sugerir aos leitores que explorem o ProcessLifecycleOwner disponível através dos componentes da Arquitetura Android
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:
Se nenhuma dessas condições for verdadeira, o aplicativo será considerado em segundo plano.
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();
}
}
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++;
}
}
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.
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;
}
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.
onStop; entre onPausee onStopé visível , mas não em primeiro plano .
onStop()é chamada, alinhada com o que você escreveu.
onPauseé chamado: uma edição recente o corrigiu.
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:
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 oActivity. Como verifico se oActivityestá 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.
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
}
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
}
}
}
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 ...
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 ...
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.