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

83

我正在我的Android应用程序中进行状态栏通知,该通知由C2DM触发。如果应用程序正在运行,我不想显示通知。如何确定应用程序是否正在运行并处于前台?


这是一个类似的问题...虽然我尝试在onStart/onStop上设置标志,但它没有起作用。我仍然不明白stop/start和pause/resume之间的区别。 - Andrew Thomas
1
你应该使用这个解决方案:https://dev59.com/KHA65IYBdhLWcg3wuhIR#5862048 - Informatic0re
自API 16以来,有一种更简单的方法,可以使用ActivityManager.getMyMemoryState - Juozas Kontvainis
自支持库版本26以来,您只需在需要时查询ProcessLifecycleOwner即可。请查看https://www.stackoverflow.com/a/52678290/6600000。 - Keivan Esbati
18个回答

1
这是@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;
    }
}

1

根据各种答案和评论,以下是一个更加内联的版本,您可以将其添加到助手类中:

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"/>

在Android Lollipop中,此权限已被弃用。 - Mussa

1
我发现一种更简单准确的方法来检查应用程序是在前台还是后台,通过将活动映射到布尔值来实现。
检查完整的代码片段在这里

1
这只有在您想要在活动启动时执行某些操作,并且您希望检查应用程序是在前台还是后台时才有用。
不使用Activity Manager,您可以通过代码进行简单的技巧。如果您仔细观察活动周期,从一个活动到另一个活动和前台到后台的流程如下所示。假设A和B是两个活动。
当从A转换到B时: 1.调用A的onPause()。 2.调用B的onResume()。 3.在B完全恢复时调用A的onStop()。
当应用程序进入后台时: 1.调用A的onPause()。 2.调用A的onStop()。
您可以通过在活动中放置标志来检测后台事件。
创建抽象活动并从其他活动中扩展它,以便您无需为需要后台事件的所有其他活动复制粘贴代码。
在抽象活动中创建标志isAppInBackground。
在onCreate()方法中: isAppInBackground = false;
在onPause()方法中: isAppInBackground = false;
在onStop()方法中: isAppInBackground = true;
你只需要在onResume()中检查isAppInBackground是否为true。 然后再次设置isAppInBackground = false即可。
对于两个活动之间的转换,由于第一个活动的onStop()总是在第二个活动恢复后调用,所以标记永远不会为true,当应用程序在后台时,活动的onStop()将立即在onPause()之后被调用,因此当您稍后打开应用程序时,标记将为true。
但是这种方法还有一种情况。如果你的应用程序屏幕已经打开并且你让手机空闲一段时间,那么手机将进入睡眠模式,当你解锁手机时,它将被视为后台事件。

1
这里是我使用的一种方法(以及支持的方法):
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;
    }
}

看起来我的解决方案被踩了,但是那是两年前发布的。由于安卓进行了多种安全更新,这种方法已经不再适用。 - Droid Chris

0
这里提到的先前方法并不是最优的。基于任务的方法需要一个可能不希望的权限,而“布尔”方法容易出现并发修改问题。
我使用的方法在大多数情况下都很好用,它是:
创建一个“MainApplication”类来跟踪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类,其他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();
    }
}

好的...但是请考虑在activityStopped()中使用ACTIVITY_COUNT.decrementAndGet();。 - ChrisCarneiro

0

这个没有全局回调函数,但是对于每个活动来说,它是onStop()。你不需要去处理原子int。只需有一个全局int表示已启动的活动数,在每个活动中,在onStart()中增加它,在onStop()中减少它。

请参考this


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

getRunningTasks不再是一个选项 - 更新的安全模型使其大多无用,并且已被弃用。 - Tony Maro
@AnaghHegde 请参考Gadenkan的上面的答案,从系统中获取正在运行的活动列表。 - Tony Maro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接