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

时间:2010-09-08 11:04:47

标签: android

通过后台,我的意思是用户当前无法看到应用程序的任何活动吗?

33 个答案:

答案 0 :(得分:384)

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

  1. 正确的解决方案(积分转至DanCommonsWareNeTeInStEiN
    使用Activity.onPauseActivity.onResume方法自行跟踪应用程序的可见性。将“可见性”状态存储在其他类中。如果您想检查服务的活动可见性,那么您可以选择自己实施ApplicationService(此解决方案还有a few variations。)
    实施例
    实现自定义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" >
    

    onPauseonResume添加到项目中的每个Activity(如果您愿意,可以为您的活动创建一个共同的祖先,但如果您的活动已经从MapActivity / ListActivity等您还需要手动编写以下内容:

    @Override
    protected void onResume() {
      super.onResume();
      MyApplication.activityResumed();
    }
    
    @Override
    protected void onPause() {
      super.onPause();
      MyApplication.activityPaused();
    }
    


    更新
    API级别14(Android 4.0)中添加了ActivityLifecycleCallbacks。您可以使用它们来跟踪用户当前是否可以看到您的应用程序的活动。请查看下面的Cornstalks' answer了解详细信息。

  2. 错误的
    我曾经建议以下解决方案:

      

    您可以使用ActivityManager.getRunningAppProcesses()检测当前的前台/后台应用程序,该应用程序返回RunningAppProcessInfo条记录的列表。要确定您的应用程序是否位于前台,请检查RunningAppProcessInfo.importance字段是否与RunningAppProcessInfo.IMPORTANCE_FOREGROUND相等,而RunningAppProcessInfo.processName等于您的应用程序包名称。

         

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

    虽然这个解决方案可能有效(并且它确实在大部分时间都有效)但我强烈建议不要使用它。这就是原因。 As Dianne Hackborn wrote

      

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

         

    是的,这些东西在内存中保留了一个列表。但是,它是在另一个进程中关闭的,由与你的线程分开运行的线程管理,而不是你可以依赖的东西(a)及时看到做出正确的决定,或者(b)在你返回时有一致的画面。此外,关于“下一个”活动的内容的决定总是在切换发生的时刻完成,直到确切的点(活动状态被短暂锁定以进行切换),我们才实际上确切知道接下来会发生什么。

         

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

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

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

答案 1 :(得分:255)

请勿使用此答案

<强> user1269737's answer is the proper (Google/Android approved) way to do this。去阅读他们的答案并给他们+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;
    }
    */
}

<强> 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()方法后继续运行。

这里的关键是“保持活动的进程正在运行 ......”如果达到这种低内存状态,您的进程实际上已被杀死(不仅仅是您的活动)。这意味着这种检查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在切换没有任何东西在前台的活动时有一个奇怪的不确定时刻。出于这个原因,如果你在活动之间切换时检查你的应用程序是否在前台(在同一个应用程序中),你会被告知你不在前台(即使你的应用程序仍然是活动应用程序并且可见)。

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

您可以在<{em> Activity之后的onStop() super.onStop()方法中查看您的应用是否可见(即,它是否在后台)。< / p>

答案 2 :(得分:81)

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
    }

}
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

答案 3 :(得分:17)

Idolon的答案容易出错,而且更复杂,在此重复check android application is in foreground or not?Determining the current foreground application from a background task or service

有一种更简单的方法:

在所有活动扩展的 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

答案 4 :(得分:13)

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

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

答案 5 :(得分:11)

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

  

解决方案的关键是理解如果我们有ActivityA和ActivityB,并且我们从ActivityA调用ActivityB(而不是调用ActivityA.finish),那么ActivityB&#39; onStart()将会被称为之前 ActivityA onStop()

这也是onStop()onPause()之间的主要差异,在我读过的文章中没有提到过。

因此,根据此活动的生命周期行为,您可以简单地计算在您的计划中调用onStart()onPause()的次数。请注意,对于程序的每个 Activity必须覆盖onStart()onStop(),以便递增/递减用于计数的静态变量。下面是实现此逻辑的代码。请注意,我使用的是扩展Application的类,所以不要忘记在应用程序标记Manifest.xml内的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--;
    }
}

