检查Android应用程序是否在后台运行


329

在背景上,我的意思是用户当前看不到应用程序的任何活动?



7
我在这里很困惑..为什么Android不能为此在应用程序类上提供简单的覆盖?在平台级别知道这一点太难了吗?@Override受保护的void onApplicationSentToBackground(){}
Chuck D

2
@ChuckD-这很有道理,这是Android SDK有时想要避免的做法。:/
Mark


1
iOS对此一无所知,不知道为什么Google会这么难。如此明显的需求。
杰里·德斯特伦斯

Answers:


388

有几种方法可以检测您的应用程序是否在后台运行,但是只有其中一种是完全可靠的:

  1. 正确的解决方案(学分去CommonsWareNeTeInStEiN
    使用自己的应用程序的轨道知名度Activity.onPauseActivity.onResume方法。将“可见性”状态存储在其他一些类中。好的选择是您自己实现ApplicationService(如果您想从服务中检查活动可见性,则此解决方案也有一些变体)。
     
    示例
    实现自定义Application类(注意isActivityVisible()静态方法):

    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;
    }

    在以下位置注册您的应用程序类AndroidManifest.xml

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

    在项目中的每个对象上添加onPause和(如果需要,您可以为“活动”创建一个共同的祖先,但是如果您的活动已经从/ 等进行了扩展,则仍然需要手动编写以下内容):onResumeActivityMapActivityListActivity

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

     
    更新
    ActivityLifecycleCallbacks已在API级别14(Android 4.0)中添加。您可以使用它们来跟踪用户当前是否可以看到应用程序的活动。有关详细信息,请查看下面的 Cornstalks答案


  2. 我曾经建议以下解决方案有

    您可以检测当前的前台/后台应用程序,ActivityManager.getRunningAppProcesses()并使用该应用程序返回RunningAppProcessInfo记录列表。要确定您的应用程序是否在前台,请检查RunningAppProcessInfo.importance字段是否等于RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile RunningAppProcessInfo.processName与您的应用程序包名称相等。

    同样,如果您ActivityManager.getRunningAppProcesses()从应用程序UI线程进行调用,则IMPORTANCE_FOREGROUND无论它实际上是否在前台,它都会返回任务的重要性。在后台线程中调用它(例如通过AsyncTask),它将返回正确的结果。

    尽管此解决方案可能会起作用(并且实际上在大多数情况下都可以起作用),但我强烈建议不要使用它。这就是为什么。正如Dianne Hackborn写道

    这些API并不是应用程序用来建立UI流程的基础,而是用于向用户显示正在运行的应用程序,任务管理器等。

    是的,在内存中有一个列表保存这些事情。但是,它在另一个进程中处于关闭状态,由与您的线程分开运行的线程进行管理,而不是您可以依靠的东西(a)及时看到正确的决定,或者(b)在返回时保持一致。加上关于“下一步”活动去的决定总是在切换发生的那一刻完成,直到直到那个确切的点(活动状态被短暂锁定以进行切换),我们才做出决定。确切地知道下一步会是什么。

    并且这里的实现和全局行为不能保证将来保持不变。

    我希望我在SO上发布答案之前已经读过这篇文章,但是希望现在承认我的错误还为时不晚。

  3. 答案之一中提到的
    Droid-Fu库的另一种错误解决方案ActivityManager.getRunningTasks用于其isApplicationBroughtToBackground方法。请参阅上方Dianne的评论,也不要使用该方法。


4
要知道您是否按下主屏幕按钮或其他应用已引起关注:1)实施好的解决方案。2)OnStop应要求isActivityVisible
Brais Gabin

28
不幸的是,您的“正确”解决方案对我不起作用。考虑您在应用程序中的活动之间进行循环。然后发生的是您的“ inForeground”标志如下所示:True,False(在第一个活动的onPause和第二个活动的onResume之间)然后再次为True,等等。然后,您将需要某种滞后。
Radu 2012年

14
如果您无法直接控制所有活动,则此解决方案将不起作用。例如,如果您有来自第三方sdk的活动,甚至启动了ACTION_VIEW意向。
user123321 2013年

66
Android真是令人震惊。没有人认为有人可能想要保留应用程序级别的数据吗?休息一下

8
该问题的真正答案似乎是“您无法正确检查”。所谓的“正确”解决方案最多是一种解决方法,因此使用ActivityLifecycleCallbacks也是如此。您仍然需要考虑在将注册为“不在前台”的活动之间进行切换。令我震惊的是,您无法检查像这样的简单事情……
丝氨酸

263

请勿使用此答案

user1269737的答案是执行此操作的正确方法(已获得Google / Android批准)。去阅读他们的答案,并给他们+1。

为了后代的缘故,我将在这里保留原始答案。这是2012年提供的最好的软件,但现在Android对此已提供适当的支持。

原始答案

密钥正在使用ActivityLifecycleCallbacks(请注意,这需要Android API级别14(Android 4.0))。只需检查停止的活动数是否等于开始的活动数即可。如果它们相等,则说明您的应用程序正在后台运行。如果还有更多开始的活动,则您的应用程序仍然可见。如果恢复的活动多于暂停的活动,则您的应用程序不仅可见,而且位于前台。然后,您的活动可以处于3个主要状态:可见和在前台,可见但不在前台,不可见并且不在前台(即在后台)。

关于此方法的真正好处是它没有异步问题getRunningTasks(),但是您也不必修改Activity应用程序中的每个内容来设置/取消设置onResumed()/中的内容onPaused()。它仅包含几行代码,并且可以在整个应用程序中使用。另外,也不需要时髦的权限。

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());
    }
}

@Mewzer提出了有关此方法的一些好问题,我想在此答案中为所有人答复:

onStop()在内存不足的情况下不会被调用;这是一个问题吗?

否。该文档onStop()说:

请注意,在内存不足的情况下(系统的内存不足,无法在调用onPause()方法后保持活动的进程运行),该方法永远不会被调用。

这里的关键是“让您的活动的进程保持运行 ……”如果达到这种内存不足的情况,您的进程实际上将被杀死(而不仅仅是活动)。这意味着这种检查背景色的方法仍然有效,因为a)如果您的进程被杀死,则无论如何都无法检查背景,并且b)如果您的进程再次启动(因为创建了新活动),则该成员的变量(无论是否为静态)MyLifecycleHandler将重置为0

这对配置更改有效吗?

默认情况下,没有。您必须在清单文件中显式设置configChanges=orientation|screensize|使用任何其他所需内容)并处理配置更改,否则您的活动将被破坏并重新创建。如果未设置,则活动的方法将按以下顺序调用:onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume。如您所见,没有重叠(通常,在两个活动之间切换时,两个活动会非常短暂地重叠,这就是这种背景检测方法的工作原理)。为了解决这个问题,您必须进行设置,configChanges以免破坏您的活动。幸运的是,我不得不设置configChanges在我所有的项目中都已经存在,因为整个屏幕旋转/调整大小不希望破坏整个活动,所以我从来没有发现这是有问题的。(感谢dpimka刷新了我的记忆并纠正了我!)

一注:

当我在此答案中说“背景”时,我的意思是“您的应用不再可见”。Android活动是可见的,但不是在前台(例如,如果有透明的通知覆盖)。这就是为什么我更新了此答案以反映这一点的原因。

重要的是要知道,Android在切换没有任何前景的活动时会有一个奇怪的困境。因此,如果您在活动之间切换(在同一应用程序中)时检查应用程序是否在前台,则会提示您不在前台(即使您的应用程序仍是活动应用程序并且可见) )。

您可以检查您的应用是否在您的前景ActivityonPause()方法之后 super.onPause()。只要记住我刚才谈到的怪异的边缘状态即可。

您可以检查您的应用程序是可见的(即,如果它不是在后台)在你ActivityonStop()方法之后 super.onStop()


1
这看起来很有趣-但是在内存不足的情况下会发生什么?不保证将调用onStop()。我们是否可以陷入未调用onStop()并且停止的计数器不递增的情况-这意味着后台检查不再可靠吗?还是永远不会发生?
Mewzer

