检查Android应用程序是否在后台运行

376

所谓背景,指的是应用程序的所有活动当前对用户都不可见。


3
可能是重复的问题:如何确定我的活动是否在前台。 - Phrogz
12
我有点困惑……为什么安卓不能在应用程序类中简单地提供一个覆盖方法来处理这个问题呢?难道在平台层面上无法解决吗?@Override protected void onApplicationSentToBackground() { } - Chuck D
2
@ChuckD - 这是有道理的,这也是Android SDK有时候喜欢避免做的事情。 :/ - Mark
http://codingaffairs.blogspot.com/2016/05/check-if-your-android-app-is-in.html - Developine
2
iOS在这方面做得非常好,不确定为什么谷歌让这变得如此困难。这是一个非常明显的需求。 - Jerry Destremps
显示剩余5条评论
37个回答

1

1
这段代码将在任何情况下检查前景色和背景色:
Java 代码:
private static boolean isApplicationForeground(Context context) {
    KeyguardManager keyguardManager =
            (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);

    if (keyguardManager.isKeyguardLocked()) {
        return false;
    }
    int myPid = Process.myPid();

    ActivityManager activityManager =
            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    List<ActivityManager.RunningAppProcessInfo> list;

    if ((list = activityManager.getRunningAppProcesses()) != null) {
        for (ActivityManager.RunningAppProcessInfo aList : list) {
            ActivityManager.RunningAppProcessInfo info;
            if ((info = aList).pid == myPid) {
                return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
            }
        }
    }
    return false;
}

Kotlin代码:
private fun isApplicationForeground(context: Context): Boolean {
        val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
        if (keyguardManager.isKeyguardLocked) {
            return false
        }
        val myPid = Process.myPid()
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        var list: List<ActivityManager.RunningAppProcessInfo>
        if (activityManager.runningAppProcesses.also { list = it } != null) {
            for (aList in list) {
                var info: ActivityManager.RunningAppProcessInfo
                if (aList.also { info = it }.pid == myPid) {
                    return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                }
            }
        }
        return false
    }

0

我认为这个问题应该更加清晰明了。什么时候?在哪里?你想知道的具体情况是什么,以便确定你的应用程序是否在后台运行?

我会用自己的方式来介绍我的解决方案。
我通过在每个活动的onStop方法中使用RunningAppProcessInfo类的"importance"字段来完成此操作,在其他活动中提供一个BaseActivity来扩展,该活动实现onStop方法以检查"importance"的值。以下是代码:

public static boolean isAppRunning(Context context) {
    ActivityManager activityManager = (ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningAppProcessInfo> appProcesses = activityManager
        .getRunningAppProcesses();
    for (RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(context.getPackageName())) {
            if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
                return true;
            } 
        }
    }
    return false;
}

这并不是一个推荐的解决方案,正如@Idolon的回答中所提到的。 - CoolMind

0
在我看来,很多答案引入了大量的代码负担,并带来了许多复杂性和难以阅读性。
当人们在SO上询问如何在ServiceActivity之间通信时,我通常建议使用LocalBroadcastManager

为什么?

好吧,引用文档:

  • 您知道广播的数据不会离开您的应用程序,因此不需要担心泄漏私人数据。

  • 其他应用程序无法向您的应用程序发送这些广播,因此您不需要担心它们可以利用的安全漏洞。

  • 它比通过系统发送全局广播更有效率。

文档中没有提到的:

  • 它不需要外部库
  • 代码很简洁
  • 实现和理解速度快
  • 没有自定义的回调/超级单例/进程内模式等...
  • ActivityApplication等上没有强引用

描述

所以,您想要检查是否有任何Activity当前在前台。通常情况下,您会在ServiceApplication类中执行此操作。

这意味着,您的Activity对象成为信号的发送者(我在/我不在)。另一方面,您的Service则成为接收器。

两个时刻,您的Activity告诉您它是进入前台还是后台(只有两个...不是6个)。

Activity进入前台时,将触发onResume()方法(也称为onCreate()之后调用)。

Activity进入后台时,将调用onPause()

这些是您的Activity应向Service发送信号以描述其状态的时刻。

如果有多个Activity,请记住一个Activity首先进入后台,然后另一个Activity进入前台。

因此,情况将是:*

Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON

Service / Application 将会持续监听这些信号并相应地采取行动。


代码(简版)

你的Service必须实现一个BroadcastReceiver以便监听信号。

this.localBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // received data if Activity is on / off
    }
}

public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL") 

Service::onCreate() 中注册 Receiver

@Override
protected void onCreate() {
    LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}

Service::onDestroy()中取消注册它。

@Override
protected void onDestroy() {
    // I'm dead, no need to listen to anything anymore.
    LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}

现在你的Activity必须通知它们的状态。
Activity::onResume()中。
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

Activity::onPause()

Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent    
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);

非常常见的情况

开发者:我想从我的Service发送数据并更新Activity。如何检查Activity是否在前台?

