我正在我的Android应用程序中进行状态栏通知,该通知由C2DM触发。如果应用程序正在运行,我不想显示通知。如何确定应用程序是否正在运行并处于前台?
我正在我的Android应用程序中进行状态栏通知,该通知由C2DM触发。如果应用程序正在运行,我不想显示通知。如何确定应用程序是否正在运行并处于前台?
或者,您可以通过ActivityManager
检查正在运行的任务,方法是使用getRunningTasks
方法。然后,在返回的任务列表中检查第一个任务(前台任务),是否为您自己的任务。
以下是代码示例:
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:如果同意这种方式,请注意此权限现已弃用。
getPackageName()
返回的字符串上调用 toString()
是多余的。另外,由于我们只关心 getRunningTasks()
返回的第一个任务,因此可以传递 1
而不是 Integer.MAX_VALUE
。 - Jonik创建一个全局变量,例如 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;
}
这是一篇相当古老的文章,但仍然非常相关。上面被接受的解决方案可能有效,但是是错误的。正如Dianne Hackborn所写:
这些API并不是供应用程序基于其UI流程,而是像显示正在运行的应用程序或任务管理器等东西。
是的,有一个在内存中保存的列表。但是,它在另一个进程中,由与您分开运行的线程管理,并且不是您可以依靠的内容:(a)及时看到以做出正确的决策,或者(b)在返回时拥有一致的图像。此外,在切换发生的点处做出关于要“下一个”活动的决定,只有在确切的点(在其中活动状态短暂锁定以执行切换)才能知道下一步将是什么。
这里的实现和全局行为也不能保证未来会保持不变。
正确的解决方法是实现:ActivityLifeCycleCallbacks。
这基本上需要一个应用程序类,并且可以在其中设置处理程序来识别应用程序中活动的状态。
onPause
和onResume
方法有何不同? - Saitama正如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()
来了解所需的状态。
从 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解决方案(非常好用!!),不要忘记添加
<uses-permission android:name="android.permission.GET_TASKS" />
到清单中。
这是稍微改进过的 Gadenkan 的方案。可以将其放在任何 Activity 中,或者作为所有 Activity 的基类。
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 中有明确说明:
注意:此方法仅用于调试和显示任务管理用户界面。这不应该用于应用程序中的核心逻辑,例如根据此处找到的信息决定不同行为。这样的用法不受支持,将来可能会出现问题。
请注意,getRunningTasks()
已在 API 级别 21 中被弃用!
从
LOLLIPOP
开始,此方法不再提供给第三方应用程序使用:介绍文档导向的最近列表意味着它可能会向调用者泄漏个人信息。为了向后兼容,它仍然会返回其数据的一小部分:至少包括调用者自己的任务,以及可能一些其他已知不敏感的任务,如主页。
因此,之前我写的内容变得更加相关:
在许多情况下,您可能可以想出更好的解决方案。例如,在所有活动中的 BaseActivity 中执行一些操作。可以在 onPause()
和 onResume()
中完成,也可以使用 RxJava 中的 Subscription
,例如,在 BaseActivity 的 onPause()
中取消订阅 "went offline" 信号的监听。
在回答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
权限。
从支持库版本26开始,您可以使用ProcessLifecycleOwner来确定应用程序的当前状态,只需像这里描述的那样将其添加到您的依赖项中,例如:
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
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
。 - Jon@Override
protected void onPause() {
unregisterReceiver(mHandleMessageReceiver);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}