如何检测 Android 应用程序何时进入后台并返回前台

发布于 2024-10-07 06:09:30 字数 73 浏览 0 评论 0 原文

我正在尝试编写一个应用程序,当它在一段时间后返回前台时,该应用程序会执行特定的操作。有没有办法检测应用程序何时发送到后台或带到前台?

I am trying to write an app that does something specific when it is brought back to the foreground after some amount of time. Is there a way to detect when an app is sent to the background or brought to the foreground?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

豆芽 2024-10-14 06:09:30

2018 年:Android 通过生命周期组件原生支持此功能。

2018 年 3 月更新:现在有更好的解决方案。请参阅 ProcessLifecycleOwner。您将需要使用新的架构组件 1.1.0(目前最新),但它是专门为实现此目的而设计的。

在此答案中提供了一个简单的示例,但我写了一个示例应用关于它的博客文章

自从我在 2014 年写下这篇文章以来,出现了不同的解决方案。有些有效,有些被认为有效,但有缺陷(包括我的!),我们作为一个社区(Android)学会了承受后果,并为特殊情况编写了解决方法。

永远不要假设单个代码片段就是您正在寻找的解决方案,这种情况不太可能发生;更好的是,尝试理解它的作用以及为什么这样做。

正如这里所写,我从未真正使用过 MemoryBoss 类,它只是一段碰巧起作用的伪代码。

除非有充分的理由让您不使用新的架构组件(确实有一些组件,特别是如果您的目标是超旧的 api),那么就继续使用它们。它们远非完美,但 ComponentCallbacks2 也不是。

更新/注释(2015 年 11 月):人们提出了两条评论,首先是应该使用 >= 而不是 == 因为文档指出您不应该检查确切的值。这对于大多数情况来说都很好,但请记住,如果您关心当应用程序转到后台时做某事,则必须使用 == >并且还将其与其他解决方案(例如活动生命周期回调)结合起来,否则您可能无法获得您想要的效果。这个例子(这发生在我身上)是,如果您想在应用程序进入后台时使用密码屏幕锁定您的应用程序(例如 1Password,如果您熟悉的话),您可能会不小心如果您的内存不足并且突然测试 >= TRIM_MEMORY,请锁定您的应用,因为 Android 会触发 LOW MEMORY 调用,并且该调用的内存容量高于您的内存。所以要小心你测试的方式/内容。

此外,有些人询问如何检测您何时返回。

我能想到的最简单的方法解释如下,但由于有些人不熟悉它,所以我在这里添加一些伪代码。假设您的 类 BaseActivity extends Activity 中有 YourApplicationMemoryBoss 类(如果您没有,则需要创建一个)。

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

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

我推荐 onStart 因为对话框可以暂停活动,所以我敢打赌,如果您所做的只是显示全屏对话框,您不希望您的应用程序认为“它进入了后台”,但您的情况可能会有所不同。

仅此而已。 if 块中的代码将仅执行一次,即使您转到另一个 Activity,新的 Activity(也扩展了 BaseActivity)也会报告 wasInBackground< /code> 为 false,因此它不会执行代码,直到调用 onMemoryTrimmed 并将标志再次设置为 true

更新/注释(2015 年 4 月):在对这段代码进行所有复制和粘贴之前,请注意,我发现了几个实例,它可能不是 100% 可靠,并且必须组合起来 与其他方法配合以达到最佳效果。
值得注意的是,有两个已知的实例onTrimMemory回调不能保证被执行:

  1. 如果您的手机在您的应用程序可见时锁定屏幕(比如您的应用程序) nn 分钟后设备锁定),不会调用此回调(或并不总是),因为锁屏位于顶部,但您的应用程序仍在“运行”,尽管已被覆盖。

  2. 如果您的设备内存相对较低(并且处于内存压力下),操作系统似乎会忽略此调用并直接进入更关键的级别。

现在,根据您了解应用程序何时进入后台的重要性,您可能需要也可能不需要扩展此解决方案以及跟踪活动生命周期等。

只需牢记上述内容并拥有一支优秀的质量保证团队即可;)

更新结束

可能会晚一些,但冰淇淋三明治(API 14)及以上内容有一个可靠的方法

事实证明,当您的应用程序不再有可见的 UI 时,就会触发回调。您可以在自定义类中实现该回调,称为 ComponentCallbacks2< /a> (是的,有两个)。此回调仅在 API 级别 14(冰淇淋三明治)及更高版本中可用

您基本上会调用该方法:

public abstract void onTrimMemory (int level)

级别为 20 或更具体地说,

public static final int TRIM_MEMORY_UI_HIDDEN

我一直在测试它,它总是有效,因为级别 20 只是一个“建议”,您可能想要释放一些资源,因为您的应用程序不再可用可见的。

引用官方文档:

onTrimMemory(int) 的级别:进程一直在显示用户界面,但现在不再这样做。此时应释放 UI 的大量分配,以便更好地管理内存。

当然,您应该实现它以实际执行它所说的操作(清除在特定时间内未使用的内存,清除一些一直未使用的集合等。可能性是无限的(请参阅官方文档以了解其他可能的< em>更关键的级别)。

但是,有趣的是,操作系统告诉您:嘿,您的应用程序进入了后台!

正是您首先想知道的。

这 确定你什么时候回来?

这很简单,我确信你有一个“BaseActivity”,所以你可以使用你的 onResume() 来标记你回来的事实。当您实际收到对上述 onTrimMemory 方法的调用时,就会说您没有回来。

如果活动正在恢复,您就不会返回。 100% 的情况下,如果用户再次返回到后面,您将收到另一个 onTrimMemory() 调用,

您需要订阅您的 Activity(或者更好的是自定义类)。

保证您始终收到此消息的最简单方法是创建一个如下所示的简单类:

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

    @Override
    public void onLowMemory() {
    }

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

为了使用此类,请在您的应用程序实现中(您有一个,对吧?),执行以下操作

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

:您创建一个 Interface ,您可以向该 if 添加一个 else 并实现 ComponentCallbacks (不带 2) API 14 以下的任何内容。该回调只有 onLowMemory() 方法,并且当您转到后台时不会被调用,但您应该使用它来修剪内存。

现在启动您的应用程序并按主页。您的 onTrimMemory(final int level) 方法应该被调用(提示:添加日志记录)。

最后一步是从回调中取消注册。最好的地方可能是应用程序的 onTerminate() 方法,但是,该方法不会在真实设备上调用:

<前><代码>/**
* 此方法适用于模拟流程环境。它将
* 永远不会在生产 Android 设备上调用,其中进程是
* 通过简单地杀死它们来删除它们;无用户代码(包括此回调)
* 这样做时会被执行。
*/

因此,除非您确实遇到不想再注册的情况,否则您可以安全地忽略它,因为您的进程无论如何都会在操作系统级别死亡。

如果您决定在某个时候取消注册(例如,如果您为应用程序提供清理和终止的关闭机制),您可以这样做:

unregisterComponentCallbacks(mMemoryBoss);

就是这样。

2018: Android supports this natively through lifecycle components.

March 2018 UPDATE: There is now a better solution. See ProcessLifecycleOwner. You will need to use the new architecture components 1.1.0 (latest at this time) but it’s specifically designed to do this.

There’s a simple sample provided in this answer but I wrote a sample app and a blog post about it.

Ever since I wrote this back in 2014, different solutions arose. Some worked, some were thought to be working, but had flaws (including mine!) and we, as a community (Android) learned to live with the consequences and wrote workarounds for the special cases.

Never assume a single snippet of code is the solution you’re looking for, it’s unlikely the case; better yet, try to understand what it does and why it does it.

The MemoryBoss class was never actually used by me as written here, it was just a piece of pseudo code that happened to work.

Unless there’s valid reason for you not to use the new architecture components (and there are some, especially if you target super old apis), then go ahead and use them. They are far from perfect, but neither were ComponentCallbacks2.

UPDATE / NOTES (November 2015): People has been making two comments, first is that >= should be used instead of == because the documentation states that you shouldn't check for exact values. This is fine for most cases, but bear in mind that if you only care about doing something when the app went to the background, you will have to use == and also combine it with another solution (like Activity Lifecycle callbacks), or you may not get your desired effect. The example (and this happened to me) is that if you want to lock your app with a password screen when it goes to the background (like 1Password if you're familiar with it), you may accidentally lock your app if you run low on memory and are suddenly testing for >= TRIM_MEMORY, because Android will trigger a LOW MEMORY call and that's higher than yours. So be careful how/what you test.

Additionally, some people have asked about how to detect when you get back.

The simplest way I can think of is explained below, but since some people are unfamiliar with it, I'm adding some pseudo code right here. Assuming you have YourApplication and the MemoryBoss classes, in your class BaseActivity extends Activity (you will need to create one if you don't have one).

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

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

I recommend onStart because Dialogs can pause an activity so I bet you don't want your app to think "it went to the background" if all you did was display a full screen dialog, but your mileage may vary.

And that's all. The code in the if block will only be executed once, even if you go to another activity, the new one (that also extends BaseActivity) will report wasInBackground is false so it won't execute the code, until onMemoryTrimmed is called and the flag is set to true again.

UPDATE / NOTES (April 2015): Before you go all Copy and Paste on this code, note that I have found a couple of instances where it may not be 100% reliable and must be combined with other methods to achieve the best results.
Notably, there are two known instances where the onTrimMemory call back is not guaranteed to be executed:

  1. If your phone locks the screen while your app is visible (say your device locks after nn minutes), this callback is not called (or not always) because the lockscreen is just on top, but your app is still "running" albeit covered.

  2. If your device is relatively low on memory (and under memory stress), the Operating System seems to ignore this call and go straight to more critical levels.

Now, depending how important it's for you to know when your app went to the background, you may or may not need to extend this solution together with keeping track of the activity lifecycle and whatnot.

Just keep the above in mind and have a good QA team ;)

END OF UPDATE

It may be late but there's a reliable method in Ice Cream Sandwich (API 14) and Above.

Turns out that when your app has no more visible UI, a callback is triggered. The callback, which you can implement in a custom class, is called ComponentCallbacks2 (yes, with a two). This callback is only available in API Level 14 (Ice Cream Sandwich) and above.

You basically get a call to the method:

public abstract void onTrimMemory (int level)

The Level is 20 or more specifically

public static final int TRIM_MEMORY_UI_HIDDEN

I've been testing this and it always works, because level 20 is just a "suggestion" that you might want to release some resources since your app is no longer visible.

To quote the official docs:

Level for onTrimMemory(int): the process had been showing a user interface, and is no longer doing so. Large allocations with the UI should be released at this point to allow memory to be better managed.

Of course, you should implement this to actually do what it says (purge memory that hasn't been used in certain time, clear some collections that have been sitting unused, etc. The possibilities are endless (see the official docs for other possible more critical levels).

But, the interesting thing, is that the OS is telling you: HEY, your app went to the background!

Which is exactly what you wanted to know in the first place.

How do you determine when you got back?

Well that's easy, I'm sure you have a "BaseActivity" so you can use your onResume() to flag the fact that you're back. Because the only time you will be saying you're not back is when you actually receive a call to the above onTrimMemory method.

It works. You don't get false positives. If an activity is resuming, you're back, 100% of the times. If the user goes to the back again, you get another onTrimMemory() call.

You need to suscribe your Activities (or better yet, a custom class).

The easiest way to guarantee that you always receive this is to create a simple class like this:

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

    @Override
    public void onLowMemory() {
    }

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

In order to use this, in your Application implementation (you have one, RIGHT?), do something like:

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

If you create an Interface you could add an else to that if and implement ComponentCallbacks (without the 2) used in anything below API 14. That callback only has the onLowMemory() method and does not get called when you go to the background, but you should use it to trim memory.

Now launch your App and press home. Your onTrimMemory(final int level) method should be called (hint: add logging).

The last step is to unregister from the callback. Probably the best place is the onTerminate() method of your App, but, that method doesn't get called on a real device:

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

So unless you really have a situation where you no longer want to be registered, you can safety ignore it, since your process is dying at OS level anyway.

If you decide to unregister at some point (if you, for example, provide a shutdown mechanism for your app to clean up and die), you can do:

unregisterComponentCallbacks(mMemoryBoss);

And that's it.

谢绝鈎搭 2024-10-14 06:09:30

更新 2021 年 11 月

实际设置如下

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
    }
}

class AppLifecycleListener : DefaultLifecycleObserver {

    override fun onStart(owner: LifecycleOwner) { // app moved to foreground
    }

    override fun onStop(owner: LifecycleOwner) { // app moved to background
    }
}

依赖项

implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"

原始答案

ProcessLifecycleOwner 似乎也是一个有前途的解决方案。