1
另外,这会忽略配置更改吗?还是由于配置更改(例如方向更改)而重新创建活动,该应用程序将被视为后台程序吗?抱歉,有问题,但我认为您正在研究某些东西,并且有兴趣知道它是否在这些极端情况下有效。
Mewzer

1
@Mewzer:我将以评论的方式进行回复,但是要获得这些答案需要一些打字,所以请稍后再检查,然后我将编辑我的答案。
Cornstalks 2012年

1
@Mewzer:您现在应该找到答案。让我知道是否还有其他问题!
Cornstalks 2012年

2
@Mewzer:我刚刚添加了一条您可能会感兴趣的注释。具体来说,请在onStop()after中检查背景super.onStop()。不要检查中的背景onPause()
Cornstalks 2012年

186

GOOGLE解决方案 -不同于以往的解决方案,不是骇客。使用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
    }
}

在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' }
    }
}

您可以在此处阅读有关Lifecycle相关架构组件的更多信息-https: //developer.android.com/topic/libraries/architecture/lifecycle


10
这绝对是正确的答案!它像魅力一样运作:D
JaviOverflow '18

2
效果很好,我也进行了一些修改,以便在此类之外companion object { private var foreground = false fun isForeground() : Boolean { return foreground } }可以更轻松地访问前景/背景状态:然后您可以使用ArchLifecycleApp.isForeground()
Jose Jet,

2
哦,老兄,这比我以前的回答好得多。向我+1。我更新了答案,使人们指向您的答案。
Cornstalks

2
尽管这是一个正确的答案,但无需实现回调,但是您可以随时查询ProcessLifecycleOwner。检查 stackoverflow.com/a/52678290/6600000
Keivan Esbati,

2
正如doc所说The LifecycleOwner for the whole application process. Note that if your application has multiple processes, this provider does not know about other processes. ,这不适用于multiple processes应用程序,我们可以优雅地实现一些api吗?
acntwww '19

23

从支持库版本26开始,您可以使用ProcessLifecycleOwner,只需按此处所述将其添加到您的依赖项中即可,例如:

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
}

然后ProcessLifecycleOwner只要需要的时候就查询应用状态,例如:

//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);

2
谢谢,这是最好和最简单的方法,特别是在使用fcm时,它适合于代码的任何部分。
Mihae Kheel

如果应用程序完全关闭,第一个方法将返回什么?
Evgeniy Mishustin,

@EvgeniyMishustin取决于应用程序的当前状态,但是通常您会看到CREATED然后DESTROYED,然后,您将不会收到任何新事件。
Keivan Esbati

那么,在后台(前景)中是否有任何“ IF”语句可以看到IF应用?
ekashking

@ekashking只是将整个语句放在if子句中。例如:if(ProcessLifecycleOwner.get()。getLifecycle()。getCurrentState()。isAtLeast(Lifecycle.State.STARTED))=>应用程序处于前台
Keivan Esbati

20

从Android API 16开始,有一种简单的方法可以检查应用程序是否在前台。它可能不是万无一失的,但是Android上没有任何方法是万无一失的。当您的服务从服务器接收更新并且必须决定是否显示通知时,此方法足够好使用(因为如果UI是前台,用户将在没有通知的情况下注意到更新)。

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

此代码应该在Service类还是其他类(例如Application类)中?非常感谢。
Woppi '16

..无论您想使用它作为最后一行,都是您要检查的布尔值。
AO_

这与AWS Android SDK用于推送通知的方法相同。
spakmad17年

请注意,“出于服务限制的目的,后台的定义不同于内存管理所使用的定义;与内存管理有关的应用程序可能在后台,而与其启动服务的能力有关的应用程序则在前台。) “ developer.android.com/about/versions/oreo/background.html
ARLabs

谢谢,这工作!我能够在中使用此代码JobService来检测该服务在后台运行。
Michael Osofsky '19

17

Idolon的答案容易出错,并且更复杂,尽管在这里重复检查Android应用程序是否在前台?在这里从后台任务或服务确定当前的前台应用程序

有一种更简单的方法:

所有活动都扩展BaseActivity上:

protected static boolean isVisible = false;

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


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

