如何检测Android应用何时进入后台并回到前台


382

我正在尝试编写一个应用程序,该应用程序在经过一段时间后又回到前台时会执行特定的操作。有没有一种方法可以检测何时将应用程序发送到后台或带到前台?


2
可能是在问题中添加了一个用例,因为它似乎并不明显,因此在给出的答案中未解决。该应用程序可能会启动另一个应用程序(例如,图库),该应用程序仍将驻留在同一堆栈中并显示为该应用程序的屏幕之一,然后按“主页”按钮。依赖App生命周期(甚至是内存管理)的任何方法都无法检测到这一点。它们会在出现外部“活动”时立即触发背景状态,而不是在您按下“主页”时触发。
丹尼斯·K

这是您正在寻找的答案:stackoverflow.com/a/42679191/2352699
FredPorciúncula17年

Answers:


98

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

没有任何直接的方式获取应用程序的状态,而在背景或前景,但即使我面临这个问题,并找到了解决方案onWindowFocusChangedonStop

有关更多详细信息,请在此处查看Android:一种解决方案,用于检测Android应用何时进入后台并返回到前台,而无需getRunningTasks或getRunningAppProcesses


173
但是,这种方法会引起误报,正如其他人指出的那样,因为在同一个应用程序中的活动之间进行转换时也会调用这些方法。
约翰·莱曼

9
比这更糟。我尝试过,有时在手机锁定时会调用onResume。如果您在文档中看到onResume的定义,则会发现:请记住,onResume并不是您的活动对用户可见的最佳指示;系统窗口(例如键盘锁)可能位于前面。使用onWindowFocusChanged(boolean)可以确定您的活动对用户可见(例如,恢复游戏)。developer.android.com/reference/android/app/...
J-柔

2
链接中发布的解决方案不使用onResume / onPause,而是onBackPressed,onStop,onStart和onWindowsFocusChanged的组合。它确实为我工作,并且我有一个相当复杂的UI层次结构(带有抽屉,动态查看器等)
Martin Marconcini

18
onPause和onResume特定于活动。没有申请。当某个应用程序进入后台并恢复运行时,它将恢复进入后台之前的特定活动。这意味着您需要在应用程序的所有Activity中实现从后台恢复执行的所有操作。我相信最初的问题是为Application而不是Activity寻找类似“ onResume”的东西。
SysHex

4
我无法相信没有适当的API可以满足如此普遍的需求。最初我以为onUserLeaveHint()会削减它,但您无法确定用户是否要离开应用程序
atsakiridis 2015年

197

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

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

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

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

永远不要以为您正在寻找的解决方案就是一小段代码,这种情况不太可能发生;更好的是,尝试了解它的作用及其原因。

MemoryBoss班从未实际使用我这里写的,它只是一块正巧工作的伪代码。

除非有正当的理由不使用新的体系结构组件(并且有一些,尤其是针对超级旧api的组件),否则请继续使用它们。它们远非完美,但都不完美ComponentCallbacks2

UPDATE / NOTES(2015年11月):人们一直在发表两条评论,第一条>=应该使用它,而不是==因为文档指出不应检查确切值。这是适用于大多数情况,但请记住,如果你关心做事情时,应用程序去的背景下,你将不得不使用== 并且还与另一种解决方案(如活动生命周期回调)结合起来,或者你可能无法达到您想要的效果。这个例子(这发生在我身上)是,如果你想锁定您的应用在进入后台时会带有密码屏幕(例如1Password,如果您熟悉的话),如果内存不足并突然测试>= TRIM_MEMORY,可能会意外锁定应用,因为Android会触发LOW MEMORY通话,比你的高。因此,请谨慎进行测试。

此外,有些人询问如何在您回来时进行检测。

我可以想到的最简单的方法将在下面说明,但是由于有些人对此不熟悉,因此我在此处添加了一些伪代码。假设您有YourApplication和中的MemoryBoss班级class BaseActivity extends Activity(如果您没有班级,则需要创建一个)。

@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

希望能有所帮助。

UPDATE / NOTES(2015年4月):在对该代码进行所有复制和粘贴之前,请注意,我发现了一些实例,其中它可能不是100%可靠的,必须将其与其他方法结合使用才能达到最佳效果。值得注意的是,在两个已知的实例中,onTrimMemory不能保证执行回叫:

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

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

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