现在在我们程序的每个Activity上,我们应该覆盖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();
}

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

  1. stateCounter = 0:已停止的数量与已启动的活动数量相等,这意味着应用程序正在后台运行。
  2. stateCounter > 0:已启动的数量大于已停止的数量,这意味着应用程序正在前台运行。
  3. 注意:stateCounter < 0意味着有更多停止的活动而不是启动,这是不可能的。如果你遇到这种情况,那就意味着你没有按照你的意愿增加/减少计数器。

    你准备好了。您应该检查您的应用程序是否位于onStop()内的背景中。

答案 6 :(得分:10)

您可以使用ProcessLifecycleOwner开始使用支持库版本26,只需像描述的here一样将其添加到依赖项中即可,例如:

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

答案 7 :(得分:8)

如果您没有亲自跟踪,则无法确定您的任何活动是否可见。也许您应该考虑提出一个新的StackOverflow问题,解释您尝试从用户体验中获得的内容,以便我们为您提供其他实现方案。

答案 8 :(得分:5)

您可以使用ComponentCallbacks2来检测应用是否在后台。顺便说一句,这个回调在API等级14(冰淇淋三明治)及以上版本中仅仅可用

您将调用该方法:

public abstract void onTrimMemory (int level)

如果级别为ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN,则应用程序处于后台。

您可以将此界面实施为activityservice

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

答案 9 :(得分:4)

在@Cornstalks的基础上回答包含一些有用的功能。

额外功能:

  • 引入了单例模式,因此您可以在应用程序的任何位置执行此操作:AppLifecycleHandler.isApplicationVisible()和AppLifecycleHandler.isApplicationInForeground()
  • 添加了重复事件的处理(请参阅注释//对可见性更改采取一些措施,//对前景更改采取一些措施)

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

答案 10 :(得分:3)

我提出的最佳解决方案是使用计时器。

你已经在onPause()中启动了一个定时器,并在onResume()中取消了相同的定时器,有一个Timer实例(通常在Application类中定义)。计时器本身设置为在2秒后(或您认为合适的任何间隔)运行Runnable,当计时器触发时,您设置一个标记应用程序在后台的标志。

在取消计时器之前的onResume()方法中,您可以查询后台标志以执行任何启动操作(例如,开始下载或启用位置服务)。

此解决方案允许您在后台堆栈上有多个活动,并且不需要任何权限来实现。

如果您使用事件总线,此解决方案也可以正常工作,因为您的计时器可以简单地触发事件,并且应用的各个部分都可以做出相应的响应。

答案 11 :(得分:3)

如果您启用开发者设置&#34;请勿保持活动状态&#34; - 仅检查创建的活动的数量是不够的。您还必须检查 isSaveInstanceState 。我的自定义方法 isApplicationRunning()检查是android app正在运行:

这是我的工作代码:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
    private int created;
    private boolean isSaveInstanceState;
    private static AppLifecycleService instance;

    private final static String TAG = AppLifecycleService.class.getName();

    public static AppLifecycleService getInstance() {
        if (instance == null) {
            instance = new AppLifecycleService();
        }
        return instance;
    }

    public static boolean isApplicationRunning() {
        boolean isApplicationRunning = true;
        if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
            isApplicationRunning = false;
        }
        return isApplicationRunning;
    }

    public static boolean isSaveInstanceState() {
        return AppLifecycleService.getInstance().isSaveInstanceState;
    }

    public static int getCountCreatedActvities() {
        return AppLifecycleService.getInstance().created;
    }

    private AppLifecycleService() {
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        this.isSaveInstanceState = true;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ++created;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        --created;
    }

    @Override
    public void onActivityResumed(Activity activity) {   }

    @Override
    public void onActivityPaused(Activity activity) { }


    @Override
    public void onActivityStarted(Activity activity) { }

    @Override
    public void onActivityStopped(Activity activity) { }        

}

答案 12 :(得分:2)