当第一个活动通过这些事件时,ProcessLifecycleOwner 将调度 ON_STARTON_RESUME 事件。 ON_PAUSEON_STOP,事件将在最后一个活动通过后延迟进行调度。此延迟足够长,可以保证在活动因配置更改而被销毁并重新创建时 ProcessLifecycleOwner 不会发送任何事件。

实现可以像根据

class AppLifecycleListener : LifecycleObserver {

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

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

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

源代码,当前延迟值为700ms

使用此功能还需要依赖项

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

UPDATE November 2021

Actual setup is as follows

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
    }
}

class AppLifecycleListener : DefaultLifecycleObserver {

    override fun onStart(owner: LifecycleOwner) { // app moved to foreground
    }

    override fun onStop(owner: LifecycleOwner) { // app moved to background
    }
}

Dependencies

implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"

ORIGINAL ANSWER

ProcessLifecycleOwner seems to be a promising solution also.

ProcessLifecycleOwner will dispatch ON_START, ON_RESUME events, as a first activity moves through these events. ON_PAUSE, ON_STOP, events will be dispatched with a delay after a last activity passed through them. This delay is long enough to guarantee that ProcessLifecycleOwner won't send any events if activities are destroyed and recreated due to a configuration change.

An implementation can be as simple as

class AppLifecycleListener : LifecycleObserver {

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

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

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

According to source code, current delay value is 700ms.

Also using this feature requires the dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
七度光 2024-10-14 06:09:30

这是我设法解决这个问题的方法。它的工作前提是,使用活动转换之间的时间参考很可能提供足够的证据来证明应用程序是否已“后台运行”。

首先,我使用了一个 android.app.Application 实例(我们称之为 MyApplication),它有一个 Timer、一个 TimerTask、一个常量来表示从一个活动转换到另一个活动可以合理地花费的最大毫秒数(我去了值为 2s),以及一个布尔值来指示应用程序是否“在后台”:

public class MyApplication extends Application {

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

该应用程序还提供了两种用于启动和停止计时器/任务的方法:

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

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

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

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

    this.wasInBackground = false;
}

该解决方案的最后一部分是添加一个调用从所有活动的 onResume() 和 onPause() 事件中的每个方法,或者最好是在所有具体活动继承的基础活动中:

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

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

    myApp.stopActivityTransitionTimer();
}

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

因此,当用户只是在您的活动之间导航时在应用程序中,离开活动的 onPause() 会启动计时器,但几乎立即进入的新活动会在达到最大转换时间之前取消计时器。所以wasInBackground将是false

另一方面,当 Activity 从启动器进入前台、设备唤醒、结束电话通话等时,很可能计时器任务在此事件之前执行,因此设置了 wasInBackground

Here's how I've managed to solve this. It works on the premise that using a time reference between activity transitions will most likely provide adequate evidence that an app has been "backgrounded" or not.

First, I've used an android.app.Application instance (let's call it MyApplication) which has a Timer, a TimerTask, a constant to represent the maximum number of milliseconds that the transition from one activity to another could reasonably take (I went with a value of 2s), and a boolean to indicate whether or not the app was "in the background":

public class MyApplication extends Application {

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

The application also provides two methods for starting and stopping the timer/task:

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

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

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

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

    this.wasInBackground = false;
}

The last piece of this solution is to add a call to each of these methods from the onResume() and onPause() events of all activities or, preferably, in a base Activity from which all of your concrete Activities inherit:

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

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

    myApp.stopActivityTransitionTimer();
}

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

So in the case when the user is simply navigating between the activities of your app, the onPause() of the departing activity starts the timer, but almost immediately the new activity being entered cancels the timer before it can reach the max transition time. And so wasInBackground would be false.

On the other hand when an Activity comes to the foreground from the Launcher, device wake up, end phone call, etc., more than likely the timer task executed prior to this event, and thus wasInBackground was set to true.

回忆追雨的时光 2024-10-14 06:09:30

编辑:新的架构组件带来了一些有前途的东西: ProcessLifecycleOwner,参见@vokilam的回答


根据Google I/O 演讲

class YourApplication : Application() {

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

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

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

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

}

是的。我知道很难相信这个简单的解决方案有效,因为我们这里有很多奇怪的解决方案。

但还有希望。

Edit: the new architecture components brought something promising: ProcessLifecycleOwner, see @vokilam's answer


The actual solution according to a Google I/O talk:

class YourApplication : Application() {

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

}


class AppLifecycleTracker : Application.ActivityLifecycleCallbacks  {

  private var numStarted = 0

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

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

}

Yes. I know it's hard to believe this simple solution works since we have so many weird solutions here.

But there is hope.

瞎闹 2024-10-14 06:09:30

当应用程序进入后台并再次进入前台时,将调用 onPause()onResume() 方法。然而,它们也会在应用程序首次启动时和被终止之前被调用。您可以在Activity中阅读更多内容。

没有任何直接的方法可以在后台或前台获取应用程序状态,但即使我也遇到过这个问题,并通过 onWindowFocusChangedonStop 找到了解决方案。

有关更多详细信息,请查看此处 Android :在没有 getRunningTasks 或 getRunningAppProcesses 的情况下检测 Android 应用何时进入后台并返回前台的解决方案

The onPause() and onResume() methods are called when the application is brought to the background and into the foreground again. However, they are also called when the application is started for the first time and before it is killed. You can read more in Activity.

There isn't any direct approach to get the application status while in the background or foreground, but even I have faced this issue and found the solution with onWindowFocusChanged and onStop.

For more details check here Android: Solution to detect when an Android app goes to the background and come back to the foreground without getRunningTasks or getRunningAppProcesses.

粉红×色少女 2024-10-14 06:09:30

根据 Martín Marconcinis 的回答(谢谢!)我终于找到了一个可靠的(而且非常简单)的解决方案。

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

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

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

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

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

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

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

然后将其添加到 Application 类的 onCreate() 中

public class MyApp extends android.app.Application {

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

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

    }

}

Based on Martín Marconcinis answer (thanks!) I finally found a reliable (and very simple) solution.

