【发布时间】:2011-12-13 13:23:43
【问题描述】:
对于这个问题,我经历了很多答案。但这都是关于单个活动的。如何检查整个应用程序是否在前台运行?
【问题讨论】:
-
ProcessLifecycleOwner 是最新的解决方案
标签: android
对于这个问题,我经历了很多答案。但这都是关于单个活动的。如何检查整个应用程序是否在前台运行?
【问题讨论】:
标签: android
我不明白你想要什么,但你可以通过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 了解更多信息..
谢谢..
【讨论】:
ActivityManager 用于背景/前景检查的用法是discouraged by Android authors。您可以查看我的以下答案以了解详细信息:stackoverflow.com/a/5862048/648313
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();
因为如果屏幕锁定,则不会收到通知。我查看了框架,但其目的并不完全清楚。我会删除它。检查进程信息状态就足够了:-)
【讨论】:
@user370305 的回答容易出错,Android 操作系统开发人员不鼓励(请查看 https://groups.google.com/forum/#!msg/android-developers/zH-2bovZSLg/L2YM8Z1N-HwJ)
还有一种更简单的方法:
在所有活动都扩展的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
【讨论】:
借助新的 Android 生命周期架构扩展,我们可以轻松实现这一目标。
只需确保在 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>
现在,每当应用程序进入前台或后台时,我们都会收到与声明的两个方法关联的日志。
【讨论】:
onCreate 函数上检查类 ArchLifecycleApp,如果它在前台。
Android 架构组件库您可以使用ProcessLifecycleOwner 为onStart 和onStop 事件设置整个应用程序进程的侦听器。为此,请让您的应用程序类实现LifecycleObserver 接口,并将onStart 和onStop 的一些注释添加到您的前台和后台方法中。
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")
}
}
【讨论】:
我已尝试使用正在运行的进程中的包过滤器。但这很奇怪。取而代之的是,我尝试了新的解决方案,并且效果很好。我检查了很多次,通过这个模块得到了完美的结果。
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) {
}
});
}
【讨论】:
检查应用是在后台还是前台。如果应用在后台,此方法将返回 true。
首先将 GET_TASKS 权限添加到您的 AndroidManifest.xml 中
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;
}
【讨论】:
我通过创建一个基本活动类找到了一个简单的解决方案,你必须从这里扩展你的所有活动类:
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 的MainApplication 类并实现Application.ActivityLifecycleCallbacks developer.android.com/reference/android/app/…
对此没有全局回调,但对于每个活动,它都是 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();
}
}
}
【讨论】:
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();
}
}
请告诉我它是否适合你们所有人。
【讨论】:
基于 getRunningTasks() 的解决方案均不适用于最新的 Android 版本,getRunningTasks() 在 API 级别 21 中已被弃用。即使仍在使用它也可以没有返回足够的信息来确定应用程序是否在前台。
改为扩展 Application 类并使用 Application.ActivityLifecycleCallbacks 来跟踪应用程序可见性状态。
public class MyApplication extends Application {
static final String APP_STATE_FOREGROUND = "com.xxx.appstate.FOREGROUND";
static final String APP_STATE_BACKGROUND = "com.xxx.appstate.BACKGROUND";
private static int m_foreground = -1;
private Handler m_handler = new Handler();
private Runnable m_guard;
public static boolean isForeground() {
return m_foreground == 1;
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(m_guard != null) {
m_handler.removeCallbacks(m_guard);
m_guard = null;
}
if(m_foreground == 1)
return;
m_foreground = 1;
sendBroadcast(new Intent(APP_STATE_FOREGROUND));
}
@Override
public void onActivityPaused(Activity activity) {
if(m_foreground == 0)
return;
/*
* Use a 400ms guard to protect against jitter
* when switching between two activities
* in the same app
*/
m_guard = new Runnable() {
@Override
public void run() {
if(m_foreground == 1) {
m_foreground = 0;
sendBroadcast(new Intent(APP_STATE_BACKGROUND));
}
}
};
m_handler.postDelayed(m_guard, 400);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
使用 400 毫秒保护计时器可消除在同一应用中的活动之间切换时对后台状态的错误检测。可以随时使用以下方式查询背景/前景状态:
MyApplication.isForeground();
如果一个类对状态转换感兴趣,它也可以监听广播事件:
private static IntentFilter m_appStateFilter;
static {
m_appStateFilter = new IntentFilter();
m_appStateFilter.addAction(MyApplication.APP_STATE_FOREGROUND);
m_appStateFilter.addAction(MyApplication.APP_STATE_BACKGROUND);
}
private BroadcastReceiver m_appStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MyApplication.APP_STATE_FOREGROUND)) {
/* application entered foreground */
} else if (action.equals(MyApplication.APP_STATE_BACKGROUND)) {
/* application entered background */
}
}
};
registerReceiver(m_appStateReceiver, m_appStateFilter);
【讨论】:
在您的应用程序类中尝试 ActivityLifecycleCallbacks。
【讨论】:
以下是最新 Android SDK 的更新解决方案。
String PackageName = context.getPackageName();
ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
ComponentName componentInfo;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
List<ActivityManager.AppTask> tasks = manager.getAppTasks();
componentInfo = tasks.get(0).getTaskInfo().topActivity;
}
else
{
List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
componentInfo = tasks.get(0).topActivity;
}
if (componentInfo.getPackageName().equals(PackageName))
return true;
return false;
希望对您有所帮助,谢谢。
【讨论】:
以下解决方案适用于 API 级别 14+
背景 ComponentCallbacks2 — 查看文档并不是 100% 清楚你将如何使用它。但是,仔细观察,您会注意到 onTrimMemory 方法传入了一个标志。这些标志通常与内存可用性有关,但我们关心的是 TRIM_MEMORY_UI_HIDDEN。通过检查 UI 是否隐藏,我们可以假设应用程序现在在后台。不是很明显,但它应该可以工作。
前景化 ActivityLifecycleCallbacks — 我们可以通过覆盖 onActivityResumed 并跟踪当前应用程序状态(前景/背景)来使用它来检测前景。
创建将由自定义应用程序类实现的接口
interface LifecycleDelegate {
fun onAppBackgrounded()
fun onAppForegrounded()
}
创建一个将实现 ActivityLifecycleCallbacks 和 ComponentCallbacks2 并覆盖 onActivityResumed 和 onTrimMemory 方法的类
// Take an instance of our lifecycleHandler as a constructor parameter
class AppLifecycleHandler(private val lifecycleDelegate: LifecycleDelegate)
: Application.ActivityLifecycleCallbacks, ComponentCallbacks2 // <-- Implement these
{
private var appInForeground = false
// Override from Application.ActivityLifecycleCallbacks
override fun onActivityResumed(p0: Activity?) {
if (!appInForeground) {
appInForeground = true
lifecycleDelegate.onAppForegrounded()
}
}
// Override from ComponentCallbacks2
override fun onTrimMemory(level: Int) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// lifecycleDelegate instance was passed in on the constructor
lifecycleDelegate.onAppBackgrounded()
}
}
}
现在我们需要做的就是让我们的自定义应用程序类实现我们的 LifecycleDelegate 接口并注册。
class App : Application(), LifeCycleDelegate {
override fun onCreate() {
super.onCreate()
val lifeCycleHandler = AppLifecycleHandler(this)
registerLifecycleHandler(lifeCycleHandler)
}
override fun onAppBackgrounded() {
Log.d("Awww", "App in background")
}
override fun onAppForegrounded() {
Log.d("Yeeey", "App in foreground")
}
private fun registerLifecycleHandler(lifeCycleHandler: AppLifecycleHandler) {
registerActivityLifecycleCallbacks(lifeCycleHandler)
registerComponentCallbacks(lifeCycleHandler)
}
}
在 Manifest 中设置 CustomApplicationClass
<application
android:name=".App"
【讨论】:
appInForeground 永远不会再变成假的了。
从 Android 19 开始,您可以像这样在 Application 类的 onCreate() 中注册应用生命周期回调:
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new AppLifecycleCallback());
}
AppLifecycleCallback 如下所示:
class AppLifecycleCallback implements Application.ActivityLifecycleCallbacks {
private int numStarted = 0;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
if (numStarted == 0) {
//app went to foreground
}
numStarted++;
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
numStarted--;
if (numStarted == 0) {
// app went to background
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
【讨论】: