检查 Android 应用程序是否在前台?

128

我查看了很多关于这个问题的答案,但它们都只涉及单个活动。如何检查整个应用程序是否在前台运行?


1
ProcessLifecycleOwner是最新的解决方案。 - S.R
15个回答

141

我不明白您想要什么,但是您可以使用ActivityManager.getRunningAppProcesses()方法来检测当前前台/后台应用程序。

类似这样的代码:

class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {

  @Override
  protected Boolean doInBackground(Context... params) {
    final Context context = params[0].getApplicationContext();
    return isAppOnForeground(context);
  }

  private boolean isAppOnForeground(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
    if (appProcesses == null) {
      return false;
    }
    final String packageName = context.getPackageName();
    for (RunningAppProcessInfo appProcess : appProcesses) {
      if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
        return true;
      }
    }
    return false;
  }
}

// Use like this:
boolean foregroud = new ForegroundCheckTask().execute(context).get();

如果我理解有误,请告诉我。

更新:查看此SO问题Determining the current foreground application from a background task or service了解更多信息。

谢谢。


2
每当我从服务器接收到通知时,如果应用程序在前台运行,则不想显示该通知。 - hacker
1
你好兄弟...现在它无法获取。每次都返回true值。该怎么办? - hacker
1
该解决方案存在一个错误,因为如果应用程序具有正在运行的服务,则其重要性可能是RunningAppProcessInfo.IMPORTANCE_FOREGROUND。更完整的解决方案在此处:https://dev59.com/LnI95IYBdhLWcg3www2Y - dongshengcn
5
这是我最初在Android:应用程序是否在后台运行?线程中提出的答案。但是,结果证明它不可靠,此外,Android作者也不建议使用ActivityManager来检查后台/前台。您可以查看我的以下回答以了解详细信息:https://dev59.com/KHA65IYBdhLWcg3wuhIR#5862048 - Idolon
3
这需要在清单文件中特别授权“GET_TASKS”。在我看来,这要求过于苛刻了。 - Ε Г И І И О
显示剩余10条评论

72

2020年10月更新:在本文中查看基于生命周期扩展的解决方案。这种方法似乎有效,更加优雅和现代。


到目前为止,我发现最整洁且未被弃用的方法如下:

@Override
public boolean foregrounded() {
    ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(appProcessInfo);
    return (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
}

它只适用于SDK 16+。

编辑

我从解决方案中删除了以下代码:

KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
// App is foreground, but screen is locked, so show notification
return km.inKeyguardRestrictedInputMode();

因为这会导致在屏幕锁定时无法收到通知。我查看了框架,发现其目的并不是完全清晰的。我建议将其移除。检查进程信息状态已足够 :-)


很遗憾,由于ActivityManager.getMyMemoryState()的限制,它只能用于API > 15的情况。 - Ayaz Alifov
抱歉,我使用的最低SDK版本是16。 - cesards
isAppOpened() 方法在哪个类中? 我在 Application 和 Activity 中找不到它。 - Safeer
这是我的自己的方法。实际上,它与解决方案无关 :-) - cesards
14
兄弟们加油,这其实一点也不糟糕,2017年适用API 16及以上版本意味着覆盖99%以上的用户。 - Hamzeh Soboh
@cesards 目前的 inKeyguardRestrictedInputMode() 已更改为 isKeyguardLocked()。 - Diljeet

44

@user370305的答案容易出错,而且被 Android OS 开发者不推荐使用(请查看https://groups.google.com/forum/#!msg/android-developers/zH-2bovZSLg/L2YM8Z1N-HwJ

更为简单的方法是:

在所有 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


1
仅回答与应用程序相关的问题,而非用户应用程序的活动。 - user370305
1
是的,但需要检查其他方法为什么不被建议使用:http://stackoverflow.com/posts/5862048/revisions - neteinstein
2
这段代码可以用来判断当前活动是在前台还是后台。 - JoaoFilipeClementeMartins
5
如果活动被切换,也会调用此函数! - Ruchir Baronia
由于我想在后台服务中访问此信息,而不是在BaseActivity中,因此我将其添加到了BaseApplication中。 - Cerlin
显示剩余5条评论

42

通过新的Android Architecture的Lifecycle extensions,我们可以非常容易地实现这一点。

只需确保在您的build.gradle文件中导入此依赖项:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.0"
}

然后在你的 Application 类中使用这个:

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        Log.d("MyApp", "App in background")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        Log.d("MyApp", "App in foreground")
    }
}

最后,使用以下代码更新你的 AndroidManifest.xml 文件:

<application
    android:name=".ArchLifecycleApp"
    //Your extra code
    ....>
</application>

现在,每当应用程序进入前台或后台,我们都会接收到与声明的两种方法相关联的日志。

更新:@OnLifecycleEvent已被弃用

@OnLifecycleEvent注释需要使用代码生成或反射,这应该避免使用。改用DefaultLifecycleObserverLifecycleEventObserver。有关更多信息,请参见此示例


2
这并不总是有效的。需要首先调用它来确定它是否在前台。例如,尝试在类ArchLifecycleApponCreate函数中检查它是否在前台。 - android developer
3
使用AndroidX后,该库现在被称为androidx.lifecycle:lifecycle-extensions。 - Alix
它运行良好,但似乎应用程序进入后台有非常短的时间延迟。当应用程序回到前台时,时间上是正常的。 - Sarith Nob
我认为如果操作系统唤醒你的应用程序来处理推送通知,这会错误地将你的应用标记为前台运行状态。如果我说错了,请纠正我。 - jacoballenwood

