所谓后台,我的意思是应用程序的活动目前对用户都不可见?
当前回答
我尝试了推荐的使用Application的解决方案。ActivityLifecycleCallbacks和许多其他的,但是它们没有像预期的那样工作。感谢Sarge,我想出了一个非常简单和直接的解决方案,我将在下面描述。
解决方案的关键是理解这样一个事实:如果我们有ActivityA和ActivityB,并且我们从ActivityA调用ActivityB(而不是调用ActivityA.finish),那么ActivityB的onStart()将在ActivityA onStop()之前被调用。
这也是onStop()和onPause()之间的主要区别,在我读过的文章中没有人提到。
So based on this Activity's Lifecycle behavior, you can simply count how many times did onStart() and onPause() got called in your program. Note that for each Activity of your program, you must override onStart() and onStop(), in order to increment/decrement the static variable used for counting. Below is the code implementing this logic. Note that I am using a class that extends Application, so dont forget to declare on Manifest.xml inside Application tag: android:name=".Utilities", although it can be implemented using a simple custom class too.
public class Utilities extends Application
{
private static int stateCounter;
public void onCreate()
{
super.onCreate();
stateCounter = 0;
}
/**
* @return true if application is on background
* */
public static boolean isApplicationOnBackground()
{
return stateCounter == 0;
}
//to be called on each Activity onStart()
public static void activityStarted()
{
stateCounter++;
}
//to be called on each Activity onStop()
public static void activityStopped()
{
stateCounter--;
}
}
现在,在我们程序的每个活动上,我们应该重写onStart()和onStop()和增量/减量,如下所示:
@Override
public void onStart()
{
super.onStart();
Utilities.activityStarted();
}
@Override
public void onStop()
{
Utilities.activityStopped();
if(Utilities.isApplicationOnBackground())
{
//you should want to check here if your application is on background
}
super.onStop();
}
根据这种逻辑,有两种可能的情况:
stateCounter = 0:停止活动的数量与启动活动的数量相等,这意味着应用程序正在后台运行。 stateCounter > 0:启动的次数大于停止的次数,即应用程序在前台运行。
注意:stateCounter < 0意味着有更多停止的活动而不是启动的活动,这是不可能的。如果您遇到这种情况,那么这意味着您没有像应该的那样增加/减少计数器。
你已经准备好了。你应该在onStop()中检查你的应用程序是否在后台。
其他回答
在我的onResume和onPause活动中,我写了一个isVisible布尔值给sharedpreferences。
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
Editor editor = sharedPrefs.edit();
editor.putBoolean("visible", false);
editor.commit();
如有需要可在其他地方阅读,
// Show a Toast Notification if App is not visible (ie in background. Not running, etc)
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
if(!sharedPrefs.getBoolean("visible", true)){...}
也许不优雅,但对我来说很管用……
当一个对话框出现在活动上方时,活动将被暂停,因此所有推荐的解决方案都是半解决方案。您还需要为对话框创建钩子。
检测应用程序是否在后台运行的方法很少,但只有一种是完全可靠的:
The right solution (credits go to Dan, CommonsWare and NeTeInStEiN) Track visibility of your application by yourself using Activity.onPause, Activity.onResume methods. Store "visibility" status in some other class. Good choices are your own implementation of the Application or a Service (there are also a few variations of this solution if you'd like to check activity visibility from the service). Example Implement custom Application class (note the isActivityVisible() static method): public class MyApplication extends Application { public static boolean isActivityVisible() { return activityVisible; } public static void activityResumed() { activityVisible = true; } public static void activityPaused() { activityVisible = false; } private static boolean activityVisible; } Register your application class in AndroidManifest.xml: <application android:name="your.app.package.MyApplication" android:icon="@drawable/icon" android:label="@string/app_name" > Add onPause and onResume to every Activity in the project (you may create a common ancestor for your Activities if you'd like to, but if your activity is already extended from MapActivity/ListActivity etc. you still need to write the following by hand): @Override protected void onResume() { super.onResume(); MyApplication.activityResumed(); } @Override protected void onPause() { super.onPause(); MyApplication.activityPaused(); } Update ActivityLifecycleCallbacks were added in API level 14 (Android 4.0). You can use them to track whether an activity of your application is currently visible to the user. Check Cornstalks' answer below for the details. The wrong one I used to suggest the following solution: You can detect currently foreground/background application with ActivityManager.getRunningAppProcesses() which returns a list of RunningAppProcessInfo records. To determine if your application is on the foreground check RunningAppProcessInfo.importance field for equality to RunningAppProcessInfo.IMPORTANCE_FOREGROUND while RunningAppProcessInfo.processName is equal to your application package name. Also if you call ActivityManager.getRunningAppProcesses() from your application UI thread it will return importance IMPORTANCE_FOREGROUND for your task no matter whether it is actually in the foreground or not. Call it in the background thread (for example via AsyncTask) and it will return correct results. While this solution may work (and it indeed works most of the time) I strongly recommend to refrain from using it. And here's why. As Dianne Hackborn wrote: These APIs are not there for applications to base their UI flow on, but to do things like show the user the running apps, or a task manager, or such. Yes there is a list kept in memory for these things. However, it is off in another process, managed by threads running separately from yours, and not something you can count on (a) seeing in time to make the correct decision or (b) have a consistent picture by the time you return. Plus the decision about what the "next" activity to go to is always done at the point where the switch is to happen, and it is not until that exact point (where the activity state is briefly locked down to do the switch) that we actually know for sure what the next thing will be. And the implementation and global behavior here is not guaranteed to remain the same in the future. I wish I had read this before I posted an answer on the SO, but hopefully it's not too late to admit my error. Another wrong solution Droid-Fu library mentioned in one of the answers uses ActivityManager.getRunningTasks for its isApplicationBroughtToBackground method. See Dianne's comment above and don't use that method either.
不要用这个答案
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()。
没有任何解决方案适合我,但我提出了一个原始的解决方案。这应该有用。如果isAppBackground返回false,那么app必须在前台。
public static boolean isAppBackground(Context context){
boolean isBackground=true;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH){
List<ActivityManager.RunningAppProcessInfo> runningProcesses =activityManager.getRunningAppProcesses();
for(ActivityManager.RunningAppProcessInfo processInfo:runningProcesses){
if(processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
for(String activeProcess:processInfo.pkgList){
if(activeProcess.equals(context.getPackageName())){
isBackground = false;
}
}
}
}
}else{
List<ActivityManager.RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1);
if(taskInfo.size()>0) {
ComponentName componentName = taskInfo.get(0).topActivity;
if(componentName.getPackageName().equals(context.getPackageName())){
isBackground = false;
}
}
}
return isBackground;
}
推荐文章
- 警告: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文件