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

iHo*_*rse 369 android background foreground

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

Mar*_*ini 184

2018年:Android通过生命周期组件本机支持此功能.

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

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

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

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

这个MemoryBoss类从来没有像我这里所写的那样实际使用过,它只是一段伪代码,恰好起作用.

除非你有充分的理由不使用新的架构组件(并且有一些,特别是如果你的目标是超级旧的api),那么继续使用它们.它们远非完美,但两者都不是ComponentCallbacks2.

更新/注释(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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我推荐onStart,因为Dialogs可以暂停一个活动,所以我打赌你不希望你的应用程序认为"它转到后台",如果你所做的只是显示全屏对话框,但你的里程可能会有所不同.

就这样.if块中的代码只会被执行一次,即使你转到另一个活动,新的(也会extends BaseActivity)将报告wasInBackgroundfalse不会执行代码,直到onMemoryTrimmed被调用并且标志再次设置为true.

希望有所帮助.

更新/注释(2015年4月):在您对此代码进行全部复制和粘贴之前,请注意我发现了几个可能不是100%可靠的实例,必须与其他方法结合才能获得最佳结果.值得注意的是,有两个已知的实例,其中onTrimMemory不保证执行回调:

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

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

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

请记住以上内容并拥有一支优秀的QA团队;)

更新结束

可能会迟到,但在冰淇淋三明治(API 14)和上面有一个可靠的方法.

事实证明,当您的应用没有更多可见的UI时,会触发回调.您可以在自定义类中实现的回调称为ComponentCallbacks2(是的,带有两个).此回调仅适用于API级别14(冰淇淋三明治)及以上.

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

public abstract void onTrimMemory (int level)
Run Code Online (Sandbox Code Playgroud)

该级别具体为20或更高

public static final int TRIM_MEMORY_UI_HIDDEN
Run Code Online (Sandbox Code Playgroud)

我一直在测试它,它总是有效,因为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.
    }
}
Run Code Online (Sandbox Code Playgroud)

为了使用它,在你的应用程序实现中(你有一个,RIGHT?),做类似的事情:

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

如果你创建一个Interface你可以添加一个else,if并实现ComponentCallbacks(没有2)在API 14以下的任何东西.回调只有onLowMemory()方法,并没有被调用当你去后台,但你应该用它来修剪内存.

现在启动您的应用程序并按回家.onTrimMemory(final int level)应调用您的方法(提示:添加日志记录).

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

/**
 * 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.
 */
Run Code Online (Sandbox Code Playgroud)

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

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

unregisterComponentCallbacks(mMemoryBoss);
Run Code Online (Sandbox Code Playgroud)

就是这样.

  • 我从一年前开始使用这种方法,对我来说一直都很可靠.知道其他人也使用它是很好的.我只是使用`level> = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN`来避免更新中的问题,第2点.关于第1点,它不是我的问题,因为应用程序没有真正去后台,所以就是这样它应该工作. (3认同)
  • 感谢Martin,使用ComponentCallbacks2.onTrimMemory()(与ActivityLifecycleCallbacks结合使用)是迄今为止我发现的唯一可靠的解决方案!对于有兴趣的人,请参阅我提供的答案. (2认同)

d60*_*402 174

以下是我设法解决这个问题的方法.它的前提是在活动转换之间使用时间参考很可能提供足够的证据证明应用程序已经"背景化".

首先,我使用了一个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;
    ...
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

    myApp.stopActivityTransitionTimer();
}