23

使用Android架构组件库,您可以使用ProcessLifecycleOwner设置整个应用程序进程的onStartonStop事件的监听器。为此,请使您的应用程序类实现LifecycleObserver接口,并将一些注释添加到前台和后台方法的onStartonStop

class ArchLifecycleApp : Application(), LifecycleObserver {

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onAppBackgrounded() {
        Log.d("Awww", "App in background")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onAppForegrounded() {
        Log.d("Yeeey", "App in foreground")
    }

}

2
在你的应用程序 build.gradle 中使用 implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" 作为依赖项。 - Vít Kapitola

15

我已经尝试使用运行进程的包过滤器,但那很奇怪。相反,我尝试了新的解决方案,这个完美地工作了。我检查了很多次,并通过此模块获得了完美的结果。

private int numRunningActivities = 0;

 public void onCreate() {
        super.onCreate();
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

                @Override
                public void onActivityStarted(Activity activity) {
                    numRunningActivities++;
                    if (numRunningActivities == 1) {
                        LogUtils.d("APPLICATION", "APP IN FOREGROUND");
                    }

                }

                @Override
                public void onActivityStopped(Activity activity) {

                    numRunningActivities--;
                    if (numRunningActivities == 0) {
                       Log.e("", "App is in BACKGROUND") 
                    }
                }


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

                @Override
                public void onActivityResumed(Activity activity) {
                }

                @Override
                public void onActivityPaused(Activity activity) {
                }

                @Override
                public void onActivityDestroyed(Activity activity) {
                }

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

我认为这个答案很好,但是如果你想知道活动/视图实际上正在显示时,最好将计数器放入onActivityResumed/onActivityPaused中。 - Pellet
这就是为什么我们需要在每个活动中投入精力。随着时间的推移,它会变得更加复杂。所以这个很好。 - Jitendra Bhadja

10

检查应用程序是否在后台或前台。如果应用程序在后台,此方法将返回true。

首先,在AndroidManifest.xml文件中添加GET_TASKS权限。

private boolean isAppIsInBackground(Context context) {
    boolean isInBackground = true;
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
        List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
            if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                for (String activeProcess : processInfo.pkgList) {
                    if (activeProcess.equals(context.getPackageName())) {
                        isInBackground = false;
                    }
                }
            }
        }
    } else {
        List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        ComponentName componentInfo = taskInfo.get(0).topActivity;
        if (componentInfo.getPackageName().equals(context.getPackageName())) {
            isInBackground = false;
        }
    }

    return isInBackground;
}

无论我是否关闭应用程序,它都可以打开我的天气应用程序。 - Ajay Mistry
2
GET_TASKS已被弃用。 - MbaiMburu

2

没有全局回调函数,但对于每个活动(activity),它是onStop()。您无需使用原子int来处理。只需在每个活动中拥有一个全局int,然后在onStart()中递增,在onStop()中递减。

public class BaseActivity extends ActionBarActivity {
public static int count = 0;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}


@Override
protected void onStart() {
    super.onStart();
    count = count + 1;
    Log.d(TAG, "onStart" + count);
    if (count == 1) {

       Toast.makeText(getApplicationContext(), "online", Toast.LENGTH_SHORT).show();

    }

}



protected void onStop() {
    super.onStop();
    count = count - 1;
    if (count == 0) {

        Toast.makeText(getApplicationContext(), "offline", Toast.LENGTH_SHORT).show();

    }
}


}

2

我发现了一个简单的解决方法,通过创建一个基础活动类,您必须从这个类扩展所有的活动类:

public class BaseActivity extends ActionBarActivity {

@Override
protected void onResume() {
    ApplicationStateChecker.view_resumed(this);
    super.onResume();
}

@Override
protected void onStop() {
    ApplicationStateChecker.view_stopped(this);
    super.onStop();

}

@Override
protected void onPause() {
    ApplicationStateChecker.view_paused(this);
    super.onPause();

}

}

ApplicationStateChecker类:

public class ApplicationStateChecker {

private  static final String _pause_string = "paused";
private  static final String _resume_string = "resumed";

private static String _view_lastState;
private static boolean _from_background = true;

public static void view_paused(Activity activity){
    _view_lastState = _pause_string;
}

public static void view_stopped(Activity activity){

    if (  _view_lastState.equals(_pause_string) ){
        //if stop called and last event was pause then app is brought to background
        _from_background = true;
    }  //if

}

public static void view_resumed(Activity activity){

    if (  _from_background ) {
       //Do your stuff here , app is brought to foreground 

    }  //if

    _from_background = false;
    _view_lastState = _resume_string;
}

更加简洁的方式是实现一个继承自Application并且实现Application.ActivityLifecycleCallbacks接口的MainApplication类。https://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks - Pierre

2

cesards的回答是正确的,但只适用于API版本>15。对于较低的API版本,我决定使用getRunningTasks()方法:

   private boolean isAppInForeground(Context context)
    {
        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
        {
            ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
            ActivityManager.RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
            String foregroundTaskPackageName = foregroundTaskInfo.topActivity.getPackageName();

            return foregroundTaskPackageName.toLowerCase().equals(context.getPackageName().toLowerCase());
        }
        else
        {
            ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
            ActivityManager.getMyMemoryState(appProcessInfo);
            if (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE)
            {
                return true;
            }

            KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            // App is foreground, but screen is locked, so show notification
            return km.inKeyguardRestrictedInputMode();
        }
    }

请告诉我这是否适用于您所有人。


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