检查Android应用程序是否在后台运行

cpp*_*dev 308 android

根据背景,我的意思是用户目前看不到应用程序的任何活动?

Ido*_*lon 386

几种方法可以检测您的应用程序是否在后台运行,但只有一种方法是完全可靠的:

  1. 正确的解决方案(学分去,CommonsWareNeTeInStEiN)
    使用自己的应用程序的轨道知名度Activity.onPause,Activity.onResume方法.将"可见性"状态存储在其他类中.好的选择是你自己实现的Applicationa或a 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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    注册您的应用程序类AndroidManifest.xml:

    <application
        android:name="your.app.package.MyApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
    
    Run Code Online (Sandbox Code Playgroud)

    添加onPauseonResume每一个Activity项目(您可能为您的活动的共同祖先,如果你想,但如果你的活动已经从扩展MapActivity/ ListActivity等你仍然需要手工编写如下):

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }
    
    Run Code Online (Sandbox Code Playgroud)

     
    更新
    ActivityLifecycleCallbacks已在API级别14(Android 4.0)中添加.您可以使用它们来跟踪用户当前是否可以看到您的应用程序的活动.请查看 Cornstalks的回答,了解详情.


  2. 我以前提出以下解决方案的错误:

    您可以检测当前前景/后台应用程序,使用ActivityManager.getRunningAppProcesses()该应用程序返回RunningAppProcessInfo记录列表.要确定您的应用程序是否在前台检查RunningAppProcessInfo.importance字段中是否等于RunningAppProcessInfo.IMPORTANCE_FOREGROUNDwhile,RunningAppProcessInfo.processName则等于您的应用程序包名称.

    此外,如果您ActivityManager.getRunningAppProcesses()从应用程序UI线程调用它,它将重新启动IMPORTANCE_FOREGROUND您的任务,无论它是否实际位于前台.在后台线程中调用它(例如via AsyncTask),它将返回正确的结果.

    虽然这个解决方案可能有效(并且它确实在大多数情况下都有效)但我强烈建议不要使用它.这就是原因.正如Dianne Hackborn所写:

    这些API不适用于基于UI流程的应用程序,而是用于向用户显示正在运行的应用程序或任务管理器等.

    是的,内存中有一个列表用于这些事情.但是,它是在另一个进程中关闭,由与您分开运行的线程管理,而不是您可以指望的事情(a)及时看到做出正确的决定,或者(b)在您返回时保持一致的图像.此外,关于"下一个"活动的内容的决定总是在切换发生的时刻完成,直到确切的点(活动状态被短暂锁定以进行切换),我们才实际上确切知道接下来会是什么.

    此处的实施和全球行为不能保证在未来保持不变.

    我希望在我在SO上发布答案之前已经阅读了这篇文章,但希望我承认错误并不算太晚.

  3. 在其中一个答案中提到的另一个错误解决方案
    Droid-FuActivityManager.getRunningTasks用于其isApplicationBroughtToBackground方法.请参阅上面的Dianne的评论,也不要使用该方法.

  • Android是如此疯狂的残骸.没有人认为有人可能想要保留应用级数据?给我一个休息时间 (65认同)
  • 不幸的是,你的'正确'解决方案对我不起作用.考虑您在应用内循环活动.然后会发生这样的事情:你的'inForeground'标志是这样的:True,False(在第一个活动的onPause和第二个活动的onResume之间)然后再次为True,等等.然后你需要某种滞后. (28认同)
  • 如果您无法直接控制所有活动,则此解决方案不起作用.例如,如果您有来自第三方sdk的活动,或者甚至发起ACTION_VIEW意图. (14认同)
  • 看起来这个问题的真正答案是"你无法正确检查".所谓的"正确"解决方案最好是解决方法,因此使用ActivityLifecycleCallbacks.您仍然需要考虑在注册为"不在前台"的活动之间切换.它让我感到震惊,你不能检查这样简单的事情...... (8认同)
  • 要知道您是否按下主页按钮或其他应用程序已获得关注:1)实施_good解决方案_.2)在````OnStop````请求````isActivityVisible````. (4认同)
  • 同意_serine_,这不是一个万无一失的解决方案.一种情况是如果用户拉下通知面板,则不会调用`onPause`,`onStop`和`onResume`事件.那么,如果没有这些事件被解雇,你会怎么做?! (2认同)

Cor*_*lks 259

不要使用这个答案

user1269737的答案是正确的(谷歌/ Android批准)方式来做到这一点.去读他们的答案并给他们+1.