@Override
public void onPause()
{
    super.onPause();
    ((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
Run Code Online (Sandbox Code Playgroud)

因此,在用户只是在应用程序的活动之间导航的情况下,离开活动的onPause()启动计时器,但几乎立即输入的新活动在计时器达到最大转换时间之前取消计时器.所以wasInBackground会是假的.

另一方面,当一个Activity从Launcher到达前台时,设备唤醒,结束通话等,很可能是在此事件之前执行的计时器任务,因此wasInBackground被设置为true.

  • 这是一个很好而简单的实现.但是我相信这应该在onStart/onStop而不是onPause/onResume上实现.即使我启动一个部分覆盖活动的对话框,也会调用onPause.关闭对话框实际上会调用onResume使其看起来好像应用程序刚刚到达前台 (27认同)
  • 我希望使用此解决方案的变体.关于上述对话的观点对我来说是一个问题,所以我尝试了@ Shubhayu的建议(onStart/onStop).然而,这并没有帮助,因为当进入A-> B时,活动B的onStart()在活动A的onStop()之前被调用. (7认同)
  • 嗨d60402,你的回答真的很有用..非常感谢你的回复...小通知.. MyApplication应该在Manifest文件应用标签中提到像android:name ="MyApplication",否则app崩溃...只是为了帮助有人喜欢我 (4认同)
  • 伟大的程序员的标志,简单解决我遇到的最复杂的问题之一. (2认同)
  • 真棒解决方案!谢谢.如果有人收到"ClassCastException"错误,那么您可能错过了将其添加到Manifest.xml中的应用程序标记中<application android:name ="your.package.MyApplication" (2认同)
  • 好的解决方案 唯一的问题是当回来时锁屏幕isInBackground变为真.有什么办法可以避免吗? (2认同)

Fre*_*ula 138

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

}
Run Code Online (Sandbox Code Playgroud)

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

但是有希望.

  • 它适用于多个活动,但对于一个 - onrotate将指示所有活动已消失或在后台 (7认同)
  • @deadfish检查答案顶部提供的I/O链接.您可以检查活动停止和开始之间的时间差距,以确定您是否真的去过背景.实际上,这是一个很棒的解决方案. (3认同)
  • 这很完美!我已经尝试过这么多奇怪的解决方案,这些解决方案有很多缺陷......非常感谢!我一直在寻找这个. (2认同)
  • @Shyri你是对的,但这是这个解决方案的一部分,所以需要担心.如果firebase依赖于此,我认为我平庸的应用程序也可以:)很棒的答案BTW. (2认同)
  • 有Java解决方案吗?这是科特林。 (2认同)

vok*_*lam 100

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

ProcessLifecycleOwner将派出ON_START,ON_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())
Run Code Online (Sandbox Code Playgroud)

根据源代码,当前的延迟值是700ms.

使用此功能还需要dependencies:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
Run Code Online (Sandbox Code Playgroud)

  • 请注意,您需要添加生命周期依赖项`实现"android.arch.lifecycle:extensions:1.0.0"和"annotationProcessor"android.arch.lifecycle:编译器:1.0.0"`来自Google的存储库(即`google( )`) (10认同)
  • 当调用外部活动(例如文件选择器)时,这不会按预期工作,在这种情况下,当打开选择器时,应用程序将被通知为后台,当选择器关闭时,应用程序将再次被通知为前台。有什么办法吗? (4认同)

Swi*_*ind 90

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

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

有关详细信息,请查看Android:解决方案,以检测Android应用程序何时进入后台并返回前台,而不使用getRunningTasks或getRunningAppProcesses.

  • 然而,正如其他人指出的那样,这种方法会导致误报,因为在同一应用程序中的活动之间进行转换时也会调用这些方法. (168认同)
  • onPause和onResume是特定于Activity的.不适用.当应用程序放在后台然后恢复时,它将恢复在进入后台之前所处的特定活动.这意味着您需要在应用程序的所有活动中实现从后台恢复所需的任何操作.我相信最初的问题是为Application而不是Activity寻找类似"onResume"的东西. (16认同)
  • 它比那更糟糕.我尝试过,有时在手机锁定时调用onResume.如果您在文档中看到onResume的定义,您会发现:请记住,onResume不是您的活动对用户可见的最佳指标; 诸如键盘锁之类的系统窗口可以在前面.使用onWindowFocusChanged(boolean)可以确定您的活动对用户可见(例如,恢复游戏).http://developer.android.com/reference/android/app/Activity.html#onResume%28 %29 (9认同)
  • 我无法相信没有为这种共同需求提供适当的API.最初我以为onUserLeaveHint()会剪掉它,但是你不知道用户是否要离开应用程序 (4认同)
  • 链接中发布的解决方案不使用onResume/onPause,而是onBackPressed,onStop,onStart和onWindowsFocusChanged的组合.它确实适合我,我有一个相当复杂的UI层次结构(带抽屉,动态视图等) (2认同)