public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

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

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

    @Override
    public void onActivityStarted(Activity activity) {
    }

    @Override
    public void onActivityResumed(Activity activity) {

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

    @Override
    public void onActivityPaused(Activity activity) {
    }

    @Override
    public void onActivityStopped(Activity activity) {
    }

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

    @Override
    public void onActivityDestroyed(Activity activity) {
    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {
    }

    @Override
    public void onLowMemory() {
    }

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

Then add this to your onCreate() of your Application class

public class MyApp extends android.app.Application {

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

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

    }

}
§对你不离不弃 2024-10-14 06:09:30

我们使用这种方法。它看起来太简单了,无法工作,但它在我们的应用程序中经过了充分测试,实际上在所有情况下都表现得非常好,包括通过“主页”按钮、“返回”按钮或屏幕锁定后进入主屏幕。尝试一下。

想法是,当在前台时,Android 总是在停止前一个活动之前启动新的活动。不能保证这一点,但这就是它的工作原理。顺便说一句,Flurry 似乎使用相同的逻辑(只是猜测,我没有检查,但它挂钩相同的事件)。

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

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

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

}

编辑:根据评论,我们还在更高版本的代码中移至 onStart() 。另外,我添加了超级调用,这是我最初的帖子中所缺少的,因为这更多的是一个概念而不是工作代码。

We use this method. It looks too simple to work, but it was well-tested in our app and in fact works surprisingly well in all cases, including going to home screen by "home" button, by "return" button, or after screen lock. Give it a try.

Idea is, when in foreground, Android always starts new activity just before stopping previous one. That's not guaranteed, but that's how it works. BTW, Flurry seems to use the same logic (just a guess, I didn't check that, but it hooks at the same events).

public abstract class BaseActivity extends Activity {

    private static int sessionDepth = 0;

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

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

}

Edit: as per comments, we also moved to onStart() in later versions of the code. Also, I'm adding super calls, which were missing from my initial post, because this was more of a concept than a working code.

尬尬 2024-10-14 06:09:30

如果您的应用程序由多个活动和/或堆叠活动(如选项卡栏小部件)组成,则覆盖 onPause() 和 onResume() 将不起作用。即,当开始一项新活动时,当前活动将在创建新活动之前暂停。完成(使用“后退”按钮)活动时也是如此。

我发现两种方法似乎可以按需要工作。

第一个需要 GET_TASKS 权限,由一个简单的方法组成,该方法通过比较包名称来检查设备上运行最多的活动是否属于应用程序:

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

    return false;
}

该方法可以在 Droid-Fu(现在称为 Ignition)框架中找到。

我自己实现的第二种方法不需要 GET_TASKS 权限,这很好。相反,它的实施起来稍微复杂一些。

在 MainApplication 类中,您有一个变量用于跟踪应用程序中正在运行的活动的数量。在每个活动的 onResume() 中,您增加变量,在 onPause() 中,您减少变量。

当正在运行的活动数量达到 0 时,如果满足以下条件,则应用程序将进入后台:

  • 正在暂停的活动尚未完成(使用了“后退”按钮)。这可以通过使用方法 Activity.isFinishing() 来完成
  • 未启动新活动(相同的包名称)。您可以重写 startActivity() 方法来设置一个指示这一点的变量,然后在 onPostResume() 中重置它,这是创建/恢复活动时运行的最后一个方法。

当您可以检测到应用程序已退出后台时,也可以轻松检测到它何时返回前台。

If your app consists of multiple activites and/or stacked activites like a tab bar widget, then overriding onPause() and onResume() will not work. I.e when starting a new activity the current activites will get paused before the new one is created. The same applies when finishing (using "back" button) an activity.

I've found two methods that seem to work as wanted.

The first one requires the GET_TASKS permission and consists of a simple method that checks if the top running activity on the device belongs to application, by comparing package names:

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

    return false;
}

This method was found in the Droid-Fu (now called Ignition) framework.

The second method that I've implemented my self does not require the GET_TASKS permission, which is good. Instead it is a little more complicated to implement.

In you MainApplication class you have a variable that tracks number of running activities in your application. In onResume() for each activity you increase the variable and in onPause() you decrease it.

When the number of running activities reaches 0, the application is put into background IF the following conditions are true:

  • The activity being paused is not being finished ("back" button was used). This can be done by using method activity.isFinishing()
  • A new activity (same package name) is not being started. You can override the startActivity() method to set a variable that indicates this and then reset it in onPostResume(), which is the last method to be run when an activity is created/resumed.

When you can detect that the application has resigned to the background it is easy detect when it is brought back to foreground as well.

安人多梦 2024-10-14 06:09:30

创建一个扩展Application。然后我们可以在其中使用它的重写方法onTrimMemory()

要检测应用程序是否进入后台,我们将使用:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }

Create a class that extends Application. Then in it we can use its override method, onTrimMemory().

To detect if the application went to the background, we will use:

 @Override
    public void onTrimMemory(final int level) {
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
            // Get called every-time when application went to background.
        } 
        else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
        }
    }
老娘不死你永远是小三 2024-10-14 06:09:30

考虑使用 onUserLeaveHint。仅当您的应用程序进入后台时才会调用此函数。 onPause 将有特殊情况需要处理,因为它可能因其他原因而被调用;例如,如果用户在您的应用程序中打开另一个 Activity(例如您的设置页面),则您的主 Activity 的 onPause 方法将被调用,即使它们仍在您的应用程序中;当您可以简单地使用 onUserLeaveHint 回调来执行您所要求的操作时,跟踪正在发生的事情会导致错误。

当调用 UserLeaveHint 时,您可以将布尔 inBackground 标志设置为 true。当调用 onResume 时,如果设置了 inBackground 标志,则仅假设您返回前台。这是因为如果用户只是在您的设置菜单中并且从未离开过应用程序,onResume 也会在您的主要活动中被调用。

请记住,如果用户在设置屏幕中点击主页按钮,则 onUserLeaveHint 将在您的设置活动中调用,而当用户返回时, onResume 将在您的设置活动中调用。如果您的主要活动中只有此检测代码,您将错过此用例。要在所有活动中使用此代码而不重复代码,请拥有一个扩展 Activity 的抽象活动类,并将通用代码放入其中。然后你的每一个活动都可以扩展这个抽象活动。

例如:

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

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

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

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}

Consider using onUserLeaveHint. This will only be called when your app goes into the background. onPause will have corner cases to handle, since it can be called for other reasons; for example if the user opens another activity in your app such as your settings page, your main activity's onPause method will be called even though they are still in your app; tracking what is going in will lead to bugs when you can instead simply use the onUserLeaveHint callback which does what you are asking.

When on UserLeaveHint is called, you can set a boolean inBackground flag to true. When onResume is called, only assume you came back into the foreground if the inBackground flag is set. This is because onResume will also be called on your main activity if the user was just in your settings menu and never left the app.