我自己实现了ActivityLifecycleCallbacks。我正在使用SherlockActivity,但是对于普通的Activity类可能会有效。

首先,我正在创建一个界面,其中包含跟踪活动生命周期的所有方法:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

其次,我在我的应用程序类中实现了这个接口:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

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

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

第三,我正在创建一个从SherlockActivity扩展的类:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

第四,所有从SherlockActivity扩展的类,我替换为MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

现在,在logcat中,您将看到在MyApplication中进行的Interface实现中编写的日志。

答案 13 :(得分:2)

为了搭载CommonsWare和Key所说的内容,你可以扩展Application类,并让你的所有活动在onPause / onResume方法上调用它。这将允许您知道哪些活动可见,但这可能会更好地处理。

你能详细说明你的想法吗?当你说在后台运行时,你是说只是让你的应用程序仍在内存中,即使它当前不在屏幕上?您是否考虑过使用服务作为一种更持久的方式来管理您的应用程序?#/ p>

答案 14 :(得分:1)

唯一正确的解决方案:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}

答案 15 :(得分:1)

Offical docs:

系统区分前景和后台应用。 (出于服务限制的目的,背景的定义与内存管理所使用的定义不同;应用程序可能在后台与内存管理相关,但在前景中与其能力相关启动服务。)如果满足以下任何条件,则认为应用程序位于前台:

  1. 无论活动是开始还是暂停,它都有可见的活动。
  2. 它有一个前台服务。
  3. 另一个前台应用程序通过绑定到其中一个服务或使用其中一个内容提供程序连接到应用程序。例如,如果另一个应用程序绑定到它,应用程序就在前台:
    • IME
    • 壁纸服务
    • 通知听众
    • 语音或文字服务
  4. 如果这些条件都不成立,则认为应用程序在后台。

答案 16 :(得分:1)

由于尚未提及,我建议读者通过ProcessLifecycleOwner探索Android Architecture components

答案 17 :(得分:1)

当Dialog位于其上方时,活动会暂停,因此所有推荐的解决方案都是半解决方案。您还需要为对话框创建钩子。

答案 18 :(得分:0)

如何使用getApplicationState()。isInForeground()?

答案 19 :(得分:0)

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

简而言之,在调用onStop()后,您的活动将不再可见。

答案 20 :(得分:0)

我认为这个问题应该更清楚。什么时候?哪里?如果您的应用程序处于后台,您想要知道的具体情况是什么?

我只是按照自己的方式介绍我的解决方案 我通过在我的应用中的每个活动的RunningAppProcessInfo方法中使用onStop类的字段“重要性”来完成此操作,这可以通过为其他活动提供BaseActivity来简单地实现实现onStop方法来检查“重要性”的值。这是代码:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}

答案 21 :(得分:0)

您应该使用共享首选项来存储属性,并使用您活动中的service binding对其进行操作。如果你只使用绑定,(永远不会使用startService),那么你的服务只有在你绑定它时才会运行(绑定onResume和unbind onPause),这会使它只在前台运行,如果你想继续工作背景,你可以使用常规的开始停止服务。

答案 22 :(得分:0)

请参阅onActivityDestroyed函数中的注释。

适用于SDK目标版本14&gt; :

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {

    public static int active = 0;

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
    }

    @Override
    public void onActivityResumed(Activity activity) {
        Log.i("Tracking Activity Resumed", activity.getLocalClassName());
        active++;
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
        active--;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
        active--;

        // if active var here ever becomes zero, the app is closed or in background
        if(active == 0){
            ...
        }

    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
        active++;
    }
}

答案 23 :(得分:0)

在我看来,许多答案会引入大量代码并带来大量复杂性和不可读性。

当人们询问如何在ServiceActivity之间进行通信时,我通常建议使用LocalBroadcastManager

<强>为什么吗

好吧,引用文档:

  
      
  • 您知道您播放的数据不会离开您的应用,因此无需担心泄露私人数据。

  •   
  • 其他应用程序无法将这些广播发送到您的应用,因此您无需担心他们可以利用安全漏洞。

  •   
  • 它比通过系统发送全球广播更有效。

  •   