每当您需要检查是否有任何应用程序活动处于前台时,只需检查一下即可 isVisible()

要了解这种方法,请检查并排活动生命周期的以下答案:活动并排生命周期


3
Idolon's answer is error prone-很遗憾,我必须同意你的看法。根据Dianne Hackborn在Google网上论坛中的评论,我更新了答案。请检查以获取详细信息。
Idolon

2
也不是万无一失的解决方案。一种情况是,如果用户下拉通知面板,然后既没有onPauseonStop也不该onResume事件被调用。那么,如果这些事件均未触发,您该怎么办?


令人遗憾的是,但是当屏幕关闭时启动活动时,此代码会出错。在这种情况下,将onResume和onPause称为isVisible = false。
CoolMind

@CoolMind您能否解释一下在后台启动活动的用例是什么?
neteinstein's

11

我尝试了使用Application.ActivityLifecycleCallbacks和其他许多应用程序的推荐解决方案,但是它们没有按预期工作。感谢Sarge,我想出了一个非常简单明了的解决方案,下面将对此进行介绍。

解决方案的关键是要了解以下事实:如果我们有ActivityA和ActivityB,并从ActivityA调用ActivityB(而不是调用ActivityA.finish),那么onStart() ActivityA 之前调用ActivityB onStop()

这也是主要的区别onStop(),并onPause()认为没有在我读的文章只是提及。

因此,根据此活动的生命周期行为,您可以简单地算出执行了多少次onStart()onPause()得到了程序中调用的。请注意,对于每个 Activity程序,您都必须覆盖onStart()onStop(),以增加/减少用于计数的静态变量。下面是实现此逻辑的代码。请注意,我使用的是extends类Application,所以不要忘记Manifest.xml在Application标签内声明:android:name=".Utilities",尽管它也可以使用简单的自定义类来实现。

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--;
    }
}

现在我们的程序的每个活动,我们应该重写onStart()onStop()和递增/递减如下图所示:

@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();
}

按照这种逻辑,有两种可能的情况:

  1. stateCounter = 0 :停止的数量等于已启动的活动的数量,这意味着应用程序在后台运行。
  2. stateCounter > 0 :启动的数量大于停止的数量,这意味着应用程序在前台运行。

注意:stateCounter < 0这意味着已停止的活动多于开始的活动,这是不可能的。如果遇到这种情况,则意味着您没有按照应有的方式增加/减少计数器。

你准备好了。您应该要检查您的应用程序是否在内部背景中onStop()


我移动if(Utilities.isApplicationOnBackground()) …Utilities。因为否则,只有特定活动会对事件做出反应。
显示名称

10

除非您自己进行跟踪,否则无法确定您的任何活动是否可见。也许您应该考虑提出一个新的StackOverflow问题,以说明您要从用户体验中实现的目标,因此我们也许可以为您提供替代的实现思路。


2
在android中,我们有一个名为“背景数据”的设置。当应用程序在后台运行时,此设置将打开任何后台数据连接。我想为我的应用程序实现“背景数据”切换,所以当用户看不到我的任何活动时,我希望我的服务停止进行任何数据传输,但是当我的活动之一恢复时,我想恢复数据传输
cppdev

1
@cppdev:希望“数据传输”由进行Service。如果是这样,请让您的活动在服务出现或消失时通知服务。如果Service确定没有可见的活动,并且仍然保持这种状态一段时间,请在下一个逻辑停止点停止数据传输。是的,这将需要您每个活动的代码,但是现在,这是不可避免的AFAIK。
CommonsWare 2010年

1
如果要避免在所有活动之间复制粘贴通用代码,则可以创建一个MyActivityClass继承Activity并实现生命周期方法的类,并使所有活动都继承自MyActivityClass。这会为没有工作PreferenceActivityMapActivity虽然(见这个问题
纪尧姆Brunerie

@CommonsWare我曾使用OnPause()OnResume()尝试过它是否处于活动状态,但是,如果我的应用程序在后台运行,则无法在视图屏幕中查看–如何检查它是否处于活动状态
Manoj 2014年

@CommonsWare我曾使用OnPause()OnResume()尝试过它是否处于活动状态,但是,如果我的应用程序在后台运行,则无法在视图屏幕中查看–如何检查它是否处于活动状态
Manoj 2014年

5

您可以使用ComponentCallbacks2来检测应用程序是否在后台。顺便说一句,此回调在API级别14(冰淇淋三明治)及更高版本可用

您将调用该方法:

public abstract void onTrimMemory (int level)

如果级别是,ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN则该应用程序处于后台。

您可以实现此接口的activityservice等等。

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
     }
   }
}