Remember that if the user hits the home button while in your settings screen, onUserLeaveHint will be called in your settings activity, and when they return onResume will be called in your settings activity. If you only have this detection code in your main activity you will miss this use case. To have this code in all your activities without duplicating code, have an abstract activity class which extends Activity, and put your common code in it. Then each activity you have can extend this abstract activity.

For example:

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

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

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

public abstract MainActivity extends AbstractActivity {
    ...
}

public abstract SettingsActivity extends AbstractActivity {
    ...
}
羁客 2024-10-14 06:09:30

android.arch.lifecycle 包提供了类和接口,可让您构建生命周期感知组件

您的应用程序应该实现 LifecycleObserver 接口:

public class MyApplication extends Application implements LifecycleObserver {

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

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

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

为此,您需要将此依赖项添加到您的 build.gradle 文件中:

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

根据 Google 的建议,您应该尽量减少在 Activity 的生命周期方法中执行的代码:

一个常见的模式是实现依赖项的操作
活动和片段的生命周期方法中的组件。
然而,这种模式会导致代码组织不良并且
错误的扩散。通过使用生命周期感知组件,您
可以将依赖组件的代码移出生命周期方法
并进入组件本身。

您可以在这里阅读更多内容:
https://developer.android.com/topic/libraries/architecture/lifecycle

The android.arch.lifecycle package provides classes and interfaces that let you build lifecycle-aware components

Your application should implement the LifecycleObserver interface:

public class MyApplication extends Application implements LifecycleObserver {

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

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

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

To do that, you need to add this dependency to your build.gradle file:

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

As recommended by Google, you should minimize the code executed in the lifecycle methods of activities:

A common pattern is to implement the actions of the dependent
components in the lifecycle methods of activities and fragments.
However, this pattern leads to a poor organization of the code and to
the proliferation of errors. By using lifecycle-aware components, you
can move the code of dependent components out of the lifecycle methods
and into the components themselves.

You can read more here:
https://developer.android.com/topic/libraries/architecture/lifecycle

ぺ禁宫浮华殁 2024-10-14 06:09:30

ActivityLifecycleCallbacks 可能很有趣,但没有很好的文档记录。

不过,如果您调用 registerActivityLifecycleCallbacks()您应该能够在创建、销毁 Activity 等时获得回调。您可以调用 getComponentName() 代表活动。

ActivityLifecycleCallbacks might be of interest, but it isn't well documented.

Though, if you call registerActivityLifecycleCallbacks() you should be able to get callbacks for when Activities are created, destroyed, etc. You can call getComponentName() for the Activity.

把人绕傻吧 2024-10-14 06:09:30

在您的应用程序中添加回调并以如下方式检查根活动:

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

        @Override
        public void onActivityStarted(Activity activity) {
        }

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

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}

In your Application add the callback and check for root activity in a way like this:

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

        @Override
        public void onActivityStarted(Activity activity) {
        }

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

        @Override
        public void onActivityResumed(Activity activity) {
        }

        @Override
        public void onActivityPaused(Activity activity) {
        }

        @Override
        public void onActivityDestroyed(Activity activity) {
        }

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
                Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
                loadDefaults();
            }
        }
    });
}
何必那么矫情 2024-10-14 06:09:30

您可以使用 ProcessLifecycleOwner 将生命周期观察器附加到它。

  public class ForegroundLifecycleObserver implements LifecycleObserver {

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

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

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

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

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

然后在您的 Application 类的 onCreate() 上调用它:

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

通过它,您将能够捕获 ON_PAUSEON_STOP 的事件当您的应用程序进入后台时发生的情况。

You can use the ProcessLifecycleOwner attaching a lifecycle observer to it.

  public class ForegroundLifecycleObserver implements LifecycleObserver {

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

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

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

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

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

then on the onCreate() of your Application class you call this:

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

with this you will be able to capture the events of ON_PAUSE and ON_STOP of your application that happen when it goes in background.

女中豪杰 2024-10-14 06:09:30

我在 Github 上创建了一个项目 app-foreground-background-listen

创建一个 BaseActivity对于应用程序中的所有 Activity。

public class BaseActivity extends Activity {

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

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

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

        super.onStart();
    }

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

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

    public void onAppStart() {

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

        // Your code
    }

    public void onAppPause() {

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

        // Your code
    }
}

现在使用这个 BaseActivity 作为所有 Activity 的超类,就像 MainActivity 扩展 BaseActivity 一样,当您启动应用程序时,将调用 onAppStart ,当应用程序从任何屏幕进入后台时,将调用 onAppPause() 。

I have created a project on Github app-foreground-background-listen

Create a BaseActivity for all Activity in your application.

public class BaseActivity extends Activity {

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

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

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

        super.onStart();
    }

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

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

    public void onAppStart() {

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

        // Your code
    }

    public void onAppPause() {

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

        // Your code
    }
}

Now use this BaseActivity as a super class of all your Activity like MainActivity extends BaseActivity and onAppStart will be called when you start your application and onAppPause() will be called when the application goes the background from any screen.

御弟哥哥 2024-10-14 06:09:30

使用 ProcessLifecycleOwner

添加这些依赖项< /strong>

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

Kotlin 中:

class ForegroundBackgroundListener : LifecycleObserver {


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

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

然后在您的基本活动中:

override fun onCreate() {
        super.onCreate()

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

请参阅我关于此主题的文章:
https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48

This is pretty easy with ProcessLifecycleOwner

Add these dependencies

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

In Kotlin:

class ForegroundBackgroundListener : LifecycleObserver {


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

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

Then in your base activity:

override fun onCreate() {
        super.onCreate()

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

See my article on this topic:
https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48

遥远的绿洲 2024-10-14 06:09:30

没有简单的生命周期方法可以告诉您整个应用程序何时进入后台/前台。

我已经用简单的方法做到了这一点。按照以下说明检测应用程序后台/前台阶段。

通过一些解决方法,这是可能的。在这里,ActivityLifecycleCallbacks 来救援。让我逐步介绍一下。

  1. 首先,创建一个扩展 android.app.Application 并实现 ActivityLifecycleCallbacks 接口的类。在Application.onCreate()中,注册回调。

    公共类App扩展了Application实现 
        Application.ActivityLifecycleCallbacks {
    
        @覆盖
        公共无效 onCreate() {
            super.onCreate();
            注册活动生命周期回调(本);
        }
    }
    
  2. 在清单中注册“App”类,如下所示,


  3. 当应用处于前台时,至少有一个处于启动状态的 Activity;当应用处于后台时,不会有处于启动状态的 Activity。

    在“App”类中声明如下2个变量。

    私有 int ActivityReferences = 0;
    私有布尔 isActivityChangingConfigurations = false;
    

    activityReferences 将保留处于已启动状态的活动数量计数。 isActivityChangingConfigurations 是一个标志,指示当前 Activity 是否正在经历配置更改(如方向切换)。

  4. 使用以下代码,您可以检测应用程序是否进入前台。

    >
    <前><代码>@Override
    公共无效onActivityStarted(活动活动){
    if (++activityReferences == 1 && !isActivityChangingConfigurations) {
    // 应用程序进入前台
    }
    }

  5. 这是检测应用程序是否进入后台的方法。

    <前><代码>@Override
    公共无效onActivityStopped(活动活动){
    isActivityChangingConfigurations = Activity.isChangingConfigurations();
    if (--activityReferences == 0 && !isActivityChangingConfigurations) {
    // 应用程序进入后台
    }
    }

工作原理:

这是通过按顺序调用 Lifecycle 方法的方式完成的一个小技巧。让我演练一个场景。

假设用户启动App并启动Launcher Activity A。生命周期调用将是,

A.onCreate()

A.onStart() (++activityReferences == 1)(应用程序进入前台)

A.onResume()

现在 Activity A 启动 Activity B。

A.onPause()

B.onCreate()

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

B.onResume()

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

然后用户从 Activity B 导航回来,

B.onPause()

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

A.onResume()

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

B.onDestroy()

然后用户按下主页按钮,

A.onPause()

A.onStop() (--activityReferences == 0)(应用程序进入后台)

如果用户从 Activity B 按下 Home 按钮而不是 Back 按钮,仍然是相同的,activityReferences 将是<代码>0。因此,我们可以检测到App进入后台。

那么,isActivityChangingConfigurations 的作用是什么?在上面的场景中,假设 Activity B 改变了方向。回调顺序将是,

B.onPause()

B.onStop() (--activityReferences == 0)(应用程序进入后台??)

B.onDestroy()

B.onCreate()

B.onStart() (++activityReferences == 1)(应用程序进入前台??)

B.onResume()

这就是为什么我们要额外检查 isActivityChangingConfigurations 以避免 Activity 进行配置更改时出现这种情况。

There are no straightforward lifecycle methods to tell you when the whole Application goes background/foreground.

I have done this with simple way. Follow the below instructions to detect application background/foreground phase.

With a little workaround, it is possible. Here, ActivityLifecycleCallbacks comes to the rescue. Let me walk through step-by-step.

  1. First, create a class that extends the android.app.Application and implements the ActivityLifecycleCallbacks interface. In the Application.onCreate(), register the callback.

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
    
  2. Register the “App” class in the Manifest as below, <application android:name=".App".

  3. There will be at least one Activity in the started state when the app is in the foreground and there will be no Activity in the started state when the app is in the background.

    Declare 2 variables as below in the “App” class.

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

    activityReferences will keep the count of number of activities in the started state. isActivityChangingConfigurations is a flag to indicate if the current Activity is going through configuration change like an orientation switch.

  4. Using the following code you can detect if the App comes foreground.

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
    
  5. This is how to detect if the App goes background.

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

How it works:

This is a little trick done with the way the Lifecycle methods are called in sequence. Let me walkthrough a scenario.

Assume that the user launches the App and the Launcher Activity A is launched. The Lifecycle calls will be,

A.onCreate()

A.onStart() (++activityReferences == 1) (App enters Foreground)

A.onResume()

Now Activity A starts Activity B.

A.onPause()

B.onCreate()

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

B.onResume()

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

Then the user navigates back from Activity B,

B.onPause()

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

A.onResume()

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

B.onDestroy()

Then the user presses Home button,

A.onPause()

A.onStop() (--activityReferences == 0) (App enters Background)

In case, if the user presses Home button from Activity B instead of Back button, still it will be the same and activityReferences will be 0. Hence, we can detect as the App entering Background.

So, what’s the role of isActivityChangingConfigurations? In the above scenario, suppose the Activity B changes the orientation. The callback sequence will be,

B.onPause()

B.onStop() (--activityReferences == 0) (App enters Background??)

B.onDestroy()

B.onCreate()

B.onStart() (++activityReferences == 1) (App enters Foreground??)

B.onResume()

That’s why we have an additional check of isActivityChangingConfigurations to avoid the scenario when the Activity is going through the Configuration changes.

2024-10-14 06:09:30

您可以使用:

受保护的void onRestart()

区分新启动和重新启动。

输入图像描述这里

You can use:

protected void onRestart ()

To differ between new starts and restarts.

enter image description here

○愚か者の日 2024-10-14 06:09:30

编辑2:我下面写的内容实际上不会起作用。 Google 拒绝了包含对 ActivityManager.getRunningTasks() 调用的应用程序。从 文档 可以明显看出, API 仅用于调试和开发目的。一旦我有时间用一个使用计时器并且几乎同样好的新方案更新下面的 GitHub 项目,我就会更新这篇文章。

编辑1:我写了一篇博客文章 并创建了一个简单的 GitHub 存储库,使这一切变得非常简单。

接受的和评价最高的答案都不是真正的最佳方法。评分最高的答案的 isApplicationBroughtToBackground() 实现不能处理应用程序的主 Activity 屈服于同一应用程序中定义的活动的情况,但它具有不同的 Java 包。我想出了一种在这种情况下可行的方法。

在 onPause() 中调用此函数,它会告诉您应用程序是否由于另一个应用程序已启动或用户按下了主页按钮而进入后台。

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

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}

Edit 2: What I've written below will not actually work. Google has rejected an app that includes a call to ActivityManager.getRunningTasks(). From the documentation, it is apparent that this API is for debugging and development purposes only. I'll be updating this post as soon as I have time to update the GitHub project below with a new scheme that uses timers and is almost as good.

Edit 1: I've written up a blog post and created a simple GitHub repository to make this really easy.

The accepted and top rated answer are both not really the best approach. The top rated answer's implementation of isApplicationBroughtToBackground() does not handle the situation where the Application's main Activity is yielding to an Activity that is defined in the same Application, but it has a different Java package. I came up with a way to do this that will work in that case.

Call this in onPause(), and it will tell you if your application is going into the background because another application has started, or the user has pressed the home button.

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

  // Check the top Activity against the list of Activities contained in the Application's package.
  if (!tasks.isEmpty()) {
    ComponentName topActivity = tasks.get(0).topActivity;
    try {
      PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
      for (ActivityInfo activityInfo : pi.activities) {
        if(topActivity.getClassName().equals(activityInfo.name)) {
          return false;
        }
      }
    } catch( PackageManager.NameNotFoundException e) {
      return false; // Never happens.
    }
  }
  return true;
}
软甜啾 2024-10-14 06:09:30

您可以简单地在应用程序类中调用此方法

ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                Log.e(TAG, "onStateChanged: " + event.toString());
            }
        });