通常不需要检查Activity是否在前台。只需通过LocalBroadcastManager从您的Service发送数据即可。如果Activity正在运行,则它将响应并执行操作。

对于这种非常常见的情况,Service成为发送方,而Activity实现BroadcastReceiver

因此,在您的Activity中创建一个Receiver。在onResume()中注册它,并在onPause()中取消注册。 没有必要使用其他生命周期方法

onReceive()中定义Receiver的行为(更新ListView,执行此操作,执行那个操作等)。

这样Activity只有在前台时才会监听,如果在后台或者被销毁就不会发生任何事情。

如果有多个Activity,哪一个处于活跃状态就会响应(如果它们也实现了Receiver)。

如果所有Activity都在后台,则没有人会响应,信号将会被忽略。

通过指定信号ID通过IntentService发送数据(见上面的代码)。


  • 除了多窗口支持。这可能有点棘手(如果需要,请进行测试)...

0

另一个没有额外依赖的方法是这个:

只需将此方法添加到您的应用程序类中,并在 onCreate() 中调用它即可。

var isInBackground = true

private fun setupActivityLifecycleCallbacks() {
    registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
        override fun onActivityStarted(activity: Activity) {}
        override fun onActivityResumed(activity: Activity) {
            isInBackground = false
        }
        override fun onActivityPaused(activity: Activity) {
            isInBackground = true
        }
        override fun onActivityStopped(activity: Activity) {}
        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
        override fun onActivityDestroyed(activity: Activity) {}
    })
}

据我所知,您甚至可以将isInBackground设置为静态的,以便使用伴生对象在没有上下文的情况下访问它。

0

这样怎么样:

boolean isBackgrounded() {
    try {
        context.startService(new Intent(action));
        return false;
    }
    catch (IllegalStateException exc) {
        // "Not allowed to start service Intent: app is in background"
        return true;
    }
}

通常情况下,使用异常来控制应用程序流程是一个不好的想法。 - Luciano Brum

0

对于这篇旧帖子的另一个解决方案(对于那些可能有所帮助的人):


<application android:name=".BaseApplication" ... >

public class BaseApplication extends Application {

    private class Status {
        public boolean isVisible = true;
        public boolean isFocused = true;
    }

    private Map<Activity, Status> activities;

    @Override
    public void onCreate() {
        activities = new HashMap<Activity, Status>();
        super.onCreate();
    }

    private boolean hasVisibleActivity() {
        for (Status status : activities.values())
            if (status.isVisible)
                return true;
        return false;
    }

    private boolean hasFocusedActivity() {
        for (Status status : activities.values())
            if (status.isFocused)
                return true;
        return false;
    }

    public void onActivityCreate(Activity activity, boolean isStarting) {
        if (isStarting && activities.isEmpty())
            onApplicationStart();
        activities.put(activity, new Status());
    }

    public void onActivityStart(Activity activity) {
        if (!hasVisibleActivity() && !hasFocusedActivity())
            onApplicationForeground();
        activities.get(activity).isVisible = true;
    }

    public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
        activities.get(activity).isFocused = hasFocus;
    }

    public void onActivityStop(Activity activity, boolean isFinishing) {
        activities.get(activity).isVisible = false;
        if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
            onApplicationBackground();
    }

    public void onActivityDestroy(Activity activity, boolean isFinishing) {
        activities.remove(activity);
        if(isFinishing && activities.isEmpty())
            onApplicationStop();
    }

    private void onApplicationStart() {Log.i(null, "Start");}
    private void onApplicationBackground() {Log.i(null, "Background");}
    private void onApplicationForeground() {Log.i(null, "Foreground");}
    private void onApplicationStop() {Log.i(null, "Stop");}

}

public class MyActivity extends BaseActivity {...}

public class BaseActivity extends Activity {

    private BaseApplication application;

    @Override
    protected void onCreate(Bundle state) {
        application = (BaseApplication) getApplication();
        application.onActivityCreate(this, state == null);
        super.onCreate(state);
    }

    @Override
    protected void onStart() {
        application.onActivityStart(this);
        super.onStart();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        application.onActivityWindowFocusChanged(this, hasFocus);
        super.onWindowFocusChanged(hasFocus);
    }

    @Override
    protected void onStop() {
        application.onActivityStop(this, isFinishing());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        application.onActivityDestroy(this, isFinishing());
        super.onDestroy();
    }

}

0
fun isAppInForeground(): Boolean {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false

    val appProcesses = activityManager.runningAppProcesses ?: return false

    val packageName = packageName
    for (appProcess in appProcesses) {
        if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
            return true
        }
    }

    return false
}

0

使用 getApplicationState().isInForeground() 怎么样?


0

您应该使用共享首选项来存储属性,并使用服务绑定从您的活动中执行操作。如果仅使用绑定(即从未使用startService),则您的服务仅在绑定到它时运行(在onResume上绑定,在onPause上解除绑定),这将使其仅在前台运行,如果您确实想要在后台工作,则可以使用常规的启动停止服务。


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