1
尝试过您的答案,但不那么可靠。当屏幕锁定或按“电源”按钮锁定屏幕时,不会触发onTrimMemory回调。如果您的应用程序可见并且您通过状态栏通知打开另一个应用程序,它也不会总是返回TRIM_MEMORY_UI_HIDDEN。唯一可靠的解决方案是实现ActivityLifecycleCallbacks并将其调整为用例。
velval '16

4

在@Cornstalks答案的基础上,包含几个有用的功能。

额外功能:

  • 引入的单例模式,因此您可以在应用程序中的任何位置执行此操作:AppLifecycleHandler.isApplicationVisible()和AppLifecycleHandler.isApplicationInForeground()
  • 添加了对重复事件的处理(请参见注释//对可见性的更改采取一些措施,//对前台的更改采取一些措施)

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;
    }
}

3

我想出的最好的解决方案是使用计时器。

您已经在onPause()中启动了一个计时器,并在onResume()中取消了同一计时器,该计时器有1个实例(通常在Application类中定义)。计时器本身设置为在2秒钟后(或您认为合适的任何间隔)运行一次Runnable,当计时器触发时,您将标志设置为将应用程序标记为处于后台。

在取消计时器之前,可以在onResume()方法中查询后台标志以执行任何启动操作(例如,开始下载或启用定位服务)。

此解决方案使您可以在后台进行多个活动,并且不需要任何许可即可实现。

如果您也使用事件总线,则此解决方案效果很好,因为您的计时器可以简单地触发事件,并且应用的各个部分都可以相应地做出响应。


我开始认为这是最好的(虽然很不幸)解决方案
dhaag23

是的,这也是我管理过的最好的解决方案。当应用程序未显示时,我需要停止蓝牙扫描,但是不能仅仅使用onpause或停止或销毁程序,因为当用户在应用程序中浏览时,我不想一直停止并启动。
CaptRespect 2016年

3

如果您打开开发人员设置“不保留活动”-仅检查创建的活动数是不够的。您还必须检查isSaveInstanceState。我的自定义方法isApplicationRunning()检查android应用是否正在运行:

这是我的工作代码:

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) { }        

}

3

唯一正确的解决方案:

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

}

我不知道此解决方案如何在我关于活动(或片段)的IF语句中的一个简单问题上给我一个答案,无论我的应用程序是在后台还是在前台。“ IF”语句在哪里?
ekashking

2

为了了解CommonsWare和Key所说的内容,您可以扩展Application类,并让所有活动在其onPause / onResume方法上进行调用。这将使您知道哪些活动是可见的,但是可能可以更好地进行处理。

您能否详细说明您的想法?当您说在后台运行时,您的意思是即使您的应用程序当前不在屏幕上,也只是将其保留在内存中?您是否考虑过将服务作为一种更持久的方式来管理您的应用程序(当应用程序没有重点关注时)?


在android中,我们有一个名为“背景数据”的设置。当应用程序在后台运行时,此设置将打开任何后台数据连接。我想为我的应用程序实现“背景数据”切换,所以当用户看不到我的任何活动时,我希望我的服务停止进行任何数据传输,但是当我的活动之一恢复时,我想恢复数据传输
cppdev

1
Application没有onPause()onResume()
CommonsWare 2010年

1
@CommonsWare您说得对,我指的是每个单独的活动都在其暂停/恢复时与应用程序联系。尽管您使用了服务,但我认为这是更明智的选择,但这基本上就是您刚刚在答案注释中分享的想法。

2

我做了自己的ActivityLifecycleCallbacks实现。我正在使用SherlockActivity,但对于正常的Activity类可能会起作用。

