我怎么知道Android应用程序是否在前台运行?
我正在做一个状态栏通知在我的Android应用程序,由c2dm触发。 如果应用程序正在运行,我不想显示通知。 你如何确定应用程序是否正在运行,并在前台?
创build一个全局variables像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; }
或者,您可以使用getRunningTasks
方法检查ActivityManager
正在运行的任务。 然后检查返回的任务列表中的第一个任务(前台任务),如果是您的任务。
这是代码示例:
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 } }
并且不要忘记在manifest.xml文件中添加GET_TASKS
权限,以便能够在上面的代码中运行getRunningTasks()
方法:
<uses-permission android:name="android.permission.GET_TASKS" />
p / s:如果同意这种方式,请注意现在这个权限已被弃用。
这是一个很老的post,但仍然相当有用。 以上接受的解决scheme可能工作但是是错误的。 正如Dianne Hackborn所写:
这些API不是让应用程序基于其UIstream,而是为用户显示正在运行的应用程序或任务pipe理器等。
是的,这些东西有一个记忆清单。 然而,它是在另一个过程中,由与您分开运行的线程pipe理的,而不是您可以指望的(a)及时看到做出正确的决定或(b)在您返回时有一致的图像。 再加上关于“下一个”要进行的活动的决定总是在交换机要发生的地方完成,而且直到那个确切点(活动状态被暂时locking以进行交换)其实知道接下来会发生什么。
而且这里的实施和全球行为不能保证在未来保持不变。
正确的解决scheme是实现: ActivityLifeCycleCallbacks 。
这基本上需要一个应用程序类,并可以在那里设置处理程序,以确定您在应用程序中的活动的状态。
正如Vinay所说,可能最好的解决scheme(支持更新的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 --; } }
并在Application的onCreate()
方法中:
registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());
然后使用ApplicationLifecycleManager.isAppVisible()
或ApplicationLifecycleManager.isAppInForeground()
来知道所需的状态。
仅供参考,如果您使用Gadenkan解决scheme(这是伟大的!)不要忘记添加
<uses-permission android:name="android.permission.GET_TASKS" />
到清单。
稍微清理Gadenkan的解决scheme的版本。 把它放在任何活动,或者可能是所有活动的基类。
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说:
注意:此方法仅用于debugging和显示任务pipe理用户界面。 这不应该用于应用程序中的核心逻辑,例如根据这里find的信息来决定不同的行为。 这种用途不被支持,将来可能会破裂。
更新(2015年2月)
请注意, getRunningTasks()
在API级别21中已被弃用 !
从
LOLLIPOP
,这种方法不再适用于第三方应用程序:引入以文档为中心的最近信息意味着它可以向调用者泄漏个人信息。 为了向后兼容,它仍然会返回一小部分数据:至less是调用者自己的任务,还有可能是一些其他任务,比如家中已知不敏感的任务。
所以我之前写的更加相关:
在很多情况下,你可能会想出更好的解决scheme。 例如,在onPause()
和onResume()
做一些事情,可能是在所有活动的BaseActivity中。
(在我们的例子中,如果我们不在前台,我们不希望启动一个离线警报活动,所以在BaseActivity onPause()
我们只是取消订阅RxJava Subscription
侦听“离线”信号。)
由于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(); }
跟着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
权限。
我想添加一个更安全的方法来做到这一点 – 比创build通知之前检查您的应用程序是否在后台 – 是分别禁用和启用广播接收器onPause()和onResume()。
这种方法使您在实际的应用程序逻辑中有更多的控制,而且将来不太可能改变。
@Override protected void onPause() { unregisterReceiver(mHandleMessageReceiver); super.onPause(); } @Override protected void onResume() { super.onResume(); registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION)); }
我发现了一个更简单和准确的方法,通过将活动映射到布尔值来检查应用程序是处于前景还是后台。
检查完整的要点在这里
这只有当你想要在你的活动开始的时候执行一些动作,以及你想要检查应用程序是在前景还是在后台的时候,它才是有用的。
而不是使用活动pipe理器有一个简单的技巧,你可以通过代码来完成。 如果仔细观察活动周期,两个活动之间以及前景与背景之间的stream程如下所示。 假设A和B是两个活动。
当从A转换到B时:1.调用A的onPause()2.调用B的onResume()3.调用A的onStop(),当B完全恢复时
当应用程序进入后台:1. A的onPause()被调用2. A的onStop()被调用
您只需在活动中添加一个标志即可检测您的后台事件。
进行抽象活动并将其从其他活动中扩展出来,以便在需要后台事件的情况下,不必复制粘贴所有其他活动的代码。
在抽象活动中创build标志isAppInBackground。
在onCreate()方法中:isAppInBackground = false;
在onPause()方法中:isAppInBackground = false;
在onStop()方法中:isAppInBackground = true;
你只需要检查你的onResume()如果isAppInBackground是真实的。 在你检查你的标志后,再设置isAppInBackground = false
对于两个活动之间的转换,由于第一个的onSTop()总是在第二个活动重新开始后调用,所以flag永远不会是true,当app在后台时,activity的onStop()将在onPause之后立即被调用,因此当您稍后打开应用程序。
在这种方法中还有一种情况。 如果您的任何应用程序屏幕已经打开,并且您将手机闲置,那么一段时间后手机将进入睡眠模式,当您解锁手机时,将在后台事件中处理。
这里是我使用的一种方法(和支持方法):
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; } }
基于各种答案和评论,下面是一个更加内联的版本,你可以添加到一个辅助类:
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"/>
以下是@ user2690455上面介绍的简单解决scheme的代码。 虽然看起来有点冗长,但总体来看它实际上是相当轻量级的
在我的情况下,我们也使用AppCompatActivity,所以我必须有2个基类。
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; } }
这里提到的以前的方法并不是最优的。 基于任务的方法需要一个可能不需要的权限,而“布尔”方法容易并发修改混乱。
我使用的方法和(我相信)在大多数情况下工作得很好:
有一个“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(); } } }
并创build一个其他活动可以扩展的基类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(); } }
没有全局callback,但是对于每个活动,它都是onStop()。 你不需要搞乱一个atomic int。 只要有一个全局int与启动活动的数量,在每一个活动增加onStart()并减less它在onStop()。
按照这个
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; }