只要记住以上几点,并拥有一支好的QA团队;)

更新结束

可能已经晚了,但是Ice Cream Sandwich(API 14)及更高版本中有一种可靠的方法。

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

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

public abstract void onTrimMemory (int level)

等级为20或更高

public static final int TRIM_MEMORY_UI_HIDDEN

我一直在测试它,它始终有效,因为20级只是一个“建议”,由于您的应用程序不再可见,您可能希望释放一些资源。

引用官方文档:

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

当然,您应该实现此功能以实际执行它所说的内容(清除一段时间内未使用的内存,清除一些未使用的集合,等等。可能性无穷无尽(有关更多其他信息,请参阅官方文档)临界水平)。

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

首先,这正是您想知道的。

您如何确定何时回来?

好吧,这很容易,我确定您有一个“ BaseActivity”,因此您可以使用onResume()来标记您回来了。因为您唯一会说您不回来的时间是您实际上收到上述onTrimMemory方法的调用时。

有用。您不会得到误报。如果某个活动正在恢复,那么您有100%的机会回来了。如果用户再次走到后面,您会onTrimMemory()接到另一个电话。

您需要预订“活动”(或者更好的是,自定义类)。

确保始终收到此消息的最简单方法是创建一个像这样的简单类:

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你可以添加elseif并实施ComponentCallbacks(不含2)以下API 14.任何使用的回调只是有onLowMemory()方法和当你去到后台不会被调用,但你应该用它来装饰记忆。

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

最后一步是从回调中注销。最好的地方可能是onTerminate()您的App 的方法,但是,该方法不会在实际设备上被调用:

/**
 * 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.
 */

因此,除非您确实有不想再注册的情况,否则您可以放心地忽略它,因为您的进程无论如何都将在OS级别上死掉。

如果您决定在某个时候取消注册(例如,如果您为应用程序提供了关闭机制以使其清理并死亡),则可以执行以下操作:

unregisterComponentCallbacks(mMemoryBoss);

就是这样。


从服务检查时,似乎仅在按下主屏幕按钮时触发。按下后退按钮不会在KitKat上触发此操作。
了解OpenGL ES

该服务没有UI,因此可能与此相关。在基本活动中而不是在服务中进行检查。您想知道什么时候隐藏了UI(也许告诉了服务,所以它就
成了

1
当您关闭手机时,它不起作用。它不会被触发。
Juangcg 2014年

2
到目前为止,我发现ComponentCallbacks2.onTrimMemory()(与ActivityLifecycleCallbacks结合使用)是唯一可靠的解决方案,谢谢Martin!对于那些感兴趣的人,请参阅我提供的答案。
rickul 2015年

3
自一年前以来,我一直在使用这种方法,对我而言一直是可靠的。很高兴知道其他人也使用它。我只是使用level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN它来避免更新中的问题,第2点。关于第1点,我并不担心,因为该应用程序并没有真正进入后台,因此这就是它应该起作用的方式。
sorianiv 2015年

175

这是我设法解决此问题的方法。它的前提是,在活动转换之间使用时间参考将最有可能提供充分的证据来证明应用程序是否已“后台”。

首先,我使用了一个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()事件,或者最好在一个基本Activity中添加对每个方法的调用,您的所有具体Activity都将继承自该基本Activity:

@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


4
嗨,d60402,您的回答确实很有帮助。.非常感谢您的答复...小提示.. MyApplication应该在清单文件应用程序标记中提及,例如android:name =“ MyApplication”,否则应用程序崩溃...仅用于帮助像我这样的人
praveenb

2
伟大的程序员的标志,简单的解决方案是我遇到过的最复杂的问题之一。
Aashish Bhatnagar 2014年

2
很棒的解决方案!谢谢。如果有人遇到“ ClassCastException”错误,那么您可能会错过将其添加到Manifest.xml中的application标记中的<application android:name =“ your.package.MyApplication”
Wahib Ul Haq

27
这是一个很好且简单的实现。但是我相信这应该在onStart / onStop而不是onPause / onResume中实现。即使我启动一个部分覆盖活动的对话框,也会调用onPause。关闭对话框实际上会调用onResume,使其看起来好像该应用程序刚刚出现在前台
Shubhayu 2014年

7
我希望使用此解决方案的变体。上面确定的有关对话的问题对我来说是个问题,因此我尝试了@Shubhayu的建议(onStart / onStop)。但是,这没有帮助,因为在进行A-> B时,活动B的onStart()在活动A的onStop()之前被调用。
Trevor

150

编辑:新的体系结构组件带来了一些有希望的东西: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
    }
  }

}

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