首先,我正在创建一个接口,该接口具有用于跟踪活动生命周期的所有方法:

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);
}

其次,我在应用程序的类中实现了此接口:

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());
    }
}

第三,我正在创建一个继承自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);
    }   
}

第四,所有从SherlockActivity扩展的类,我都替换为MySherlockActivity:

public class MainActivity extends MySherlockActivity{

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

}

现在,在logcat中,您将看到My​​Application中实现的Interface实现中编程的日志。


1

当对话框上方出现活动时,活动将暂停,因此所有建议的解决方案均为半解。您还需要为对话框创建钩子。



1

官方文档:

该系统区分前台和后台应用程序。(背景的服务限制目的的定义是从由存储器管理中使用的定义不同的;一个应用程序可能是在后台涉及存储器管理,但在前景作为涉及它能够发射服务)。一个应用程序是如果满足以下任一条件,则视为处于前台:

  1. 无论活动是开始还是暂停,它都有一个可见的活动。
  2. 它具有前台服务。
  3. 另一个前台应用程序通过绑定到其服务之一或通过使用其内容提供商之一而连接到该应用程序。例如,如果另一个应用绑定到该应用,则该应用位于前台:
    • 墙纸服务
    • 通知侦听器
    • 语音或文字服务

如果所有这些条件都不成立,则认为该应用程序处于后台。


0

此旧帖子的另一种解决方案(适用于那些可能有帮助的帖子):


<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();
    }

}

0

请参阅onActivityDestroyed函数中的注释。

适用于SDK目标版本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++;
    }
}

0

您应该使用共享首选项来存储属性,并使用活动中的服务绑定对其进行操作。如果仅使用绑定(永远不要使用startService),则只有在绑定到服务时(绑定onResume和取消绑定onPause),服务才会运行,这将使其仅在前台运行,并且如果您要在后台可以使用常规的启停服务。


0

我认为这个问题应该更清楚。什么时候?哪里?如果您的应用程序在后台运行,您想知道什么具体情况?

我只是以自己的方式介绍我的解决方案。
我通过在我的应用程序RunningAppProcessInfo中每个活动的onStop方法中使用类的“重要性”字段来完成此操作,可以通过BaseActivity为其他活动提供扩展来实现该功能,onStop以实现检查“重要性”值的方法来简单地实现此目的。这是代码:

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;
}

如@Idolon的回答所述,这不是推荐的解决方案。
CoolMind

0

我建议通读此页: http //developer.android.com/reference/android/app/Activity.html

简而言之,onStop()调用之后,您的活动将不再可见。


3
我的应用程序中大约有10个活动。所以我想知道如果用户看不见它们。总之,我想知道我的应用程序是否整体在后台运行
cppdev

因此,您便可以追踪所有10道菜。或者,如CommonsWare所建议,说明您要执行的操作。
关键

3
这是不对的。您的活动是可见的,直到onStop; 在它之间onPauseonStop并且可见,但在前景中可见
nickgrim

@nickgrim:什么不正确?我说过,活动在onStop()调用后不再可见,这与您编写的内容一致。
关键

@Key:您最初说的onPause是直到被调用:最近的修改已纠正您。
nickgrim '16

0

使用getApplicationState()。isInForeground()怎么样?


0

在我看来,许多答案会带来大量代码负担,并带来许多复杂性和不可读取性。

当人们问SO Service和a 之间如何通信时Activity,我通常建议使用LocalBroadcastManager


为什么?

好吧,通过引用文档:

  • 您知道您正在广播的数据不会离开您的应用程序,因此无需担心泄漏私人数据。

  • 其他应用程序不可能将这些广播发送到您的应用程序,因此您无需担心会利用它们的安全漏洞。

  • 它比通过系统发送全局广播更有效。

不在文档中:

  • 它不需要外部库
  • 该代码是最少的
  • 快速实施和理解
  • 无需任何自定义的自我实现回调/超单身/进程内模式...
  • 没有强引用ActivityApplication...

描述

因此,您想检查Activity当前是否有任何前台。您通常在Service或您的Application班级中这样做。