Lifecycle.Event 将简单地返回应用程序的状态,

ON_CREATE
ON_START
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
ON_ANY

它将返回 ON_PAUSE & ON_STOP 当应用程序进入后台时
并将返回 ON_START &当应用程序到达前台时 ON_RESUME

you can simply call this method in your application class

ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                Log.e(TAG, "onStateChanged: " + event.toString());
            }
        });

Lifecycle.Event will simply return the state of the application

ON_CREATE
ON_START
ON_RESUME
ON_PAUSE
ON_STOP
ON_DESTROY
ON_ANY

it will return ON_PAUSE & ON_STOP when the app goes to background
and will return ON_START & ON_RESUME when the app comes to the foreground

情仇皆在手 2024-10-14 06:09:30

我找到了一个很好的方法来检测应用程序是否进入前台或后台。
这是我的代码
希望这对您有帮助。

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

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

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

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

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

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

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

}

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

}

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

}

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

}

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

}

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

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

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

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

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

}

I found a good method to detect application whether enter foreground or background.
Here is my code.
Hope this help you.

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

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

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

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

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

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

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

}

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

}

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

}

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

}

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

}

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

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

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

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

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

}

冷心人i 2024-10-14 06:09:30

我将其与 Google Analytics EasyTracker 一起使用,并且它有效。它可以扩展为使用简单的整数来完成您想要的操作。

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

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

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

            }

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

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

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

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

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

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

I was using this with Google Analytics EasyTracker, and it worked. It could be extended to do what you seek using a simple integer.

public class MainApplication extends Application {

    int isAppBackgrounded = 0;

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

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

            }

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

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

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

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

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

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}
月下凄凉 2024-10-14 06:09:30

由于我没有找到任何方法,也可以在不检查时间戳的情况下处理旋转,所以我想我也分享了我们现在如何在我们的应用程序中执行此操作。
这个答案的唯一补充 https://stackoverflow.com/a/42679191/5119746 是,我们还采用方向考虑。

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

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

然后,对于回调,我们首先有简历:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

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

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

和 onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

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

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

        mAppIsInBackground = true
    }

然后,添加: 检查方向变化:

   override fun onConfigurationChanged(newConfig: Configuration) {

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

就是这样。希望这对某人有帮助:)

Since I did not find any approach, which also handles rotation without checking time stamps, I thought I also share how we now do it in our app.
The only addition to this answer https://stackoverflow.com/a/42679191/5119746 is, that we also take the orientation into consideration.

class MyApplication : Application(), Application.ActivityLifecycleCallbacks {

   // Members

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

Then, for the callbacks we have the resume first:

   // ActivityLifecycleCallbacks

   override fun onActivityResumed(activity: Activity?) {

      mResumed++

      if (mAppIsInBackground) {

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

         mAppIsInBackground = false
      }
      mOrientationWasChanged = false
    }

And onActivityStopped:

   override fun onActivityStopped(activity: Activity?) {

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

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

        mAppIsInBackground = true
    }

And then, here comes the addition: Checking for orientation changes:

   override fun onConfigurationChanged(newConfig: Configuration) {

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

That's it. Hope this helps someone :)

温柔戏命师 2024-10-14 06:09:30

此处正确答案

创建名为 MyApp 的类,如下所示:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

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

    private boolean isInBackground = false;

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


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


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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

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

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

然后,在您想要的任何地方(最好在应用程序中启动第一个活动)添加以下代码:

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

完成!现在,当应用程序在后台运行时,我们会收到日志 status : we are out
当我们进入应用程序时,我们会收到日志 status : we are out

Correct Answer here

Create class with name MyApp like below:

public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {

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

    private boolean isInBackground = false;

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


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


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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

        if(isInBackground){

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

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }

    @Override
    public void onConfigurationChanged(Configuration configuration) {

    }

    @Override
    public void onLowMemory() {

    }
}

Then, everywhere you want (better first activity launched in app), add the code below:

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

Done! Now when the app is in the background, we get log status : we are out
and when we go in app, we get log status : we are out

记忆之渊 2024-10-14 06:09:30

这是通过使用去抖动逻辑来确保我们不会获得连续的背景/前景事件的解决方案。因此,它总是反映背景/前景的稳定状态。

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import java.util.Timer
import java.util.TimerTask

/**
 * An observer class to listen on the app's lifecycle.
 */
class AppLifecycleObserver(
    private val onAppGoesToBackground: () -> Unit = {},
    private val onAppEntersForeground: () -> Unit = {}
) : LifecycleEventObserver {

    private val debounce = DebouncingTimer(timeout = 10)

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        debounce.refresh {
            when (event.targetState) {
                Lifecycle.State.CREATED -> onAppGoesToBackground()
                Lifecycle.State.RESUMED -> onAppEntersForeground()
                else -> Unit
            }
        }
    }

    fun attach() {
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    fun detach() {
        ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
    }

    private class DebouncingTimer(private val timeout: Long) {

        private var timer: Timer? = null

        fun refresh(job: () -> Unit) {
            timer?.cancel()
            timer = Timer()
            timer?.schedule(object : TimerTask() {
                override fun run() = job.invoke()
            }, timeout)
        }
    }
}

只需创建一个 AppLifecycleObserver 实例:

private val appLifecycleObserver = AppLifecycleObserver(
        onAppGoesToBackground = { // do whatever... },
        onAppEntersForeground = { // do whatever... }
    )
// Attach the observer when it is needed:
appLifecycleObserver.attach()

// Remove when there is no need to it:
appLifecycleObserver.detach()

不要忘记添加适当的 版本

implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")

Here is the solution that by using a debouncing logic, makes sure we are not getting consecutive background/foreground events. So, it always reflects a stable state of backgrounding/foregrounding.

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import java.util.Timer
import java.util.TimerTask

/**
 * An observer class to listen on the app's lifecycle.
 */
class AppLifecycleObserver(
    private val onAppGoesToBackground: () -> Unit = {},
    private val onAppEntersForeground: () -> Unit = {}
) : LifecycleEventObserver {

    private val debounce = DebouncingTimer(timeout = 10)

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        debounce.refresh {
            when (event.targetState) {
                Lifecycle.State.CREATED -> onAppGoesToBackground()
                Lifecycle.State.RESUMED -> onAppEntersForeground()
                else -> Unit
            }
        }
    }

    fun attach() {
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    fun detach() {
        ProcessLifecycleOwner.get().lifecycle.removeObserver(this)
    }

    private class DebouncingTimer(private val timeout: Long) {

        private var timer: Timer? = null

        fun refresh(job: () -> Unit) {
            timer?.cancel()
            timer = Timer()
            timer?.schedule(object : TimerTask() {
                override fun run() = job.invoke()
            }, timeout)
        }
    }
}

Just needs to create an instance of AppLifecycleObserver:

private val appLifecycleObserver = AppLifecycleObserver(
        onAppGoesToBackground = { // do whatever... },
        onAppEntersForeground = { // do whatever... }
    )
// Attach the observer when it is needed:
appLifecycleObserver.attach()

// Remove when there is no need to it:
appLifecycleObserver.detach()

Don't forget to add a proper version of the dependency:

implementation("androidx.lifecycle:lifecycle-process:$lifecycle_version")
猥︴琐丶欲为 2024-10-14 06:09:30

LifecycleObserver 已弃用。使用 DefaultLifecycleObserver 代替:

public class YourApplication extends Application implements DefaultLifecycleObserver {

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

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onStart(owner);
    }

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onResume(owner);
    }

    @Override
    public void onPause(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onPause(owner);
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onStop(owner);
    }

    @Override
    public void onDestroy(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onDestroy(owner);
    }
}

依赖项:

  implementation 'androidx.lifecycle:lifecycle-common:2.5.1'
  implementation 'androidx.lifecycle:lifecycle-process:2.5.1'

LifecycleObserver is deprecated. Use DefaultLifecycleObserver instead:

public class YourApplication extends Application implements DefaultLifecycleObserver {

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

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onStart(owner);
    }

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onResume(owner);
    }

    @Override
    public void onPause(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onPause(owner);
    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onStop(owner);
    }

    @Override
    public void onDestroy(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onDestroy(owner);
    }
}

Dependencies:

  implementation 'androidx.lifecycle:lifecycle-common:2.5.1'
  implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
归途 2024-10-14 06:09:30

我所做的是确保所有应用内活动均通过 startActivityForResult 启动,然后检查 onActivityResult 是否在 onResume 之前调用。如果不是,则意味着我们刚刚从应用程序之外的某个地方返回。

boolean onActivityResultCalledBeforeOnResume;

@Override
public void startActivity(Intent intent) {
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    onActivityResultCalledBeforeOnResume = true;
}

@Override
protected void onResume() {
    super.onResume();
    if (!onActivityResultCalledBeforeOnResume) {
        // here, app was brought to foreground
    }
    onActivityResultCalledBeforeOnResume = false;
}

What I did is make sure that all in-app activities are launched with startActivityForResult then checking if onActivityResult was called before onResume. If it wasn't, it means we just returned from somewhere outside our app.

boolean onActivityResultCalledBeforeOnResume;

@Override
public void startActivity(Intent intent) {
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    onActivityResultCalledBeforeOnResume = true;
}

@Override
protected void onResume() {
    super.onResume();
    if (!onActivityResultCalledBeforeOnResume) {
        // here, app was brought to foreground
    }
    onActivityResultCalledBeforeOnResume = false;
}
稀香 2024-10-14 06:09:30

我的解决方案受到@d60402的答案的启发,并且还依赖于时间窗口,但不使用Timer

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

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

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

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

其中SingletonApplicationApplication的扩展代码>类:

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

My solution was inspired by @d60402's answer and also relies on a time-window, but not using the Timer:

public abstract class BaseActivity extends ActionBarActivity {

  protected boolean wasInBackground = false;

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

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

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

where the SingletonApplication is an extension of Application class:

public class SingletonApplication extends Application {
  public boolean isInBackground = false;
  public long lastForegroundTransition = 0;
}
素食主义者 2024-10-14 06:09:30

我知道有点晚了,但我认为所有这些答案确实存在一些问题,而我按照下面的方式做了,效果很完美。

创建一个像这样的活动生命周期回调:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

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

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

然后将其注册到您的应用程序类上,如下所示:

public class MyApp extends Application {

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

i know its a little late but i think all these answers do have some problems while i did it like below and that works perfect.

create a activity life cycle callback like this:

 class ActivityLifeCycle implements ActivityLifecycleCallbacks{

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

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

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

        lastActivity = activity;
    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

and just register it on your application class like below:

public class MyApp extends Application {

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
扛刀软妹 2024-10-14 06:09:30

这似乎是 Android 中最复杂的问题之一,因为(截至撰写本文时)Android 没有与 iOS 等效的 applicationDidEnterBackground()applicationWillEnterForeground() 回调。我使用了由 AppState 库 /jenzz" rel="nofollow noreferrer">@jenzz。

[AppState 是]一个基于 RxJava 的简单、反应式 Android 库,用于监视应用程序状态变化。每次应用程序进入后台并返回前台时,它都会通知订阅者。

事实证明,这正是我所需要的,特别是因为我的应用程序有多个活动,所以简单地检查活动上的 onStart()onStop() 并不会减少它。

首先,我将这些依赖项添加到 gradle:

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

然后,将这些行添加到代码中的适当位置是一个简单的问题:

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

根据您订阅可观察对象的方式,您可能必须取消订阅以避免内存泄漏。更多信息请参见 github 页面

This appears to be one of the most complicated questions in Android since (as of this writing) Android doesn't have iOS equivalents of applicationDidEnterBackground() or applicationWillEnterForeground() callbacks. I used an AppState Library that was put together by @jenzz.

[AppState is] a simple, reactive Android library based on RxJava that monitors app state changes. It notifies subscribers every time the app goes into background and comes back into foreground.

It turned out this is exactly what I needed, especially because my app had multiple activities so simply checking onStart() or onStop() on an activity wasn't going to cut it.

First I added these dependencies to gradle:

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

Then it was a simple matter of adding these lines to an appropriate place in your code:

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

Depending on how you subscribe to the observable, you may have to unsubscribe from it to avoid memory leaks. Again more info on the github page.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文