为了后人的缘故,我会在这里留下原来的答案.这是2012年最好的,但现在Android对此有适当的支持.

原始答案

关键是使用ActivityLifecycleCallbacks(请注意,这需要Android API级别14(Android 4.0)).只需检查已停止活动的数量是否等于已启动活动的数量.如果它们相同,那么您的应用程序正在落后.如果有更多已启动的活动,则您的应用程序仍然可见.如果恢复活动多于暂停活动,则您的应用程序不仅可见,而且还在前台.您的活动可以有3种主要状态,然后:可见和前景,可见但不在前景,不可见而不在前景(即在后台).

关于这种方法的真正好处是,它不具备异步的问题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;
    }
    */
}
Run Code Online (Sandbox Code Playgroud)

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

@Mewzer已经问了一些关于这种方法的好问题,我想在这个答案中为每个人回答:

onStop()在低记忆情况下不会被调用; 这是一个问题吗?

不.文件onStop()说:

请注意,在内存不足的情况下,可能永远不会调用此方法,在这种情况下,系统没有足够的内存来保持活动进程在调用onPause()方法后运行.

这里的关键是"让你的活动进程保持运行 ......"如果达到这种低内存状态,你的进程实际上就被杀死了(不仅仅是你的活动).这意味着这种检查backgrounded-ness的方法仍然有效,因为a)如果您的进程被终止,您无法检查背景,b)如果您的进程再次启动(因为创建了新活动),该成员变量(无论是否为静态)MyLifecycleHandler将被重置为0.

这适用于配置更改吗?

默认情况下,没有.您必须在清单文件中显式设置configChanges=orientation|screensize(|使用您想要的任何其他内容)并处理配置更改,否则您的活动将被销毁并重新创建.如果您未设置此项,则将按此顺序调用您的活动的方法:onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume.如您所见,没有重叠(通常,当在两者之间切换时,两个活动非常短暂地重叠,这就是这种背景检测方法的工作原理).为了解决这个问题,您必须进行设置,configChanges以便不破坏您的活动.幸运的是,我必须configChanges已经在我的所有项目中设置,因为我的整个活动在屏幕旋转/调整大小时被破坏是不可取的,所以我从来没有发现这是有问题的.(感谢dpimka为此刷新我的记忆并纠正我!)

一个说明:

当我在这个答案中说"背景"时,我的意思是"你的应用程序不再可见".Android活动可见但不在前台(例如,如果有透明通知叠加层).这就是我更新这个答案以反映这一点的原因.

重要的是要知道Android在切换前景没有任何东西的活动时会有一个奇怪的不确定时刻.出于这个原因,如果你在活动之间切换时检查你的应用程序是否在前台(在同一个应用程序中),你会被告知你不在前台(即使你的应用程序仍然是活动应用程序并且可见).

您可以在之后ActivityonPause()方法中检查您的应用是否在前台.请记住我刚才谈到的奇怪的状态. super.onPause()

您可以检查您的应用程序是可见的(即,如果它不是在后台)在你ActivityonStop()方法之后 super.onStop().

  • @Mewzer:我刚刚添加了一条您可能感兴趣的注释.具体来说,在`super.onStop()`之后检查`onStop()`中的背景.不要在`onPause()`中检查背景. (2认同)

use*_*737 134

GOOGLE解决方案 - 不像以前的解决方案那样是黑客.使用ProcessLifecycleOwner

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
    }

}
Run Code Online (Sandbox Code Playgroud)