但是有希望。


3
这样完美!我已经尝试了很多怪异的解决方案,这些解决方案有很多缺陷……非常感谢!我已经找了一段时间了。
Eggakin Baconwalker

7
它适用于多种活动,但只有一种-onrotate会指示所有活动都已消失或在后台
死鱼

2
@Shyri,您是正确的,但这是此解决方案的一部分,因此需要担心。如果firebase依赖于此,我认为我的中等应用也可以:)很好的回答。
ElliotM

3
@deadfish检查答案顶部提供的I / O链接。您可以检查活动停止和开始之间的时间间隔,以确定您是否真的去过后台。实际上,这是一个绝妙的解决方案。
Alex Berdnikov

2
有Java解决方案吗?这是科特林。
Giacomo Bartoli

116

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

ProcessLifecycleOwner将出动ON_STARTON_RESUME活动,通过这些活动第一活性移动。ON_PAUSE,,ON_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

另外,使用此功能还需要dependencies

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

10
请注意,您需要添加生命周期依赖项implementation "android.arch.lifecycle:extensions:1.0.0"annotationProcessor "android.arch.lifecycle:compiler:1.0.0"从Google的存储库(即google())中添加
Codesalot爵士,17年

1
这对我来说很棒,谢谢。由于错误,我不得不使用api'android.arch.lifecycle:extensions:1.1.0'而不是实现,原因是错误指出Android依赖项的编译和运行时类路径版本不同。
FSUWX2011

这是一个很好的解决方案,因为它可以在模块中工作,而无需活动参考!
最多

应用程式当机时,这无法运作。是否有任何解决方案也可以通过该解决方案来解决应用崩溃事件
-tejraj,

很好的解决方案。拯救了我的一天。
晴,

69

基于MartínMarconcinis的回答(谢谢!),我终于找到了可靠(非常简单)的解决方案。

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

    }

}

可以显示您如何在应用程序中使用它吗,我可以从App类还是其他地方调用它?
JPM

这是完美的谢谢!!到目前为止,在测试方面的工作非常出色
aherrick

此示例不完整。什么是registerActivityLifecycleCallbacks?
Noman 2015年

它是类android.app.Application中的方法
rickul

1
做得好+1排在最前面,因为它很完美,不要寻找其他答案,这是基于@reno答案的,但有一个真实的例子
Stoycho Andreev

63

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

想法是,当处于前台时,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()。另外,我还要添加超级​​调用,这是我最初的帖子中所没有的,因为这更多的是概念而非有效的代码。


2
尽管我使用的是onStart而不是onResume,但这是最可靠的答案。
格雷格·恩尼斯

您应该在覆盖的方法中添加对super.onResume()和super.onStop()的调用。否则会引发android.app.SuperNotCalledException。
Jan Laussmann

1
对我来说,它是行不通的……或者至少在您旋转设备时也会触发该事件(这是一种错误的肯定恕我直言)。
Noya 2015年

非常简单有效的解决方案!但是我不确定它是否可以与部分透明的活动配合使用,从而使先前活动的某些部分可见。从文档中,onStop is called when the activity is no longer visible to the user
Nicolas Buquet 2015年

3
如果用户更改第一个活动的方向会怎样?它将报告该应用程序进入后台,这是不正确的。您如何处理这种情况?
Nimrod Dayan 2015年

54

如果您的应用程序包含多个活动和/或堆叠的活动(例如标签栏小部件),则覆盖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(现在称为点火)框架中找到了此方法。

我自己实现的第二种方法不需要GET_TASKS权限,这很好。相反,它的实现有点复杂。

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

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

  • 暂停的活动尚未完成(使用了“返回”按钮)。这可以通过使用activity.isFinishing()方法来完成
  • 没有启动新的活动(相同的程序包名称)。您可以重写startActivity()方法来设置一个变量,以指示此情况,然后在onPostResume()中将其重置,这是创建/恢复活动时要运行的最后一个方法。