不在文档中:

  • 它不需要外部库
  • 代码很少
  • 实施和理解的速度很快
  • 没有自定义的自我实现的回调/超单例/进程内 无论如何......
  • ActivityApplication,...
  • 没有强引用

<强>描述

因此,您要检查Activity中是否有任何当前位于前台。您通常在ServiceApplication课程中执行该操作。

这意味着,您的Activity个对象成为信号的发送者(我正在/我正在关闭)。另一方面,Service成为Receiver

两个时刻,Activity会告诉您它是在前台还是在后台(是的只有两个......不是6个)。

Activity进入前台时,会触发onResume()方法(也会在onCreate()之后调用)。

Activity进入后方时,会调用onPause()

这些是您Activity应向您的Service发送信号以描述其状态的时刻。

如果有多个Activity,请记住,Activity首先进入后台,然后另一个进入前景。

情况如下:*

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Service / Application只会继续听取这些信号并采取相应行动。

代码(TLDR)

您的Service必须实施BroadcastReceiver才能收听信号。

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Receiver

中注册Service::onCreate()
@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Service::onDestroy()

中取消注册
@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

现在你的Activity必须传达他们的状态。

Activity::onResume()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

非常非常常见的情况

  

开发人员:我想从Service发送数据并更新Activity。如何检查Activity是否在前台?

通常无需检查Activity是否在前台。只需通过您LocalBroadcastManager的{​​{1}}发送数据即可。如果Service已启用,则会响应并采取行动。

对于这种非常常见的情况,Activity成为发件人,Service实现Activity

因此,在BroadcastReceiver中创建Receiver。在Activity中注册并在onResume()中取消注册。 无需使用其他生命周期方法

onPause()中定义Receiver行为(更新ListView,执行此操作,执行此操作,...)。

这样onReceive()只有在前景中才会收听,如果它在后面或被摧毁,则不会发生任何事情。

如果有多个Activity,则Activity处于启用状态将响应(如果它们也实现了Activity)。

如果所有人都在后台,没有人会回应,信号就会迷失。

通过指定信号ID,从Receiver通过Service发送数据(请参阅上面的代码)。

答案 24 :(得分:0)

这个旧帖子的另一个解决方案(对于那些可能有帮助的帖子):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}

答案 25 :(得分:0)

B

答案 26 :(得分:0)

如果您想知道特定活动是否正在进行,以及您是否是无法直接访问该应用程序的SDK,则没有一个答案适合特定情况。对我来说,我处于后台线程中,刚刚收到了有关新聊天消息的推送通知,并且只想在聊天屏幕不在前台时显示系统通知。

使用其他答案中推荐的ActivityLifecycleCallbacks,我创建了一个小型util类,其中包含MyActivity是否在前景中的逻辑。

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {

private var isMyActivityInForeground = false

init {
    (context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}

fun isMyActivityForeground() = isMyActivityInForeground

override fun onActivityPaused(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = false
    }
}

override fun onActivityResumed(activity: Activity?) {
    if (activity is MyActivity) {
        isMyActivityInForeground = true
    }
}

}

答案 27 :(得分:0)

此代码将在任何情况下检查foregroundbackground

Java代码:

private static boolean isApplicationForeground(Context context) {
    KeyguardManager keyguardManager =
            (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);

    if (keyguardManager.isKeyguardLocked()) {
        return false;
    }
    int myPid = Process.myPid();

    ActivityManager activityManager =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    List<ActivityManager.RunningAppProcessInfo> list;

    if ((list = activityManager.getRunningAppProcesses()) != null) {
        for (ActivityManager.RunningAppProcessInfo aList : list) {
            ActivityManager.RunningAppProcessInfo info;
            if ((info = aList).pid == myPid) {
                return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
            }
        }
    }
    return false;
}

科林代码:

private fun isApplicationForeground(context: Context): Boolean {
        val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
        if (keyguardManager.isKeyguardLocked) {
            return false
        }
        val myPid = Process.myPid()
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        var list: List<ActivityManager.RunningAppProcessInfo>
        if (activityManager.runningAppProcesses.also { list = it } != null) {
            for (aList in list) {
                var info: ActivityManager.RunningAppProcessInfo
                if (aList.also { info = it }.pid == myPid) {
                    return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                }
            }
        }
        return false
    }

答案 28 :(得分:0)

另一种没有额外依赖的方法是这个:

只需将此方法添加到您的应用程序类并在 onCreate() 中调用它

var isInBackground = true

private fun setupActivityLifecycleCallbacks() {
    registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
        override fun onActivityStarted(activity: Activity) {}
        override fun onActivityResumed(activity: Activity) {
            isInBackground = false
        }
        override fun onActivityPaused(activity: Activity) {
            isInBackground = true
        }
        override fun onActivityStopped(activity: Activity) {}
        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
        override fun onActivityDestroyed(activity: Activity) {}
    })
}

AFAIK 你甚至可以将 isInBackground 设为静态,这样你就可以使用伴随对象在没有上下文的情况下访问它

答案 29 :(得分:0)

没有任何解决方案适合我,但我提出了一个原始解决方案。这应该有效。如果 isAppBackground 返回 false,则应用必须在前台。

public static boolean isAppBackground(Context context){
        boolean isBackground=true;
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH){
            List<ActivityManager.RunningAppProcessInfo> runningProcesses =activityManager.getRunningAppProcesses();
            for(ActivityManager.RunningAppProcessInfo processInfo:runningProcesses){
                if(processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
                    for(String activeProcess:processInfo.pkgList){
                        if(activeProcess.equals(context.getPackageName())){
                            isBackground = false;
                        }
                    }
                }
            }
        }else{
            List<ActivityManager.RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1);
            if(taskInfo.size()>0) {
                ComponentName componentName = taskInfo.get(0).topActivity;
                if(componentName.getPackageName().equals(context.getPackageName())){
                    isBackground = false;
                }
            }
        }
        return isBackground;
    }

答案 30 :(得分:-1)

回答可能为时已晚,但如果有人来访,那么这是我建议的解决方案, 一个应用程序想要知道它处于后台或前景状态的原因可能很多,有些是, 1.当用户在BG时显示祝酒词和通知。 2.首次执行某些任务用户来自BG,如民意调查,重绘等。

Idolon和其他人的解决方案负责第一部分,但不是第二部分。如果您的应用中有多个活动,并且用户正在它们之间切换,那么当您处于第二个活动时,可见标记将为false。因此无法确定性地使用它。

我做了CommonsWare建议的事情,“如果服务确定没有可见的活动,并且它会保持一段时间,请在下一个逻辑中停止数据传输停止点。“

粗体线很重要,可用于实现第二项。所以我所做的是,一旦我得到onActivityPaused(),不要直接将visible更改为false,而是使用3秒的定时器(即下一个活动应该启动的最大值),如果没有onActivityResumed( )在接下来的3秒内调用,将可见变为false。 类似地,在onActivityResumed()中如果有一个计时器,那么我取消它。 总而言之,可见变为isAppInBackground。

抱歉无法复制粘贴代码......

答案 31 :(得分:-1)

在我的活动onResume和onPause中,我向SharedPrefences写了一个isVisible布尔值。

    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    Editor editor = sharedPrefs.edit();
    editor.putBoolean("visible", false);
    editor.commit();

在需要时通过其他地方阅读

    // Show a Toast Notification if App is not visible (ie in background. Not running, etc) 
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
    if(!sharedPrefs.getBoolean("visible", true)){...}

也许不优雅,但它对我有用......

答案 32 :(得分:-3)

我建议您使用其他方法来执行此操作。

我想你想在程序启动时显示启动屏幕,如果它已经在后端运行,请不要显示它。

您的应用程序可以连续将当前时间写入特定文件。 当您的应用程序启动时,请检查上一个时间戳,如果current_time-last_time&gt;您指定用于写入最新时间的时间范围,则表示您的应用程序已停止,系统或用户自己将其终止。