这意味着,您的Activity对象成为信号的发送者(我打开/我关闭)。你Service,在另一方面,变成Receiver

两个时刻,您的Activity告诉您它是在前台还是在后台(是,只有两个,而不是6)。

Activity进入前台时,将onResume()触发该方法(也称为after onCreate())。

Activity后面走时,onPause()被称为。

这些是您Activity应该向您发送信号Service以描述其状态的时刻。

如果是多个Activity,请记住Activity先进入背景,然后再进入前景。

因此情况将是:*

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

Service/ Application只会继续监听这些信号,并采取相应的行动。


代码(TLDR)

Service必须实现一个BroadcastReceiver才能监听信号。

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

注册ReceiverService::onCreate()

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

取消注册 Service::onDestroy()

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

现在,您Activity的必须传达其状态。

Activity::onResume()

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

Activity::onPause()

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

非常非常常见的情况

开发人员:我想从我的设备发送数据Service并更新Activity。如何检查Activity

通常无需检查Activity前景是否在前台。只需通过发送数据LocalBroadcastManager从你的Service。如果Activity打开,则它将响应并采取行动。

对于这种非常常见的情况,Service成为发送者,并Activity实现BroadcastReceiver

因此,创建一个Receiver在你的Activity。在中注册,然后在中onResume()注销onPause()无需使用其他生命周期方法

ReceiveronReceive()(更新ListView,执行此操作,执行此操作,...)中定义行为。

这样,Activity仅当它在前景中时,它才会侦听,而如果它在后方或被破坏,则什么也不会发生。

如果有多个Activity,则无论哪个Activity启用(如果它们也实现Receiver)都会响应。

如果全部都在后台,则没人会响应,信号只会丢失。

通过指定信号ID 从Service通孔发送数据Intent(请参见上面的代码)。


  • 多窗口支持除外。这可能很棘手(如果需要请进行测试)...

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
}

0

如果您想了解特定活动是否在前景中,以及您是否是无法直接访问该应用程序的SDK,则没有一个答案适合特定情况。对我来说,我处于后台线程中,刚刚收到了有关新聊天消息的推送通知,并且只想在聊天屏幕不在前台时显示系统通知。

使用ActivityLifecycleCallbacks在其他答案中推荐的那个,我创建了一个小的util类,该类包含有关是否MyActivity在前景中的逻辑。

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
    }
}

}


-1

在onResume和onPause活动中,我为SharedPrefences写了一个isVisible布尔值。

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

并在需要时通过其他地方阅读,

    // 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)){...}

也许不优雅,但是对我有用。


-1

回答可能为时已晚,但是如果有人来访问,那么这就是我建议的解决方案,一个应用想要知道其处于后台或进入前台状态的原因可能很多,其中一些是1。当用户在BG中时显示敬酒和通知。2.要首次执行某些任务的用户来自BG,例如投票,重绘等。

Idolon和其他人的解决方案只涉及第一部分,而不适用于第二部分。如果您的应用中有多个活动,并且用户正在它们之间进行切换,那么当您进入第二个活动时,可见标志将为false。因此,不能确定地使用它。

我做了CommonsWare所建议的操作,“如果服务确定没有可见的活动,并且这种方式保持了一段时间,请在下一个逻辑停止点停止数据传输。”

粗线非常重要,可用于实现第二项。因此,我要做的就是一旦获得onActivityPaused(),不要直接将visible更改为false,而是要设置3秒的计时器(这是应该启动下一个活动的最大值),如果没有onActivityResumed( )在接下来的3秒钟内致电,将可见更改为false。同样,如果有计时器,则在onActivityResumed()中取消它。综上所述,可见变为isAppInBackground。

抱歉,无法复制粘贴代码...


-3

我建议您使用另一种方法来执行此操作。

我想您想在程序启动时显示启动屏幕,如果它已经在后端运行,请不要显示它。

您的应用程序可以将当前时间连续写入特定文件。在您的应用程序启动时,请检查最后一个时间戳,如果current_time-last_time>您指定的写入最新时间的时间范围,则意味着您的应用程序已停止,已被系统或用户杀死。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.