当您可以检测到应用程序已退出后台时,很容易检测到何时又将其带回前台。


18
Google可能会拒绝使用ActivityManager.getRunningTasks()的应用程序。该文档指出,这仅是针对开发人员的目的。developer.android.com/reference/android/app/...
天空凯尔西


1
我发现我不得不组合使用这些方法。在14中启动活动时,调用了onUserLeaveHint()。`@Override public void onUserLeaveHint(){inBackground = isApplicationBroughtToBackground(); }`
上市船

7
用户不会对使用功能强大的android.permission.GET_TASKS权限感到满意。
MSquare

6
getRunningTasks在API级别21中已弃用。–
Noya

33

创建一个扩展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
        }
    }

1
FragmentActivity您也可能想要添加level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
Srujan Simha'3

2
非常感谢您指出此方法,每当用户恢复后台活动时,我都需要显示一个Pin对话框,使用此方法编写一个pref值并在baseActivity上检查该值。
山姆

18

考虑使用onUserLeaveHint。仅当您的应用进入后台时才会调用此方法。onPause将处理一些特殊情况,因为可以出于其他原因调用它;例如,如果用户在您的应用程序中打开了另一个活动(例如您的“设置”页面),则即使主活动的onPause方法仍在您的应用程序中,也会被调用;当您可以简单地使用onUserLeaveHint回调来执行您要查询的操作时,跟踪发生的情况将导致错误。

调用UserLeaveHint时,可以将boolean 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 {
    ...
}

19
导航到另一个活动时,也会调用onUserLeaveHint
Jonas Stawski 2013年

3
当例如打进电话且通话活动变为活动状态时,不会调用onUserLeaveHint,因此这也有一个极端的情况-可能还有其他情况,因为您可以向意图添加一个标志以禁止onUserLeaveHint呼叫。developer.android.com/reference/android/content/…–
Groxx

1
另外,onResume也无法正常工作。我尝试过,有时在手机锁定时会调用onResume。如果您在文档中看到onResume的定义,则会发现:请记住,onResume并不是您的活动对用户可见的最佳指示;系统窗口(例如键盘锁)可能位于前面。使用onWindowFocusChanged(boolean)可以确定您的活动对用户可见(例如,恢复游戏)。developer.android.com/reference/android/app/...
J-柔

该解决方案并不能帮助确定前景/背景,如果有多个activities.Plz是指stackoverflow.com/questions/3667022/...
拉吉特里维迪


13

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的建议,您应尽量减少在活动的生命周期方法中执行的代码:

一种常见的模式是在活动和片段的生命周期方法中实现依赖组件的动作。但是,这种模式导致代码的组织不善,并导致错误扩散。通过使用支持生命周期的组件,您可以将依赖组件的代码移出生命周期方法,然后移入组件本身。

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


并将其添加为清单,例如:<application android:name =“。AnotherApp”>
Dan Alboteanu

9

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

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

我会考虑使用这种实现方式。从一项活动过渡到另一项活动仅需几毫秒。根据上一次活动消失的时间,可以考虑通过特定策略重新​​登录用户。
drindt

6

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

为您的应用程序中的所有Activity创建一个BaseActivity。

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()。


@kiran boghra:您的解决方案中是否存在误报?
Harish Vishwakarma

在这种情况下,可以完美地使用onStart()和onStop()函数。告诉您有关您的应用的信息
Pir Fahim Shah

6

使用ProcessLifecycleOwner非常简单

添加这些依赖

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


5

您可以使用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");
    }
}

然后在onCreate()您的Application类的上调用此代码:

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

这样,你就能捕捉到的事件ON_PAUSEON_STOP你的应用程序时,它会在后台这种情况发生。


4

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

我已经用简单的方法做到了。请按照以下说明检测应用程序的背景/前景阶段。

