如何判断 Android 应用程序是否在前台运行?

我正在做一个状态栏通知在我的 android 应用程序,是由 c2dm 触发。如果应用程序正在运行,我不想显示通知。如何确定应用程序是否正在运行并处于前台?

80299 次浏览

创建一个类似于 private boolean mIsInForegroundMode;的全局变量,并在 onPause()中分配一个 false值,在 onResume()中分配一个 true值。

示例代码:

private boolean mIsInForegroundMode;


@Override
protected void onPause() {
super.onPause();
mIsInForegroundMode = false;
}


@Override
protected void onResume() {
super.onResume();
mIsInForegroundMode = true;
}


// Some function.
public boolean isInForeground() {
return mIsInForegroundMode;
}

或者,您可以使用 ActivityManager检查通过 getRunningTasks方法运行的任务。然后检查返回的任务列表中的第一个任务(前景中的任务) ,如果它是您的任务。< br > 以下是代码示例: < br >

public Notification buildNotification(String arg0, Map<String, String> arg1) {


ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager
.getRunningTasks(Integer.MAX_VALUE);
boolean isActivityFound = false;


if (services.get(0).topActivity.getPackageName().toString()
.equalsIgnoreCase(appContext.getPackageName().toString())) {
isActivityFound = true;
}


if (isActivityFound) {
return null;
} else {
// write your code to build a notification.
// return the notification you built here
}


}

不要忘记在 Xml文件中添加 GET_TASKS权限,以便能够在上面的代码中运行 getRunningTasks()方法:

<uses-permission android:name="android.permission.GET_TASKS" />

如果同意这种方式,请注意这种许可现在已被废弃。

根据 Gadenkan 的回复,我需要这样的东西,这样我就可以知道我的应用是否在前台运行,但我需要的是一个应用范围广的东西,不需要我在整个应用程序中设置/取消标志。

Gadenkan 的代码非常准确,但它不是我自己的风格,我觉得它可以更整洁,所以在我的应用程序中,它浓缩到这里。

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(旁注: 如果你想让支票反过来工作,你可以删除!)

尽管使用这种方法需要 GET_TASKS权限。

仅供参考,如果您使用加登坎溶液(这是伟大的! !)不要忘记添加

<uses-permission android:name="android.permission.GET_TASKS" />

到货单上。

略微清理版本的 加登坎的解决方案。把它的任何活动,或者可能是一个基类的所有活动。

protected boolean isRunningInForeground() {
ActivityManager manager =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
if (tasks.isEmpty()) {
return false;
}
String topActivityName = tasks.get(0).topActivity.getPackageName();
return topActivityName.equalsIgnoreCase(getPackageName());
}

为了能够调用 getRunningTasks(),你需要在你的 AndroidManifest.xml中加入以下内容:

<uses-permission android:name="android.permission.GET_TASKS"/>

请注意 ActivityManager.getRunningTasks() Javadoc 说的话:

注意: 此方法仅用于调试和显示任务 管理用户界面。 这永远不应该用于核心逻辑 在应用程序中,例如基于 这些用途并不受支持 在未来可能会断裂。

最新情况(2015年2月)

请注意,getRunningTasks()已在 API 级别21中弃用

LOLLIPOP开始 方法不再适用于第三方应用程序: 最近以文件为中心的介绍意味着它可能泄露人 信息传递给调用者。为了向后兼容,它仍然 返回其数据的一小部分: 至少是调用者自己的任务, 可能还有一些其他的任务,比如家庭 敏感。

因此,我之前写的内容更有意义:

在许多情况下,你可能会想出一个更好的解决方案。例如,在 onPause()onResume()中执行某些操作,可能是在所有活动的 BaseActivity 中执行某些操作。

(在我们的例子中,如果我们不在前台,我们不希望启动离线警报活动,所以在 BaseActivity onPause()中,我们只需要从 RxJava Subscription中取消订阅就可以听到“离线”信号。)

基于各种答案和评论,这里有一个更内联的版本,您可以添加到助手类:

public static boolean isAppInForeground(Context context) {
List<RunningTaskInfo> task =
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
.getRunningTasks(1);
if (task.isEmpty()) {
return false;
}
return task
.get(0)
.topActivity
.getPackageName()
.equalsIgnoreCase(context.getPackageName());
}

正如在其他答案中提到的,您需要添加以下权限到您的 AndroidManifest.xml

<uses-permission android:name="android.permission.GET_TASKS"/>

这是一个相当老的职位,但仍然相当相关。上述接受的解决方案可能工作,但是是错误的

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

是的,存储器里有这些东西的列表。然而,它在另一个进程中是关闭的,由与您的进程分开运行的线程进行管理,并且不能指望(a)及时看到并做出正确的决定,或者(b)在您返回时拥有一致的图片。另外,关于“下一个”活动要去哪里的决定总是在切换发生的时候完成的,直到那个确切的时间点(活动状态被短暂地锁定以进行切换) ,我们才真正知道下一个活动会是什么。

这里的实现和全局行为并不能保证在将来保持不变。

正确的解决方案是实现: ActivityLifeCycleCallback 活动生命周期回调

这基本上需要一个应用程序类和处理程序可以设置在那里,以确定您的活动在应用程序的状态。

我想补充的是,比起在创建通知之前检查应用程序是否在后台,更安全的方法是分别禁用和启用 Broadcast Receiver onPuse ()和 onResume ()。

这种方法使您能够更好地控制实际的应用程序逻辑,而且以后不太可能改变。

@Override
protected void onPause() {
unregisterReceiver(mHandleMessageReceiver);
super.onPause();
}


@Override
protected void onResume() {
super.onResume();
registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}

我发现了一种更简单、更准确的方法,通过将活动映射为 boolean 来检查应用程序是在前台还是在后台。

检查完整的要点 给你

对此没有全局回调,但是对于每个活动它都是 onStop ()。你不需要搞乱原子整型。只需要有一个包含已启动活动数量的全局 int,在每个活动中,它在 onStart ()中递增,在 onStop ()中递减。

跟着 这个

只有当你想在活动开始时执行某些操作,并且想检查应用程序是在前台还是后台时,这个选项才有用。

与使用活动管理器不同,有一个简单的技巧,您可以通过代码来完成。 如果您仔细观察活动周期,那么两个活动之间的流程和前景到背景之间的流程如下所示。 假设 A 和 B 是两个活动。

当从 A 过渡到 B: 1. A 的 onPuse ()被调用 2. B 的 onResume ()被称为 当 B 完全恢复时,调用 A 的 onStop ()

当应用程序进入后台: 1. A 的 onPuse ()被调用 2. 调用 A 的 onStop ()

您可以通过简单地在活动中放置一个标志来检测背景事件。

创建一个抽象活动,并从其他活动中扩展它,这样您就不必复制粘贴所有其他活动的代码,无论您在哪里需要背景事件。

在抽象的活动中,创建标志。

在 onCreate ()方法中: IsAppIn 背景 = false;

在 onPuse ()方法中: IsAppIn 背景 = false;

在 onStop ()方法中: 背景 = 真;

您只需要检入 onResume () ,如果 isAppInBack 为 true。 在你检查你的标志之后,再次设置 isAppInBack = false

对于两个活动之间的转换,因为 onSTop ()总是在第二个活动恢复之后调用,标志永远不会为真,当应用在后台时,onStop ()的活动将在 onPuse 之后立即被调用,因此当你稍后打开应用时,标志将为真。

不过在这种方法中还有一种情况。 如果您的任何应用程序屏幕已经打开,你把手机空闲,然后一段时间后,手机将进入睡眠模式,当你解锁手机,它将在后台事件处理。

下面是上面@user2690455描述的简单解决方案的代码。虽然它看起来有点冗长,但是你会看到它实际上是相当 轻量级的

在我的例子中,我们也使用 AppCompatActivity,所以我必须有两个基类。

public class BaseActivity extends Activity {


/**
* Let field be set only in base class
* All callers must use accessors,
* and then it's not up to them to manage state.
*
* Making it static since ..
* 1. It needs to be used across two base classes
* 2. It's a singleton state in the app
*/
private static boolean IS_APP_IN_BACKGROUND = false;


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


BaseActivity.onResumeAppTracking(this);


BaseActivity.setAppInBackgroundFalse();
}


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


BaseActivity.setAppInBackgroundTrue();
}


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


BaseActivity.setAppInBackgroundFalse();
}


protected static void onResumeAppTracking(Activity activity) {


if (BaseActivity.isAppInBackground()) {


// do requirements for returning app to foreground
}


}


protected static void setAppInBackgroundFalse() {


IS_APP_IN_BACKGROUND = false;
}


protected static void setAppInBackgroundTrue() {


IS_APP_IN_BACKGROUND = true;
}


protected static boolean isAppInBackground() {


return IS_APP_IN_BACKGROUND;
}
}

正如 Vinay 所说,可能最好的解决方案(为了支持更新的 android 版本,14 +)是在 Application类实现中使用 ActivityLifecycleCallbacks