在app.gradle中

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

  • 这肯定是正确的答案!它就像一个魅力:D (6认同)
  • 正如 doc 所说,“整个申请过程的 LifecycleOwner。请注意,如果您的应用程序有多个进程,则此提供程序不知道其他进程。`,这不适用于“多进程”应用程序,是否有一些我们可以优雅地实现的 api? (5认同)
  • @OnLifecycleEvent 注释已弃用。建议使用 DefaultLifecycleObserver 或 LifecycleEventObserver 代替。来源:https://developer.android.com/reference/androidx/lifecycle/OnLifecycleEvent (4认同)
  • 这很完美,我也进行了一些修改,以便可以在此类之外更轻松地访问前台/背景状态:`companion object {private var前台=假fun isForeground():布尔值{返回前台}}`具有“ ArchLifecycleApp.isForeground()”的前景状态 (2认同)
  • 哦,老兄,这比我以前的回答好得多。向我+1。我更新了答案,将人们指向您的答案。 (2认同)
  • 尽管这是一个正确的答案,但无需实现回调,但是您可以随时查询ProcessLifecycleOwner。检查/sf/answers/3687480331/ (2认同)

net*_*ein 17

Idolon的答案是容易出错的,而且更复杂,虽然在这里重复检查android应用程序是否在前台?这里从后台任务或服务确定当前前台应用程序

有一个更简单的方法:

所有活动扩展BaseActivity上:

protected static boolean isVisible = false;

 @Override
 public void onResume()
 {
     super.onResume();
     setVisible(true);
 }


 @Override
 public void onPause()
 {
     super.onPause();
     setVisible(false);
 }
Run Code Online (Sandbox Code Playgroud)

每当您需要检查您的应用程序活动是否在前台时,只需检查 isVisible() ;

要了解此方法,请检查并行活动生命周期的答案:活动并行生命周期

  • "偶像的答案很容易出错" - 不幸的是我不得不同意你的看法.根据Dianne Hackborn对Google网上论坛的评论,我更新了我的答案.请查看详细信息. (3认同)
  • 这也不是一个万无一失的解决方案.一种情况是如果用户拉下通知面板,则不会调用`onPause`,`onStop`和`onResume`事件.那么,如果没有这些事件被解雇,你会怎么做?! (2认同)

Kei*_*ati 16

启动支持库版本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
}
Run Code Online (Sandbox Code Playgroud)

然后只需查询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);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这是最好和最简单的方法,特别适用于使用 fcm 时代码的任何部分。 (3认同)

Juo*_*nis 15

从Android API 16开始,有一种简单的方法可以检查应用是否在前台.它可能不是万无一失,但Android上没有任何方法是万无一失的.当您的服务从服务器接收更新并且必须决定是否显示通知时,此方法足够好用(因为如果UI是前台,用户将在没有通知的情况下注意到更新).

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Run Code Online (Sandbox Code Playgroud)


Men*_*ris 11

我尝试了使用Application.ActivityLifecycleCallbacks和许多其他的推荐解决方案,但它们没有按预期工作.感谢Sarge,我想出了一个非常简单直接的解决方案,我将在下面介绍.

解决方案的关键是理解如果我们有ActivityA和ActivityB,并且我们从ActivityA调用ActivityB(而不是调用ActivityA.finish),那么onStart() ActivityA 之前调用ActivityB 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--;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们的程序的每个活动,我们应该重写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();
}
Run Code Online (Sandbox Code Playgroud)

有了这个逻辑,有两种可能的情况:

  1. stateCounter = 0 :已停止的数量与已启动的活动数量相等,这意味着应用程序正在后台运行.
  2. stateCounter > 0 :已启动的数量大于已停止的数量,这意味着应用程序正在前台运行.

注意:stateCounter < 0意味着有更多停止的活动而不是启动,这是不可能的.如果您遇到这种情况,那么这意味着您没有按照自己的意愿增加/减少计数器.

你准备好了.您应该检查您的应用程序是否在内部onStop().


Com*_*are 9

没有办法,没有你自己跟踪它,以确定你的任何活动是否可见.也许你应该考虑问一个新的StackOverflow问题,解释你试图通过用户体验实现什么,所以我们也许可以给你另外的实现思路.

  • 在android中,我们有一个名为"Background Data"的设置.当应用程序在后台运行时,此设置将关闭任何后台数据连接.我想为我的应用程序实现"后台数据"切换,所以当我的任何活动都不对用户可见时,我希望我的服务停止进行任何数据传输,但是当我的一个活动恢复时,我想恢复数据传输 (2认同)

Roh*_*rya 5

您可以使用ComponentCallbacks2来检测应用程序是否在后台.BTW此回调仅适用于API级别14(冰淇淋三明治)及以上.

您将调用该方法:

public abstract void onTrimMemory (int level)

如果等级是ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN应用程序在后台.

您可以实现此接口的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
     }
   }
}
Run Code Online (Sandbox Code Playgroud)


Key*_*Key 1

我建议阅读此页面:http://developer.android.com/reference/android/app/Activity.html

onStop()简而言之,您的 Activity 在被调用后就不再可见。

  • 我的应用程序中有大约 10 个活动。所以我想知道它们是否对用户可见。总之,我想知道我的应用程序作为一个整体是否在后台运行 (3认同)
  • 这是不正确的。您的 Activity 在“onStop”之前一直可见;在“onPause”和“onStop”之间,它“可见”,但“不在前台”。 (3认同)

归档时间:

查看次数:

203525 次

最近记录:

6 年 前