所谓后台,我的意思是应用程序的活动目前对用户都不可见?
当前回答
不要用这个答案
user1269737的答案是正确的(谷歌/Android批准)方法来做到这一点。去读他们的答案,给他们一个+1。
为了子孙后代,我将把我最初的答案留在这里。这在2012年是最好的,但现在Android已经对此提供了适当的支持。
原来的答案
The key is using ActivityLifecycleCallbacks (note that this requires Android API level 14 (Android 4.0)). Just check if the number of stopped activities is equal to the number of started activities. If they're equal, your application is being backgrounded. If there are more started activities, your application is still visible. If there are more resumed than paused activities, your application is not only visible, but it's also in the foreground. There are 3 main states that your activity can be in, then: visible and in the foreground, visible but not in the foreground, and not visible and not in the foreground (i.e. in the background).
这个方法的真正优点是它没有getRunningTasks()所做的异步问题,但你也不必修改应用程序中的每个Activity来设置/取消onresume ()/onPaused()中的某些内容。它只是几行自包含的代码,它可以在整个应用程序中工作。另外,它也不需要奇怪的权限。
MyLifecycleHandler.java:
public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
// I use four separate variables here. You can, of course, just use two and
// increment/decrement them instead of using four and incrementing them all.
private int resumed;
private int paused;
private int started;
private int stopped;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
++resumed;
}
@Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityStarted(Activity activity) {
++started;
}
@Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
// If you want a static function you can use to check if your application is
// foreground/background, you can use the following:
/*
// Replace the four variables above with these four
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
// And these two public static functions
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
*/
}
MyApplication.java:
// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
@Override
public void onCreate() {
// Simply add the handler, and that's it! No need to add any code
// to every activity. Everything is contained in MyLifecycleHandler
// with just a few lines of code. Now *that's* nice.
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
@Mewzer问了一些关于这种方法的好问题,我想在这里回答大家:
onStop()在内存不足的情况下不会被调用;这里有问题吗?
不。onStop()的文档说:
注意,这个方法可能永远不会被调用,在低内存的情况下,在调用onPause()方法后,系统没有足够的内存来保持你的活动进程运行。
这里的关键是“保持您的活动进程运行…”如果达到这种低内存情况,您的进程实际上会被杀死(不仅仅是您的活动)。这意味着这种检查后台性的方法仍然有效,因为a)如果您的进程被杀死,您无论如何都不能检查后台性,b)如果您的进程再次启动(因为创建了一个新的活动),MyLifecycleHandler的成员变量(无论是静态的还是非静态的)将被重置为0。
这是否适用于配置更改?
By default, no. You have to explicitly set configChanges=orientation|screensize (| with anything else you want) in your manifest file and handle the configuration changes, or else your activity will be destroyed and recreated. If you do not set this, your activity's methods will be called in this order: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. As you can see, there is no overlap (normally, two activities overlap very briefly when switching between the two, which is how this backgrounding-detection method works). In order to get around this, you must set configChanges so that your activity is not destroyed. Fortunately, I've had to set configChanges already in all of my projects because it was undesirable for my entire activity to get destroyed on screen rotate/resize, so I've never found this to be problematic. (thanks to dpimka for refreshing my memory on this and correcting me!)
注意:一个
当我在这个回答中说“背景”时,我的意思是“你的应用不再可见”。Android活动可以是可见的,但不是在前台(例如,如果有一个透明的通知覆盖)。这就是为什么我更新了这个答案来反映这一点。
重要的是要知道,当切换活动时,前台没有任何东西,Android有一个奇怪的边缘时刻。出于这个原因,如果你在切换活动(在同一个应用程序中)时检查你的应用程序是否在前台,你会被告知你不在前台(即使你的应用程序仍然是活动的应用程序并且可见)。
你可以在super.onPause()之后的Activity的onPause()方法中检查你的应用程序是否在前台。记住我刚才说过的奇怪的边缘状态。
你可以检查你的应用程序是否可见(即如果它不在后台)在你的活动的onStop()方法后super.onStop()。
其他回答
参见onActivityDestroyed函数中的注释。
适用于SDK目标版本14>:
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
public static int active = 0;
@Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
active--;
}
@Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
active++;
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
@Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
active++;
}
@Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
active--;
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
active--;
// if active var here ever becomes zero, the app is closed or in background
if(active == 0){
...
}
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
active++;
}
}
我认为这个问题应该更清楚。什么时候?在哪里?你想知道应用是否处于后台的具体情况是什么?
我只是用我的方法介绍我的解决方案。 我通过在我的应用程序中的每个活动的onStop方法中使用RunningAppProcessInfo类的字段“重要性”来完成这一点,这可以简单地通过为其他活动提供一个BaseActivity来扩展,实现onStop方法来检查“重要性”的值。代码如下:
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;
}
在我看来,许多答案引入了大量的代码,带来了很多复杂性和可读性。
当人们问SO如何在服务和活动之间通信时,我通常建议使用LocalBroadcastManager。
Why?
我引用一下医生的话:
你知道你广播的数据不会离开你的应用程序,所以不需要担心泄露私人数据。 其他应用程序不可能将这些广播发送到你的应用程序,所以你不需要担心他们可以利用的安全漏洞。 这比通过系统发送全球广播更有效。
文档里没有:
它不需要外部库 代码是最少的 它易于实现和理解 没有自定义的自实现回调/超单例/进程内 任何模式…… 没有对Activity, Application,…
描述
你想要检查是否有Activity当前在前台。您通常在服务或应用程序类中这样做。
这意味着,您的Activity对象成为信号的发送者(I'm on / I'm off)。另一方面,您的服务成为接收者。
Activity有两个时刻告诉你它是在前台还是在后台(是的,只有两个…不是6)。
当Activity进入前台时,onResume()方法被触发(也在onCreate()之后调用)。
当Activity回到后面时,onPause()被调用。
在这些时刻,您的活动应该向您的服务发送信号,以描述其状态。
在有多个Activity的情况下,记住一个Activity先进入后台,然后另一个进入前台。
所以情况是:*
Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON
服务/应用程序将简单地监听这些信号并进行相应的操作。
代码(TLDR)
你的服务必须实现一个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")
在服务中注册接收者::onCreate()
@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必须传达它们的状态。
在活动::onResume ()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
在活动::onPause ()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
这是一种非常非常常见的情况
开发人员:我想从我的服务发送数据并更新活动。我如何检查活动是否在前景?
通常不需要检查Activity是否在前台。只需从服务中通过LocalBroadcastManager发送数据。如果活动处于开启状态,那么它将做出响应并采取行动。
对于这种非常常见的情况,服务成为发送方,活动实现BroadcastReceiver。
因此,在您的活动中创建一个Receiver。在onResume()中注册,在onPause()中取消注册。不需要使用其他生命周期方法。
在onReceive()中定义Receiver行为(更新ListView,做这个,做那个,…)
这样Activity只会在它在前台的时候监听,如果它在后面或者被销毁了什么也不会发生。
在多个活动的情况下,任何一个活动都将响应(如果它们也实现了Receiver)。
如果所有人都在后台,没有人会回应,信号就会丢失。
通过Intent(见上面的代码)通过指定信号ID从服务发送数据。
除了多窗口支持。这可能很棘手(如果需要,请测试它)…
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
}
除非你自己跟踪它,否则无法确定你的任何活动是否可见。也许你应该考虑问一个新的StackOverflow问题,解释一下你想从用户体验中获得什么,这样我们也许可以给你其他的实现想法。
推荐文章
- 警告:API ' variable . getjavacompile()'已过时,已被' variable . getjavacompileprovider()'取代
- 安装APK时出现错误
- 碎片中的onCreateOptionsMenu
- TextView粗体通过XML文件?
- 如何使线性布局的孩子之间的空间?
- DSL元素android.dataBinding。enabled'已过时,已被'android.buildFeatures.dataBinding'取代
- ConstraintLayout:以编程方式更改约束
- PANIC: AVD系统路径损坏。检查ANDROID_SDK_ROOT值
- 如何生成字符串类型的buildConfigField
- Recyclerview不调用onCreateViewHolder
- Android API 21工具栏填充
- Android L中不支持操作栏导航模式
- 如何在TextView中添加一个子弹符号?
- PreferenceManager getDefaultSharedPreferences在Android Q中已弃用
- 在Android Studio中创建aar文件