ric*_*kul 68

根据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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class MyApp extends android.app.Application {

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

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

    }

}
Run Code Online (Sandbox Code Playgroud)


Nic*_*lov 61

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

想法是,当处于前台时,Android总是在停止前一个活动之前启动新活动.这不是保证,但它是如何工作的.BTW,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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑:根据评论,我们也在更高版本的代码中移动到onStart().此外,我正在添加超级调用,这些调用在我的初始帖子中缺失,因为这更像是一个概念而不是一个工作代码.

  • 如果用户更改第一个活动的方向会发生什么?它会报告应用程序进入后台,这是不正确的.你如何处理这种情况? (3认同)
  • 这是最可靠的答案,尽管我使用onStart而不是onResume. (2认同)

Emi*_*mil 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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法在Droid-Fu(现在称为Ignition)框架中找到.

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

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

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

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

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

  • Google可能会拒绝使用ActivityManager.getRunningTasks()的应用.该文档指出它只是敌人的目的.http://developer.android.com/reference/android/app/ActivityManager.html#getRunningTasks%28int%29 (18认同)
  • 用户不会对使用强大的权限android.permission.GET_TASKS感到高兴. (7认同)
  • 在API级别21中不推荐使用getRunningTasks. (6认同)

Har*_*eet 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
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

Old*_*664 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 {
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 导航到另一个活动时也会调用onUserLeaveHint (19认同)
  • 例如,当电话呼入进入并且呼叫活动变为活动时,不会调用onUserLeaveHint,因此这也有边缘情况 - 也可能存在其他情况,因为您可以向意图添加标志以抑制onUserLeaveHint调用.http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NO_USER_ACTION (3认同)

Ren*_*eno 13

ActivityLifecycleCallbacks可能是有意义的,但它没有很好的文档记录.

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

  • 由于api等级14 = (11认同)

mat*_*dev 11

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}
Run Code Online (Sandbox Code Playgroud)

根据Google的建议,您应该最小化在生命周期活动方法中执行的代码:

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

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


Cyn*_*era 8

在您的应用程序中添加回调并检查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();
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)


kir*_*hra 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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在使用此BaseActivity作为所有Activity的超类,如MainActivity扩展BaseActivity,并在启动应用程序时调用onAppStart,并在应用程序从任何屏幕进入后台时调用onAppPause().


Ege*_*glu 6

使用ProcessLifecycleOwner非常简单

添加这些依赖项

implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
Run Code Online (Sandbox Code Playgroud)

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的基础活动中:

override fun onCreate() {
        super.onCreate()

        ProcessLifecycleOwner.get()
                .lifecycle
                .addObserver(
                        ForegroundBackgroundListener()
                                .also { appObserver = it })
    }
Run Code Online (Sandbox Code Playgroud)

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


Kom*_*are 6

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

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

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

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

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

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

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

    private int activityReferences = 0;
    private boolean isActivityChangingConfigurations = false;
    
    Run Code Online (Sandbox Code Playgroud)

    activityReferences将保持开始状态的活动数量计数。isActivityChangingConfigurations是一个标志,用于指示当前 Activity 是否正在经历配置更改,如方向切换。

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

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

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

这个怎么运作:

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

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

A.onCreate()

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

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)(应用进入后台)

万一,如果用户从 Activity B 按下 Home 按钮而不是 Back 按钮,它仍然是相同的并且 activityReferences 将是0。因此,我们可以检测到应用程序进入后台。

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

B.onPause()

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

B.onDestroy()

B.onCreate()

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

B.onResume()

这就是为什么我们有一个额外的检查isActivityChangingConfigurations来避免 Activity 经历配置更改时的情况。


AYB*_*BTU 5

您可以使用:

protected void onRestart()

区分新的启动和重新启动。

在此处输入图片说明


Alé*_*lho 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
Run Code Online (Sandbox Code Playgroud)

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