我试图写一个应用程序,做一些具体的时候,它被带回前台后一段时间。是否有一种方法可以检测应用程序是被发送到后台还是被带到前台?


当前回答

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

2018年3月更新:现在有了更好的解决方案。看到ProcessLifecycleOwner。您将需要使用新的体系结构组件1.1.0(目前的最新版本),但它是专门为此设计的。

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

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

永远不要假设一个代码片段就是你想要的解决方案,这是不可能的;更好的做法是,试着理解它是做什么的,以及它为什么这么做。

MemoryBoss类实际上从未被我使用过,它只是一段碰巧工作的伪代码。

除非有充分的理由不使用新的体系结构组件(确实有一些,特别是如果您的目标是超级老的api),否则就继续使用它们。它们远非完美,但ComponentCallbacks2也不是。

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

此外,有些人问过如何检测你什么时候回来。

下面解释了我能想到的最简单的方法,但由于有些人不熟悉它,所以我在这里添加了一些伪代码。假设你有YourApplication类和MemoryBoss类,在你的类BaseActivity扩展了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块中的代码只会执行一次,即使你去了另一个活动,新的活动(也扩展了BaseActivity)将报告wasInBackground为false,所以它不会执行代码,直到onmemorytrim被调用并且标志被再次设置为true。

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

如果你的手机锁定屏幕,而你的应用程序是可见的(比如你的设备锁定nn分钟后),这个回调不会被调用(或不总是),因为锁定屏幕只是在顶部,但你的应用程序仍然“运行”,尽管覆盖。 如果您的设备内存相对较低(并且处于内存压力之下),操作系统似乎会忽略这个调用,直接进入更关键的级别。

现在,取决于你知道你的应用什么时候进入后台有多重要,你可能需要也可能不需要扩展这个解决方案,同时跟踪活动生命周期等等。

只要记住以上几点,并拥有一个优秀的QA团队;)

更新结束

可能晚了,但在冰淇淋三明治(API 14)及以上有一个可靠的方法。

当你的应用没有更多可见的UI时,一个回调被触发。可以在自定义类中实现的回调称为ComponentCallbacks2(是的,带有一个2)。此回调仅在API级别14(冰淇淋三明治)及以上可用。

你基本上得到一个方法的调用:

public abstract void onTrimMemory (int level)

级别是20或更多

public static final int TRIM_MEMORY_UI_HIDDEN

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

引用官方文件:

onTrimMemory(int)的级别:进程已经显示了一个用户界面,并且不再这样做。这时候应该释放大量的UI分配,以便更好地管理内存。

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

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

这正是你一开始就想知道的。

你怎么确定你什么时候回来?

好吧,这很容易,我相信你有一个“BaseActivity”,所以你可以使用你的onResume()标记事实,你回来了。因为只有当您实际接收到对上述onTrimMemory方法的调用时,您才会说您没有返回。

它的工作原理。你不会得到假阳性。如果一项活动恢复了,那么你就百分百地回来了。如果用户再次返回,您将得到另一个onTrimMemory()调用。

您需要订阅您的Activities(或者更好的是,定制类)。

保证你总是收到这个的最简单的方法是创建一个简单的类,像这样:

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

如果你创建了一个接口,你可以在If中添加一个else,并实现在API 14以下的任何东西中使用的ComponentCallbacks(没有2)。该回调只有onLowMemory()方法,当你进入后台时不会被调用,但你应该使用它来修剪内存。

现在启动你的App,按home键。你的onTrimMemory(最终int级别)方法应该被调用(提示:添加日志记录)。

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

/** *此方法用于模拟过程环境。它将 *永远不要在生产Android设备上调用,那里有进程 *通过简单地杀死它们来移除;没有用户代码(包括此回调) 执行*。 * /

因此,除非你真的遇到了不想再注册的情况,否则你可以安全地忽略它,因为你的进程在操作系统级已经死亡了。

如果你决定在某些时候取消注册(例如,如果你为你的应用程序提供了一个关闭机制来清理和死亡),你可以这样做:

unregisterComponentCallbacks(mMemoryBoss);

就是这样。

其他回答

2021年11月更新

实际设置如下

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

class AppLifecycleListener : DefaultLifecycleObserver {

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

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

依赖关系

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

原来的答案

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

ProcessLifecycleOwner将分派ON_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())

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

使用此特性还需要依赖项:

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

在你的应用程序中添加回调并检查根活动,如下所示:

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

由于我没有找到任何方法,它也处理旋转而不检查时间戳,我想我也分享我们现在如何在我们的应用程序中这样做。 对这个答案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)
   }

就是这样。希望这能帮助到一些人:)

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

  public class ForegroundLifecycleObserver implements LifecycleObserver {

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

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

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

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

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

然后在你的Application类的onCreate()你调用这个:

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

有了它,你将能够捕捉应用程序在后台运行时发生的ON_PAUSE和ON_STOP事件。

我们可以使用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 */ }
    }
}