package com.telcel.contenedor.appdelegate;


import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;


/** Determines global app lifecycle states.
*
* The following is the reference of activities states:
*
* The <b>visible</b> lifetime of an activity happens between a call to onStart()
* until a corresponding call to onStop(). During this time the user can see the
* activity on-screen, though it may not be in the foreground and interacting with
* the user. The onStart() and onStop() methods can be called multiple times, as
* the activity becomes visible and hidden to the user.
*
* The <b>foreground</b> lifetime of an activity happens between a call to onResume()
* until a corresponding call to onPause(). During this time the activity is in front
* of all other activities and interacting with the user. An activity can frequently
* go between the resumed and paused states -- for example when the device goes to
* sleep, when an activity result is delivered, when a new intent is delivered --
* so the code in these methods should be fairly lightweight.
*
* */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {


/** Manages the state of opened vs closed activities, should be 0 or 1.
* It will be 2 if this value is checked between activity B onStart() and
* activity A onStop().
* It could be greater if the top activities are not fullscreen or have
* transparent backgrounds.
*/
private static int visibleActivityCount = 0;


/** Manages the state of opened vs closed activities, should be 0 or 1
* because only one can be in foreground at a time. It will be 2 if this
* value is checked between activity B onResume() and activity A onPause().
*/
private static int foregroundActivityCount = 0;


/** Returns true if app has foreground */
public static boolean isAppInForeground(){
return foregroundActivityCount > 0;
}


/** Returns true if any activity of app is visible (or device is sleep when
* an activity was visible) */
public static boolean isAppVisible(){
return visibleActivityCount > 0;
}


public void onActivityCreated(Activity activity, Bundle bundle) {
}


public void onActivityDestroyed(Activity activity) {
}


public void onActivityResumed(Activity activity) {
foregroundActivityCount ++;
}


public void onActivityPaused(Activity activity) {
foregroundActivityCount --;
}




public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}


public void onActivityStarted(Activity activity) {
visibleActivityCount ++;
}


public void onActivityStopped(Activity activity) {
visibleActivityCount --;
}
}

在应用 onCreate()方法时:

registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());

然后使用 ApplicationLifecycleManager.isAppVisible()ApplicationLifecycleManager.isAppInForeground()来知道所需的状态。

     public static boolean isAppRunning(Context context) {


// check with the first task(task in the foreground)
// in the returned list of tasks


ActivityManager activityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services =
activityManager.getRunningTasks(Integer.MAX_VALUE);
if
(services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
{
return true;
}
return false;
}

这里提到的前面的方法并不是最优的。基于任务的方法需要一个可能不需要的权限,而“布尔”方法容易出现并发修改混乱。

我使用的方法(我相信)在大多数情况下都很有效:

拥有一个“ MainApplication”类来跟踪 < em > AtomicInteger 中的活动计数:

import android.app.Application;


import java.util.concurrent.atomic.AtomicInteger;


public class MainApplication extends Application {
static class ActivityCounter {
private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);


public static boolean isAppActive() {
return ACTIVITY_COUNT.get() > 0;
}


public static void activityStarted() {
ACTIVITY_COUNT.incrementAndGet();
}


public static void activityStopped() {
ACTIVITY_COUNT.decrementAndGet();
}
}
}

并创建一个其他活动将扩展的基本 Activity 类:

import android.app.Activity;
import android.support.annotation.CallSuper;


public class TestActivity extends Activity {
@Override
@CallSuper
protected void onStart() {
MainApplication.ActivityCounter.activityStarted();
super.onStart();
}


@Override
@CallSuper
protected void onStop() {
MainApplication.ActivityCounter.activityStopped();
super.onStop();
}
}

从 API 16开始,你可以这样做:

static boolean shouldShowNotification(Context context) {
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
return true;


KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
// app is in foreground, but if screen is locked show notification anyway
return km.inKeyguardRestrictedInputMode();
}

下面是我使用的方法(以及支持方法) :

private boolean checkIfAppIsRunningInForeground() {
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
if(appProcessInfo.processName.contains(this.getPackageName())) {
return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
}
}
return false;
}


private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
switch (appImportance) {
//user is aware of app
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
return true;
//user is not aware of app
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
default:
return false;
}
}

启动支持库版本26你可以使用 ProcessLificycleOwner来确定应用程序的当前状态,只需要将它添加到你的依赖项中,比如说:

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来检查应用程序的状态,例如,检查应用程序是否在前台运行,你只需要这样做:

 boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
if(!isAppInForeground)
//Show Notification in status bar