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

How to detect when an Android app goes to the background and come back to the foreground

我正在尝试编写一个应用程序,当它在一段时间后返回前台时,它会做一些特定的事情。有没有一种方法可以检测应用程序何时被发送到后台或何时被带到前台?


2018年:Android通过生命周期组件本地支持这一点。

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

这个答案中提供了一个简单的示例,但我写了一个示例应用程序和一篇关于它的博客文章。好的。

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

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

我从来没有像这里写的那样使用过MemoryBoss类,它只是一段伪代码而已。好的。

除非有正当的理由不使用新的体系结构组件(并且有一些组件,特别是针对超旧的API),否则继续使用它们。它们远非完美,但以东也不是完美的。好的。

更新/注释(2015年11月):人们提出了两种意见,第一种意见是应该使用>=而不是==,因为文档说明您不应该检查确切的值。在大多数情况下,这是很好的,但请记住,如果你只关心在应用程序进入后台时做一些事情,那么你必须使用==并将它与另一个解决方案(如活动生命周期回调)结合起来,否则你可能无法获得你想要的效果。例如(我也遇到过这样的情况),如果你想在应用程序进入后台时用密码屏幕锁定它(比如,如果你熟悉的话,用1password),如果你的内存不足,突然测试>= TRIM_MEMORY,你可能会意外地锁定你的应用程序,因为android会触发LOW MEMORY调用,这比你的。所以要小心你的测试方法。好的。

此外,有些人还询问了如何检测你何时回来。好的。

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

