如何检测Android应用程序何时进入后台并返回前台 [英] How to detect when an Android app goes to the background and come back to the foreground

查看:78
本文介绍了如何检测Android应用程序何时进入后台并返回前台的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

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

2018 年 3 月更新:现在有了更好的解决方案.请参阅ProcessLifecycleOwner.您将需要使用新的架构组件 1.1.0(此时是最新的),但它专门设计用于执行此操作.

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

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

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

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

除非有正当理由让您不使用新的架构组件(并且有一些,尤其是当您针对超旧的 api 时),然后继续使用它们.它们远非完美,但 ComponentCallbacks2 也不是.

UPDATE/NOTES(2015 年 11 月):人们提出了两个评论,首先是应该使用 >= 而不是 == 因为文档指出您不应该检查确切的值.这在大多数情况下都没有问题,但请记住,如果您关心在应用程序进入后台时某事,您将不得不使用 == and 还可以将其与其他解决方案(例如 Activity Lifecycle 回调)结合使用,否则您可能无法获得您想要的效果.这个例子(这发生在我身上)是,如果你想在进入后台时使用密码屏幕锁定你的应用程序(比如 1Password,如果你熟悉它),你可能会不小心如果您的内存不足并且突然测试 >= TRIM_MEMORY,请锁定您的应用程序,因为 Android 会触发 LOW MEMORY 调用,并且该调用高于您的调用.所以要小心你测试的方式/内容.

另外,也有人问过如何检测你什么时候回来.

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

@Override受保护的无效 onStart() {super.onStart();如果(mApplication.wasInBackground()){//在这里,当您的应用程序从后台恢复时,您只需要调用一次您希望发生的代码mApplication.setWasInBackground(false);}}

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

仅此而已.if 块中的代码将只执行一次,即使你转到另一个活动,新的活动(也扩展 BaseActivity)会报告 wasInBackgroundfalse 所以它不会执行代码,直到 onMemoryTrimmed 被调用并且标志再次设置为 true.>

希望有所帮助.

更新/注意事项(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() 调用.

您需要订阅您的活动(或者更好的是,自定义类).

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

公共类 MemoryBoss 实现了 ComponentCallbacks2 {@覆盖public void onConfigurationChanged(final Configuration newConfig) {}@覆盖公共无效 onLowMemory() {}@覆盖public void onTrimMemory(最终整数级别){如果(级别 == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){//我们在后台}//你不妨在这里实现一些内存清理,成为一个不错的 Android 开发者.}}

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

MemoryBoss mMemoryBoss;@覆盖公共无效 onCreate() {super.onCreate();如果 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {mMemoryBoss = new MemoryBoss();registerComponentCallbacks(mMemoryBoss);}}

如果你创建了一个 Interface,你可以向那个 if 添加一个 else 并实现 ComponentCallbacks(没有2) 用于 API 14 以下的任何内容.该回调只有 onLowMemory() 方法,并且在您进入后台时不会被调用,但您应该使用它来修剪记忆.

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

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

<块引用>

/*** 此方法用于模拟过程环境.它会* 永远不会在生产 Android 设备上调用,其中进程* 通过简单地杀死它们来移除;没有用户代码(包括这个回调)* 在这样做时被执行.*/

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

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

unregisterComponentCallbacks(mMemoryBoss);

就是这样.

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

解决方案

2018: Android supports this natively through lifecycle components.

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

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

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

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

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

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

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

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

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

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

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

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

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

Hope that helps.

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

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

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

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

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

END OF UPDATE

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

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

You basically get a call to the method:

public abstract void onTrimMemory (int level)

The Level is 20 or more specifically

public static final int TRIM_MEMORY_UI_HIDDEN

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

To quote the official docs:

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

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

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

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

How do you determine when you got back?

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

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

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

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

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

    @Override
    public void onLowMemory() {
    }

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

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

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

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

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

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

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

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

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

unregisterComponentCallbacks(mMemoryBoss);

And that's it.

这篇关于如何检测Android应用程序何时进入后台并返回前台的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