只需稍作解决,便可以实现。在这里,ActivityLifecycleCallbacks可以解救。让我逐步讲解。

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

    public class App extends Application implements 
        Application.ActivityLifecycleCallbacks {
    
        @Override
        public void onCreate() {
            super.onCreate();
            registerActivityLifecycleCallbacks(this);
        }
    }
  2. 如下所示,在清单中注册“ App”类<application android:name=".App"

  3. 当应用程序处于前台状态时,至少有一个处于启动状态的活动,而当应用程序处于后台时,则不会处于启动状态。

    在“ App”类中声明两个变量,如下所示。

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

    activityReferences将使活动数量保持在开始状态。isActivityChangingConfigurations是一个标志,用于指示当前“活动”是否正在经历配置更改(例如方向开关)。

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

    @Override
    public void onActivityStarted(Activity activity) {
        if (++activityReferences == 1 && !isActivityChangingConfigurations) {
            // App enters foreground
        }
    }
  5. 这是检测应用程序是否进入后台的方法。

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

怎么运行的:

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

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

A.onCreate()

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

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

然后用户按下“主页”按钮,

A.onPause()

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

如果用户按下活动B中的“主页”按钮而不是“返回”按钮,则仍然是相同的,activityReferences将是 0。因此,我们可以检测到App进入后台。

那么,它的作用是isActivityChangingConfigurations什么?在上述情况下,假设活动B改变了方向。回调顺序为

B.onPause()

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

B.onDestroy()

B.onCreate()

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

B.onResume()

这就是为什么我们要进行额外检查isActivityChangingConfigurations以避免活动在进行配置更改时发生的情况。


3

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

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

}


3

您可以使用:

受保护的void onRestart()

在新的启动和重新启动之间有所不同。

在此处输入图片说明


3

编辑2:我在下面编写的内容实际上无法正常工作。Google拒绝了一个包含对ActivityManager.getRunningTasks()的调用的应用程序。从文档中可以明显看出,此API仅用于调试和开发目的。我将有时间使用以下使用计时器的新方案来更新下面的GitHub项目时,立即对本帖子进行更新。

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

公认的和评分最高的答案并不是真正的最佳方法。评分最高的答案是isApplicationBroughtToBackground()的实现,无法处理应用程序的主Activity屈服于在同一Application中定义的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;
}

仅供参考,在onStart()中调用它可以避免在例如由于警报响起引发简单对话框时调用它。
Sky Kelsey 2012年

2

正确答案在这里

创建名称为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


1

我的解决方案受到@ 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;
}

1

我将它与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) {

            }
        });
    }
}

1

我知道有点晚了,但是我认为所有这些答案的确在遇到问题时遇到了一些问题,如下所示,并且效果很好。

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

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

每次活动都一直调用它。例如,如果我想检测用户在线状态,该如何使用
Maksim Kniazev

多数民众赞成在这个问题想要。仅当您转到主屏幕并返回任何活动时,它才会被调用。
阿米尔·齐亚拉蒂

如果您的意思是互联网连接,我认为最好在需要时进行检查。如果您需要调用api,请在致电前检查互联网连接。
Amir Ziarati

1

这似乎是Android中最复杂的问题之一,因为(截至撰写本文时)Android没有与iOS等效的applicationDidEnterBackground()applicationWillEnterForeground()回调。我用了一个届时AppState图书馆,是由放在一起@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页面上获得更多信息 。


1

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

做那里提到的一切。但是,与其Base Activity为每个活动以及将and覆盖为主要onResume(),而不是使用a 作为父项onPause,执行以下操作:

在您的应用程序类中,添加以下行:

registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks回调);

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

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


1

您可以借助ActivityLifecycleCallbacksComponentCallbacks2类似下面的内容轻松实现此目的。

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

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将获取回调。像下面这样。

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

希望这可以帮助。

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


1

由于我没有找到任何方法,该方法也可以在不检查时间戳的情况下处理旋转,因此我想我也分享了我们现在如何在应用程序中进行操作。这个答案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)
   }

而已。希望这可以帮助某人:)


1

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

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并捕获所需的事件。例如:

appForegroundStateLiveData.observeForever { state ->
    when(state) {
        AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
        AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
    }
}

0

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


我没有谈论数据库...是什么意思?
Joris Weimar

我支持你的答案。即使我们可以在暂停调用时将该标志值保存在数据库中,也不是一个好的解决方案
。– Sandeep P
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.