1
2
3
4
5
6
7
8
9
@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块中的代码只执行一次,即使您转到另一个活动,新的活动(也就是extends BaseActivity将报告wasInBackgroundfalse,因此它不会执行代码,直到调用onMemoryTrimmed并将标志再次设置为true。好的。

希望有帮助。好的。

更新/注释(2015年4月):在您复制并粘贴此代码之前,请注意,我发现了一些不完全可靠的实例,必须与其他方法结合才能获得最佳结果。值得注意的是,有两种已知的情况不能保证执行onTrimMemory回拨:好的。

  • 如果你的手机在你的应用程序可见时锁定了屏幕(比如说你的设备在nn分钟后锁定),这个回调不会被调用(或者不总是),因为锁定屏幕只是在上面,但是你的应用程序仍然"运行",尽管覆盖了。好的。

  • 如果您的设备内存相对较低(并且内存不足),操作系统似乎会忽略此调用并直接进入更关键的级别。好的。

  • 现在,根据您知道应用程序何时进入后台的重要性,您可能需要或可能不需要扩展此解决方案,同时跟踪活动生命周期和其他内容。好的。

    请记住以上几点,并拥有一支优秀的质量保证团队;)好的。

    更新结束好的。

    可能很晚了,但在冰淇淋三明治(API 14)和更高版本中有一种可靠的方法。好的。

    结果发现,当你的应用程序没有更多可见的用户界面时,会触发回调。可以在自定义类中实现的回调称为componentcallback2(是的,有两个)。此回调仅在API级别14(冰淇淋三明治)及以上版本中可用。好的。

    您基本上可以调用该方法:好的。

    1
    public abstract void onTrimMemory (int level)

    级别为20或更具体好的。

    1
    public static final int TRIM_MEMORY_UI_HIDDEN

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

    引用官方文件:好的。

    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.

    Ok.

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

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

    这正是你想知道的。好的。

    你怎么决定你什么时候回来?好的。

    很简单,我相信你有一个"baseActivity",所以你可以用onResume()来标记你回来的事实。因为你唯一一次说你不回来是在你实际收到对上述onTrimMemory方法的调用时。好的。

    它起作用了。你不会得到假阳性。如果一项活动正在恢复,你会100%地回来。如果用户再次回到后台,您会接到另一个onTrimMemory()呼叫。好的。

    你需要寄托你的活动(或者更好的是,定制类)。好的。

    最简单的方法是创建这样一个简单的类来保证您始终收到它:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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.
        }
    }

    为了使用它,在应用程序实现中(您有一个,对吗?),执行如下操作:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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,并实现在API 14下面的任何地方使用的ComponentCallbacks(没有2)。该回调只具有onLowMemory()方法,当您转到后台时不会被调用,但您应该使用它来修剪内存。好的。

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

    最后一步是从回调中注销。也许最好的地方是你的应用程序的onTerminate()方法,但这种方法在真正的设备上不会被调用:好的。

    1
    2
    3
    4
    5
    6
    /**
     * 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.
     */

    因此,除非您真的有一个不再希望注册的情况,否则您可以安全地忽略它,因为无论如何,您的进程都将在操作系统级别上停止。好的。

    如果您决定在某个时间点取消注册(例如,如果您为应用程序提供一个关闭机制来清理和关闭),则可以执行以下操作:好的。

    1
    unregisterComponentCallbacks(mMemoryBoss);

    就是这样。好的。好啊。


    以下是我解决这个问题的方法。它的工作前提是,在活动转换之间使用时间引用很可能会提供足够的证据,证明应用程序已经"后退"或没有。

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

    1
    2
    3
    4
    5
    6
    7
    public class MyApplication extends Application {

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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()事件或(最好是)在所有具体活动继承的基本活动中向这些方法中的每一个添加调用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @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是假的。

    另一方面,当一个活动从启动程序、设备唤醒、结束电话呼叫等进入前台时,很可能是在此事件之前执行的计时器任务,因此WasinBackground设置为true。


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

    谷歌I/O对话的实际解决方案是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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
        }
      }

    }

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

    但还是有希望的。


    当应用程序被带到后台并再次进入前台时,调用onPause()onResume()方法。但是,当应用程序第一次启动并且在终止之前也会调用它们。您可以在活动中阅读更多内容。

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

    有关更多详细信息,请查看此处android:检测何时android应用程序进入后台并返回前台而不使用getrunningtasks或getrunningappprocesses的解决方案。


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

    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.

    实现可以简单到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class AppLifecycleListener implements LifecycleObserver {

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

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

    // register observer
    ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycleListener());

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

    使用此功能还需要dependencies

    1
    2
    implementation"android.arch.lifecycle:extensions:1.1.1"
    annotationProcessor"android.arch.lifecycle:compiler:1.1.1"


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    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;
            }
        }
    }

    然后将其添加到应用程序类的onCreate()中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class MyApp extends android.app.Application {

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

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

        }

    }


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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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()。另外,我还添加了超级调用,这在我最初的帖子中是缺失的,因为这更多的是一个概念,而不是一个工作代码。


    如果您的应用程序包含多个活动和/或堆叠的活动,如选项卡小部件,则覆盖onpause()和onresume()将不起作用。即,当启动新活动时,当前活动将在创建新活动之前暂停。当完成(使用"后退"按钮)一项活动时,同样适用。

    我找到了两种可行的方法。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    }

    这种方法是在机器人fu(现在称为点火)框架中发现的。

    我自己实现的第二个方法不需要"获取任务"权限,这很好。相反,实现起来有点复杂。

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

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

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

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


    创建扩展Application的类。然后在它中我们可以使用它的覆盖方法,onTrimMemory()

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

    1
    2
    3
    4
    5
    6
    7
    8
     @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
            }
        }


    考虑使用onUserLeaveHint。只有当你的应用程序进入后台时才会调用。OnPause将有一些角落案例需要处理,因为它可以被其他原因调用;例如,如果用户在应用程序中打开另一个活动(如设置页面),则即使主要活动的OnPause方法仍在应用程序中,也会被调用;跟踪将要发生的情况将导致错误,而您只需使用OnUserLeavehint回调,执行您的请求。

    当调用on userleavehint时,可以将布尔inbackground标志设置为true。当调用onresume时,只有在设置了inbackground标志时,才假设您回到了前台。这是因为,如果用户只是在设置菜单中,并且从未离开过应用程序,那么在您的主要活动上也会调用onresume。

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

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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 {
        ...
    }


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

    但是,如果调用registerActivityLifecycleCallbacks(),则应该能够在创建、销毁活动等时获得回调。您可以为活动调用getcomponentname()。


    android.arch.lifecycle包提供了类和接口,使您能够构建具有生命周期意识的组件。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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文件中:

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

    按照谷歌的建议,您应该最小化活动生命周期方法中执行的代码:

    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.

    您可以在此处阅读更多信息:https://developer.android.com/topic/libraries/architecture/lifecycle


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    @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();
                }
            }
        });
    }


    我在Github应用前台后台监听上创建了一个项目

    为应用程序中的所有活动创建一个BaseActivity。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    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用作所有活动的超级类,如mainActivity extends baseActivity,当启动应用程序时将调用onAppStart,当应用程序从任何屏幕进入后台时将调用onAppPause()。


    这对于ProcessLifecycleOwner来说非常简单

    添加这些依赖项

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

    在Kotlin:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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")
        }
    }

    然后在基本活动中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    override fun onCreate() {
            super.onCreate()

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

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


    你可以使用:

    protected void onRestart ()

    以区别新启动和重新启动。

    enter image description here


    编辑2:我在下面写的内容实际上不起作用。谷歌拒绝了一个包含对activitymanager.getrunningtasks()的调用的应用程序。从文档中可以明显看出,此API仅用于调试和开发目的。我将在有时间更新下面的Github项目时尽快更新这篇文章,新方案使用计时器,几乎和以前一样好。

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

    公认的答案和最高评价的答案都不是最好的方法。ISApabyBruttToBoeStand()中的最高评级答案的实现不处理应用程序的主要活动屈服于同一应用程序中定义的活动的情况,但它具有不同的Java包。我想出了一个办法来解决这个问题。

    在onpause()中调用这个函数,它将告诉您,您的应用程序是因为另一个应用程序已经启动,还是因为用户按下了Home按钮而进入后台。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    /**
     * 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();

    }


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

    我做这件事的方法很简单。按照以下说明检测应用程序背景/前景阶段。

    只要稍加变通,这是可能的。在这里,活动生命周期回调来拯救。让我一步一步走过去。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class App extends Application implements
        Application.ActivityLifecycleCallbacks {

        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  • 将"app"类注册到清单中,如下所示,.

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

    ZZU1〔2〕

    activityReferences将保留处于已启动状态的活动数。isActivityChangingConfigurations是一个标志,用于指示当前活动是否像方向开关一样经历配置更改。

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

    1
    2
    3
    4
    5
    6
    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  • 这是如何检测应用程序是否进入后台。

    1
    2
    3
    4
    5
    6
    7
    @Override
    public void onActivityStopped(Activity activity) {
        isActivityChangingConfigurations = activity.isChangingConfigurations();
        if (--activityReferences == 0 && !isActivityChangingConfigurations) {
            // App enters background
        }
    }
  • 它是如何工作的:

    这是用顺序调用生命周期方法的方式完成的一个小技巧。让我来介绍一个场景。

    假设用户启动应用程序,启动程序活动A启动。生命周期调用将是,

    A.onCreate()

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

    A.onResume()

    现在活动A开始活动B。

    A.onPause()

    B.onCreate()

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

    B.onResume()

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

    然后用户从活动B返回,

    B.onPause()

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

    A.onResume()

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

    B.onDestroy()

    然后用户按Home键,

    A.onPause()

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

    如果用户从活动B中按下Home按钮而不是Back按钮,那么它仍然是相同的,并且ActivityReferences将是0。因此,我们可以在应用程序进入后台时进行检测。

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

    B.onPause()

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

    B.onDestroy()

    B.onCreate()

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

    B.onResume()

    这就是为什么我们要对isActivityChangingConfigurations进行额外检查,以避免在活动经历配置更改时出现这种情况。


    这里回答正确

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    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() {

        }
    }

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

    1
    2
    3
    MyApp myApp = new MyApp();
    registerComponentCallbacks(myApp);
    getApplication().registerActivityLifecycleCallbacks(myApp);

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


    这似乎是Android中最复杂的问题之一,因为(在本文中)Android没有与applicationDidEnterBackground()applicationWillEnterForeground()回调相同的iOS。我使用了@jenzz组装的AppState库。

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

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

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

    1
    2
    3
    4
    dependencies {
        compile 'com.jenzz.appstate:appstate:3.0.1'
        compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
    }

    然后简单地将这些行添加到代码中的适当位置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //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;
            }
        }
    });

    根据订阅Observable的方式,您可能需要取消订阅,以避免内存泄漏。在Github页面上再次显示更多信息。


    这是@d60402答案的修改版本:https://stackoverflow.com/a/15573121/4747587

    做上面提到的一切。但是,不要让Base Activity作为每个活动的父代,也不要让它覆盖onResume()onPause,而是执行以下操作:

    在应用程序类中,添加行:

    RegisterActivityLifecycleCallbacks(application.ActivityLifecycleCallbacks回调);

    这个callback有所有的活动生命周期方法,现在您可以覆盖onActivityResumed()onActivityPaused()

    看看这个要点:https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acf2ac79fa1b


    我的解决方案的灵感来自于@d60402的回答,而且还依赖于一个时间窗口,但不使用Timer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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类的扩展:

    1
    2
    3
    4
    public class SingletonApplication extends Application {
      public boolean isInBackground = false;
      public long lastForegroundTransition = 0;
    }

    我在谷歌分析easytracker上使用这个,它很管用。它可以扩展为使用一个简单的整数来执行所需的操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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) {

                }
            });
        }
    }


    我知道有点晚了,但我认为所有这些答案确实有一些问题,而我像下面这样做,这是完美的。

    创建活动生命周期回调,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     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) {

        }
    }

    只需在应用程序类中注册它,如下所示:

    1
    2
    3
    4
    5
    6
    7
    public class MyApp extends Application {

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


    你可以在ActivityLifecycleCallbacksComponentCallbacks2的帮助下轻松实现这一点,如下所示。

    创建一个实现上述接口的类AppLifeCycleHandler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    package com.sample.app;

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

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

      AppLifeCycleCallback appLifeCycleCallback;

      boolean appInForeground;

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

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

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

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

      }

      @Override
      public void onActivityStarted(Activity activity) {

      }

      @Override
      public void onActivityPaused(Activity activity) {

      }

      @Override
      public void onActivityStopped(Activity activity) {

      }

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

      }

      @Override
      public void onActivityDestroyed(Activity activity) {

      }

      @Override
      public void onConfigurationChanged(Configuration configuration) {

      }

      @Override
      public void onLowMemory() {

      }

      interface AppLifeCycleCallback {

        void onAppBackground();

        void onAppForeground();
      }
    }

    在扩展Application的类中,实现AppLifeCycleCallback以在应用程序在前台和后台之间切换时获取回调。像下面这样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{

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

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

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

    希望这有帮助。

    编辑作为替代方案,您现在可以使用生命周期感知架构组件。


    因为我找不到任何方法,它也可以在不检查时间戳的情况下处理旋转,所以我想我还可以在我们的应用程序中分享我们现在的方法。除了这个答案https://stackoverflow.com/a/42679191/5119746之外,我们还考虑了方向。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
       // ActivityLifecycleCallbacks

       override fun onActivityResumed(activity: Activity?) {

          mResumed++

          if (mAppIsInBackground) {

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

             mAppIsInBackground = false
          }
          mOrientationWasChanged = false
        }

    和OnActivity已停止:

    1
    2
    3
    4
    5
    6
    7
    8
       override fun onActivityStopped(activity: Activity?) {

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

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

            mAppIsInBackground = true
        }

    然后,这里还有一个补充:检查方向变化:

    1
    2
    3
    4
    5
    6
    7
    8
       override fun onConfigurationChanged(newConfig: Configuration) {

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

    就是这样。希望这能帮助别人:)


    我通过实现一个使用onresume、onpause和onstop活动回调的baseactivity,成功地监控了后台和前台的应用程序导航。这是我的实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    override fun onResume() {
        super.onResume()
        if (AppActivityState.state == AppState.ON_LAUNCHED) {
            // We are in the first launch.
            onLaunched()
        } else {
            if (AppActivityState.state == AppState.ON_BACKGROUND) {
                // We came from background to foreground.
                AppActivityState.state = AppState.ON_FOREGROUND
                onForeground()
            } else {
                // We are just navigating through pages.
                AppActivityState.state = AppState.RESUMED
            }
        }
    }

    override fun onPause() {
        super.onPause()
        // If state is followed by onStop then it means we will going to background.
        AppActivityState.state = AppState.PAUSED
    }

    override fun onStop() {
        super.onStop()

        // App will go to background base on the 'pause' cue.
        if (AppActivityState.state == AppState.PAUSED) {
            AppActivityState.state = AppState.ON_BACKGROUND
            onBackground()
        }
    }

    创建BaseActivity之后,您只需将此活动扩展到应用程序上的任何活动。

    在这些类型的实现中,您可以准确地检测到以下内容:-OnBackground>应用程序将转到后台-OnForeground>应用程序将返回前台-OnLaunch>应用程序刚刚打开

    我希望这能帮助你:)


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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;
    }

    这是我的解决方案。只需在主应用程序类中注册此ActivityLifecycleCallbacks。在评论中,我提到了一个用户配置文件活动边缘案例。这个活动只是一个有透明边缘的活动。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    /**
     * This class used Activity lifecycle callbacks to determine when the application goes to the
     * background as well as when it is brought to the foreground.
     */
    public class Foreground implements Application.ActivityLifecycleCallbacks
    {
        /**
         * How long to wait before checking onStart()/onStop() count to determine if the app has been
         * backgrounded.
         */
        public static final long BACKGROUND_CHECK_DELAY_MS = 500;

        private static Foreground sInstance;

        private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
        private boolean mIsForeground = false;
        private int mCount;

        public static void init(final Application application)
        {
            if (sInstance == null)
            {
                sInstance = new Foreground();
                application.registerActivityLifecycleCallbacks(sInstance);
            }
        }

        public static Foreground getInstance()
        {
            return sInstance;
        }

        public boolean isForeground()
        {
            return mIsForeground;
        }

        public boolean isBackground()
        {
            return !mIsForeground;
        }

        @Override
        public void onActivityStarted(final Activity activity)
        {
            mCount++;

            // Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
            // the app before it runs.
            mMainThreadHandler.removeCallbacksAndMessages(null);

            if (!mIsForeground)
            {
                mIsForeground = true;
            }
        }

        @Override
        public void onActivityStopped(final Activity activity)
        {
            mCount--;

            // A transparent Activity like community user profile won't stop the Activity that launched
            // it. If you launch another Activity from the user profile or hit the Android home button,
            // there are two onStops(). One for the user profile and one for its parent. Remove any
            // posted Runnables so we don't get two session ended events.
            mMainThreadHandler.removeCallbacksAndMessages(null);
            mMainThreadHandler.postDelayed(new Runnable()
            {
                @Override
                public void run()
                {
                    if (mCount == 0)
                    {
                        mIsForeground = false;
                    }
                }
            }, BACKGROUND_CHECK_DELAY_MS);
        }

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

        }

        @Override
        public void onActivityResumed(final Activity activity)
        {

        }

        @Override
        public void onActivityPaused(final Activity activity)
        {

        }

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

        }

        @Override
        public void onActivityDestroyed(final Activity activity)
        {

        }
    }

    通过使用下面的代码,我可以得到我的应用前景或背景状态。

    有关它工作的详细信息,请单击此处

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import android.content.ComponentCallbacks2;
    import android.content.Context;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.Toast;

    public class MainActivity extends AppCompatActivity {

    private Context context;
    private Toast toast;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
    }

    private void showToast(String message) {
        //If toast is already showing cancel it
        if (toast != null) {
            toast.cancel();
        }

        toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
        toast.show();
    }

    @Override
    protected void onStart() {
        super.onStart();
        showToast("App In Foreground");
    }

    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            showToast("App In Background");
        }
      }
    }

    这些答案似乎不正确。当另一个活动开始和结束时,也会调用这些方法。您可以做的是保留一个全局标志(是的,全局错误:)并在每次启动新活动时将其设置为true。在每个活动的onCreate中将其设置为false。然后,在onpause中检查这个标志。如果它是假的,你的应用程序就会进入后台,或者会被杀。


    我们可以使用LiveData扩展此解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {

        private var lifecycleListener: LifecycleObserver? = null

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

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

        internal inner class AppLifecycleListener : LifecycleObserver {

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

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

        enum class State {
            FOREGROUND, BACKGROUND
        }
    }

    现在我们可以订阅这个LiveData并捕获所需的事件。例如:

    1
    2
    3
    4
    5
    6
    appForegroundStateLiveData.observeForever { state ->
        when(state) {
            AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
            AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
        }
    }

    这个解决方案怎么样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class BaseActivity extends Activity
    {

        static String currentAct ="";

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

            if (currentAct.equals(""))
                Toast.makeText(this,"Start", Toast.LENGTH_LONG).show();

            currentAct = getLocalClassName();
        }

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

            if (currentAct.equals(getLocalClassName()))
            {
                currentAct ="";
                Toast.makeText(this,"Stop", Toast.LENGTH_LONG).show();
            }
        }
    }

    所有活动都需要扩展BaseActivity。

    当一个活动调用另一个(a->b)时,currentAct不等于getlocalclassname(),因为第二个活动(b)的onStart()在第一个(a)的onStop()之前调用(https://developer.android.com/guide/components/activities.html coordinatingactivities)。

    当用户按下Home按钮或在应用程序之间进行更改时,只需调用OnStop(),然后CurrentAct等于GetLocalClassName()。


    这是我的解决方案https://github.com/doridori/androidutils/blob/master/app/src/main/java/com/doridori/lib/app/activitycounter.java

    基本上涉及到使用计时器计算所有活动的生命周期方法,以捕捉当前前台没有活动但应用程序是(即轮换)的情况。


    我的应用需要"重启"后,从后台返回-显示一系列的活动,根据客户的要求。在详细搜索了如何管理背景/前景转换(iOS和Android之间的处理方式非常不同)之后,我越过了这个问题。在这里找到了非常有用的帮助,特别是来自投票最多的答案和标记为正确的答案。然而,只要在每次应用程序进入前台时重新启动根活动,当你想到UX时,看起来就太烦人了。我认为最适合的解决方案是结合@girishnair和@d60402的答案:当应用程序调整内存时调用计时器,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    public void onTrimMemory(int level) {
        if (stateOfLifeCycle.equals("Stop")) {
            startActivityTransitionTimer();
        }

        super.onTrimMemory(level);
    }

    我的计时器设置为30秒-我正在考虑增加一点。

    1
    private final long MAX_ACTIVITY_TRANSITION_TIME = 30000;

    当应用程序进入前台,重新启动,或者应用程序被销毁时,调用该方法取消计时器。

    应用程序内扩展:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Override
    public void onActivityCreated(Activity activity, Bundle arg1) {
        stopActivityTransitionTimer();
        stateOfLifeCycle ="Create";
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        stopActivityTransitionTimer();
        stateOfLifeCycle ="Destroy";
    }

    在活动上(最好是在基本活动上,由其他人继承):

    1
    2
    3
    4
    5
    6
    7
    @Override
    protected void onStart() {
        super.onStart();
        if (App.wasInBackground) {
            stopActivityTransitionTimer();
        }
    }

    在我的例子中,当应用程序在最长时间后进入前台时,会创建一个新任务,因此在应用程序扩展类中对OnActivityCreated()或OnActivityDestroyed()调用StopActivityTransitionTimer(),这就不需要在活动中调用该方法。希望它有帮助。


    我正在使用此解决方案:http://nathanel.hevenet.com/android-dev-detecting-when-your-app-is-in-the-background-across-activities/

    简而言之,构建一个专门的服务,每个活动都会向他报告每个生命周期事件,而这个服务会获取有关应用程序状态的信息。

    很像@oldschool4664解决方案,但在我看来更干净


    主要的问题是,当您从后台启动一个活动时,必须获得一个特定的行为。如果重写onpause()和onresume()方法,您将得到一个接近答案,但不是解决方案。问题是,即使不最小化应用程序,也会调用onpause()和onresume()方法,这些方法可以在启动活动时调用,稍后按后退按钮返回活动。要消除该问题并真正了解应用程序何时来自后台,必须获取正在运行的进程并与进程进行比较:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private boolean isApplicationBroughtToBackground() {
        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> tasks = am.getRunningTasks(1);
        if (!tasks.isEmpty()) {
            ComponentName topActivity = tasks.get(0).topActivity;
            if (!topActivity.getPackageName().equals(getPackageName())) {
                return true;
            }
        }
        return false;
    }

    现在您必须声明一个布尔变量:

    1
    public boolean wasPaused = false;

    并询问您的活动何时进入后台:

    1
    2
    3
    4
    5
    6
    @Override
    public void onPause(){
        super.onPause();
        if(isApplicationBroughtToBackground())
            wasPaused = true;
    }

    现在,当您的活动再次出现在屏幕上时,在onresume()方法中询问:

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    public void onResume(){
        super.onResume();
        if(wasPaused){
            lockScreen(true);
        }
        wasPaused = false;
    }

    就是这样。现在,当您的活动进入后台,稍后用户将其置于前台时,将显示锁定屏幕。

    如果您想对应用程序的任何活动重复此行为,您必须创建一个活动(可能是baseActivity),放置此方法,并且所有活动都必须从baseActivity继承。

    我希望这对你有帮助。

    问候语!