所谓背景,指的是应用程序的所有活动当前对用户都不可见。
有几种方法可以检测您的应用程序是否在后台运行,但只有一种是完全可靠的:
正确的解决方案(感谢Dan、CommonsWare和NeTeInStEiN)
通过使用Activity.onPause
、Activity.onResume
方法自己跟踪应用程序的可见性。将“可见性”状态存储在其他类中。好的选择是您自己实现的Application
或Service
(如果您想从服务中检查活动可见性,还有几个变种的解决方案)。
示例
实现自定义的Application
类(注意isActivityVisible()
静态方法):
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
在 AndroidManifest.xml
中注册你的应用程序类:
<application
android:name="your.app.package.MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name" >
为项目中的每个Activity
添加onPause
和onResume
(如果您想的话,可以为您的Activities创建一个共同的祖先,但是如果您的activity已经扩展自MapActivity
/ListActivity
等,则仍需要手动编写以下内容):
@Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
@Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
更新
ActivityLifecycleCallbacks 在 API level 14(Android 4.0)中添加。您可以使用它们来跟踪应用程序的活动是否当前对用户可见。有关详细信息,请参见下面的Cornstalks' answer。
错误的方法
我曾经建议以下解决方案:
您可以使用
ActivityManager.getRunningAppProcesses()
来检测当前前台/后台应用程序,它返回一个RunningAppProcessInfo
记录列表。要确定您的应用程序是否在前台,请检查RunningAppProcessInfo.importance
字段是否等于RunningAppProcessInfo.IMPORTANCE_FOREGROUND
,而RunningAppProcessInfo.processName
是否等于您的应用程序软件包名称。此外,如果您从您的应用程序UI线程调用
ActivityManager.getRunningAppProcesses()
,它将为您的任务返回importanceIMPORTANCE_FOREGROUND
,无论它实际上是否在前台。在后台线程中调用它(例如通过AsyncTask
),它将返回正确的结果。
虽然这个解决方案可能有效(实际上大多数情况下都能正常运行),但我强烈建议不要使用它。以下是原因。正如Dianne Hackborn所写的:
这些API不是为了让应用程序基于其UI流而存在,而是用于显示用户正在运行的应用程序、任务管理器或类似功能。
是的,有一个在内存中保存这些内容的列表。但是,它在另一个进程中,由比您运行的线程管理,并且不是您可以依赖的东西(a)及时看到以做出正确的决定或(b)在您返回时具有一致的图片。加上决定“下一个”活动要去哪里总是在切换发生的地方做出的,直到那个确切的点(状态被简短地锁定以执行切换的地方)我们才真正知道下一步会发生什么。
而且,这里的实现和全局行为不能保证在未来保持不变。
我希望在我在SO上发布答案之前能读到这些内容,但希望现在还不算太晚承认我的错误。
另一个错误的解决方案
Droid-Fu 库中提到的一个答案使用了 ActivityManager.getRunningTasks
的isApplicationBroughtToBackground
方法。看看Dianne上面的评论并且也不要使用那个方法。
谷歌解决方案 - 不像以前的解决方案那样是黑客行为。使用ProcessLifecycleOwner
Kotlin:
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
// App in foreground
}
}
Java:
public class ArchLifecycleApp extends Application implements LifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded() {
//App in background
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded() {
// App in foreground
}
}
在app.gradle文件中
dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.0"
//New Android X dependency is this -
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
}
allprojects {
repositories {
...
google()
jcenter()
maven { url 'https://maven.google.com' }
}
}
你可以在这里阅读有关生命周期相关架构组件的更多信息- https://developer.android.com/topic/libraries/architecture/lifecycle
user1269737的答案是正确的(Google / Android批准的)方法,请阅读他们的答案并给予+1。
我将保留我的原始答案以供后人参考。这是2012年最好的可用方法,但现在Android已经为此提供了适当的支持。
关键是使用ActivityLifecycleCallbacks
(请注意,这需要Android API级别14(Android 4.0))。只需检查停止的活动数是否等于启动的活动数。如果它们相等,则您的应用正在后台运行。如果有更多的启动活动,则您的应用仍然可见。如果恢复的活动比暂停的活动多,则您的应用不仅可见,而且还在前台。因此,您的活动可以处于三种主要状态:可见且在前台,可见但不在前台以及不可见且不在前台(即在后台)。
这种方法的真正好处在于它没有异步问题getRunningTasks()
,但您也不必修改应用程序中的每个Activity
以在onResumed()
/onPaused()
中设置/取消设置某些内容。它只是几行代码,是自包含的,并且适用于整个应用程序。此外,也不需要奇怪的权限。
MyLifecycleHandler.java:
public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
// I use four separate variables here. You can, of course, just use two and
// increment/decrement them instead of using four and incrementing them all.
private int resumed;
private int paused;
private int started;
private int stopped;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
}
@Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
}
@Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
// If you want a static function you can use to check if your application is
// foreground/background, you can use the following:
/*
// Replace the four variables above with these four
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
// And these two public static functions
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
*/
}
MyApplication.java:
// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
@Override
public void onCreate() {
// Simply add the handler, and that's it! No need to add any code
// to every activity. Everything is contained in MyLifecycleHandler
// with just a few lines of code. Now *that's* nice.
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
@Mewzer提出了一些有关此方法的好问题,我希望在这个答案中回应每个人:
onStop()
在低内存情况下不会被调用; 这是一个问题吗?
不。 onStop()
的文档说:
请注意,在低内存情况下,系统没有足够的内存来保持您的活动进程在其
onPause()
方法被调用后运行时,可能永远不会调用此方法。
关键在于“保持您的活动进程运行...”如果达到了这种低内存情况,您的进程实际上已经被终止(不仅仅是您的活动)。这意味着检查后台状态的这种方法仍然有效,因为a)如果您的进程被杀死,无论如何都无法检查后台状态,b)如果您的进程重新启动(因为创建了新活动),则 MyLifecycleHandler
的成员变量(无论是静态还是非静态)将被重置为 0
。
这对配置更改有效吗?
默认情况下,不是。您必须在清单文件中显式设置configChanges = orientation | screensize
(使用 |
和其他任何内容),并处理配置更改,否则您的活动将被销毁并重新创建。如果您不设置此项,则您的活动方法将按以下顺序调用: onCreate -> onStart -> onResume ->(现在旋转) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume
。正如您所看到的,没有重叠(通常,两个活动在切换时会非常短暂地重叠,这就是此后台检测方法的工作方式)。为了解决这个问题,您必须设置configChanges
,以便您的活动不会被销毁。幸运的是,我已经在所有项目中设置了configChanges
,因为屏幕旋转/调整大小时整个活动都被销毁是不可取的,因此我从未发现这是有问题的。(感谢dpimka让我记起来并纠正我!)
一个注释:
当我在这个答案中说“背景”时,我的意思是“您的应用程序不再可见”。 Android活动可以可见但不在前台(例如,如果有透明通知覆盖)。这就是为什么我更新了这个答案以反映这一点。
重要的是要知道,在切换活动时,Android有一个奇怪的中间状态,此时没有任何东西在前台。因此,如果您在活动之间(在同一个应用程序中)检查应用程序是否在前台,则会告诉您不在前台(即使您的应用程序仍然是活动的应用程序并且可见)。
在Activity
的onPause()
方法中的super.onPause()
之后,您可以检查您的应用是否在前台。请记住我刚刚谈到的奇怪的半途状态。Activity
的onStop()
方法中的super.onStop()
之后,您可以检查您的应用是否可见(即它不在后台)。super.onStop()
之后的onStop()
中检查后台运行,不要在onPause()
中检查后台运行。 - Cornstalks自支持库版本 26 起,您可以使用 ProcessLifecycleOwner。只需像这里描述的那样将其添加到依赖项中,例如:
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}
每当您想要查询应用程序状态时,只需查询ProcessLifecycleOwner
即可,例如:
// Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;
// Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
dependencies { def lifecycle_version = "2.2.0" implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" }
- Nesho Neshev自 Android API 16 开始,有一种简单的方法可以检查应用程序是否处于前台。它可能不是百分之百可靠的,但在Android上没有任何方法是完美无缺的。当您的服务从服务器接收更新并需要决定是否显示通知时,此方法足够好用了(因为如果 UI 处于前台,用户将会注意到更新而无需通知)。
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
startService
出现问题。 - Shenk虽然check android application is in foreground or not?以及Determining the current foreground application from a background task or service中出现过Idolon的答案,但是该答案容易出错并且更加复杂。这里有一个更简单的方法:
在所有Activity都继承的BaseActivity中:
protected static boolean isVisible = false;
@Override
public void onResume()
{
super.onResume();
setVisible(true);
}
@Override
public void onPause()
{
super.onPause();
setVisible(false);
}
每当您需要检查应用程序中的任何活动是否在前台时,只需检查 isVisible()
;
要了解此方法,请查看并比较活动生命周期的答案:Activity side-by-side lifecycle
onPause
、onStop
,也不会调用onResume
事件。那么如果没有触发这些事件,你该怎么办呢?! - user4750643我尝试了使用Application.ActivityLifecycleCallbacks和其他许多建议的解决方案,但它们并没有按预期工作。感谢Sarge,我想出了一个相当简单和直接的解决方案,如下所述。
该解决方案的关键在于理解以下事实:如果我们有ActivityA和ActivityB,并且我们从ActivityA调用ActivityB(而不是调用
ActivityA.finish
),那么ActivityB的onStart()
将在ActivityA的onStop()
之前被调用。
这也是onStop()
和onPause()
之间的主要区别,这些文章中没有提到。
所以基于这个Activity的生命周期行为,您可以简单地计算程序中onStart()
和onPause()
分别被调用了多少次。请注意,对于您程序中的每个Activity
,您必须重写onStart()
和onStop()
,以便增加/减少用于计数的静态变量。以下是实现此逻辑的代码。请注意,我使用了一个扩展了Application
的类,因此不要忘记在Manifest.xml
中声明,即在Application标签中声明:android:name=".Utilities"
,尽管它也可以使用一个简单的自定义类来实现。
public class Utilities extends Application
{
private static int stateCounter;
public void onCreate()
{
super.onCreate();
stateCounter = 0;
}
/**
* @return true if application is on background
* */
public static boolean isApplicationOnBackground()
{
return stateCounter == 0;
}
//to be called on each Activity onStart()
public static void activityStarted()
{
stateCounter++;
}
//to be called on each Activity onStop()
public static void activityStopped()
{
stateCounter--;
}
}
现在,在我们程序的每个活动中,我们应该重写 onStart()
和 onStop()
方法,并按照下面所示进行增量/减量:
@Override
public void onStart()
{
super.onStart();
Utilities.activityStarted();
}
@Override
public void onStop()
{
Utilities.activityStopped();
if(Utilities.isApplicationOnBackground())
{
//you should want to check here if your application is on background
}
super.onStop();
}
根据这个逻辑,有两种可能的情况:
stateCounter = 0
:停止的数量与启动的活动数量相等,这意味着应用程序正在后台运行。stateCounter > 0
:启动的数量大于停止的数量,这意味着应用程序正在前台运行。注意:stateCounter < 0
表示停止的活动数量多于启动的活动数量,这是不可能的。如果您遇到这种情况,则意味着您没有按照应该进行的增加/减少计数器。
现在你可以开始了。你应该在 onStop()
方法中检查你的应用程序是否处于后台运行状态。
if(Utilities.isApplicationOnBackground()) …
移到 Utilities
中。否则,只有特定的活动才能对此事件做出反应。 - Display Name除非您自己跟踪,否则没有其他方法可以确定您的任何活动是否可见。也许您应该考虑提出一个新的StackOverflow问题,解释一下您尝试从用户体验中实现什么,这样我们就可以给您提供替代的实现思路。
Activity
继承并实现生命周期方法的类MyActivityClass
,并使所有活动都从MyActivityClass
继承。但是,这对于PreferenceActivity
或MapActivity
不起作用(请参见此问题)。 - Guillaume BrunerieComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
,则会调用以下方法:
public abstract void onTrimMemory (int level)
此时应用处于后台状态。activity
、service
等组件中。public class MainActivity extends AppCompatActivity 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) {
// app is in background
}
}
}
在@Cornstalks的回答基础上,增加了一些有用的功能。
额外的功能:
App.java
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}
AppLifecycleHandler.java
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;
private final String DebugName = "AppLifecycleHandler";
private boolean isVisible = false;
private boolean isInForeground = false;
private static AppLifecycleHandler instance;
public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}
return instance;
}
private AppLifecycleHandler() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
@Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}
// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);
// take some action on change of visibility
}
private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}
// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);
// take some action on change of in foreground
}
public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}
public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}
@Override protected void onApplicationSentToBackground() { }
- Chuck D