我正在由c2dm触发的android应用中执行状态栏通知。如果应用程序正在运行,我不想显示通知。您如何确定该应用程序是否正在运行并且在前台?
我正在由c2dm触发的android应用中执行状态栏通知。如果应用程序正在运行,我不想显示通知。您如何确定该应用程序是否正在运行并且在前台?
Answers:
作出这样一个全局变量private boolean mIsInForegroundMode;
和分配false
的价值onPause()
和true
价值onResume()
。
样例代码:
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
方法检查哪些任务正在运行。然后检查返回的任务列表中的第一个任务(前景中的任务)(如果它是您的任务)。
这是代码示例:
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
}
}
并且不要忘记GET_TASKS
在manifest.xml文件中添加权限,以便能够运行getRunningTasks()
上述代码中的方法:
<uses-permission android:name="android.permission.GET_TASKS" />
p / s:如果以这种方式同意,请注意,此权限现在已弃用。
toString()
由返回的StringgetPackageName()
是多余的。另外,由于我们仅对所返回的第一个任务感兴趣getRunningTasks()
,因此我们可以传递1
而不是Integer.MAX_VALUE
。
这是一篇很老的文章,但仍然很有用。以上接受的解决方案可能有效,但有误。正如Dianne Hackborn写道:
这些API并不是应用程序基于其UI流程的基础,而是用于诸如向用户显示正在运行的应用程序或任务管理器之类的事情。
是的,在内存中有一个列表保存这些事情。但是,它在另一个进程中处于关闭状态,由与您的线程分开运行的线程进行管理,而不是您可以依靠的东西(a)及时看到正确的决定,或者(b)在返回时保持一致的情况。加上关于“下一步”活动去的决定总是在切换发生的那一刻完成,直到直到那个确切的点(活动状态被短暂锁定以进行切换),我们才做出决定。真正知道接下来会发生什么。
并且这里的实现和全局行为不能保证将来保持不变。
正确的解决方案是实施:ActivityLifeCycleCallbacks。
这基本上需要一个应用程序类,并且可以在其中设置处理程序以标识您在应用程序中的活动状态。
onPause
和onResume
方法有何不同?
正如Vinay所说的,最好的解决方案(支持更新的android版本,14+)可能是ActivityLifecycleCallbacks
在Application
类实现中使用的。
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解决方案的版本。将其放置在任何活动中,或者将其作为所有活动的基类。
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所说的内容:
注意:此方法仅用于调试和显示任务管理用户界面。绝对不要将其用于应用程序的核心逻辑,例如,根据此处找到的信息确定不同的行为。不支持此类用途,将来可能会中断。
请注意,API级别21中getRunningTasks()
已弃用该功能!
截至目前
LOLLIPOP
,该方法不再对第三方应用程序可用:以文档为中心的最新消息的引入意味着它可以将个人信息泄漏给呼叫者。为了向后兼容,它仍将返回其数据的一小部分:至少调用者自己的任务,以及可能还有一些其他不敏感的任务,例如home。
所以我之前写的更有意义:
在许多情况下,您可能会想出一个更好的解决方案。例如,在onPause()
和中onResume()
(可能在BaseActivity中)为您的所有Activity做某事。
(在我们的案例中,如果我们不在前台,我们不希望启动离线警报活动,因此在BaseActivity中,onPause()
我们只是取消订阅RxJava,以Subscription
监听“离线”信号。)
根据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'
,请在项目gradle中使用。
根据各种答案和评论,以下是可以内联的版本,您可以将其添加到帮助器类中:
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"/>
我想补充一点,比在创建通知之前检查您的应用是否在后台,一种更安全的方法是分别禁用和启用广播接收器onPause()和onResume()。
此方法为您提供了对实际应用程序逻辑的更多控制,并且将来不太可能更改。
@Override
protected void onPause() {
unregisterReceiver(mHandleMessageReceiver);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}
这是@ user2690455上面描述的简单解决方案的代码。虽然看起来有些冗长,但您会发现总体上它非常轻巧
就我而言,我们还使用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;
}
}
仅当您要在活动开始以及要检查应用程序在前台还是后台的位置执行某些操作时,此功能才有用。
除了使用活动管理器之外,您还可以通过代码完成一个简单的技巧。如果仔细观察活动周期,则两个活动之间以及从前台到背景的流程如下。假设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。n在检查标志后再次设置isAppInBackground = false
对于两个活动之间的过渡,因为第一个活动的onSTop()总是在第二个活动恢复后调用,所以标志永远不会为真,并且当应用程序处于后台时,活动的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 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”类来跟踪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();
}
}