所谓后台,我的意思是应用程序的活动目前对用户都不可见?


当前回答

参见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++;
    }
}

其他回答

你可以使用ComponentCallbacks2来检测应用程序是否在后台。顺便说一下,这个回调只在API级别14(冰淇淋三明治)及以上可用。

你会得到一个方法的调用:

onTrimMemory (int级)

如果级别是ComponentCallbacks2。TRIM_MEMORY_UI_HIDDEN则应用程序处于后台。

您可以将此接口实现到活动、服务等。

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
   @Override
   public void onConfigurationChanged(final Configuration newConfig) {

   }

   @Override
   public void onLowMemory() {

   }

   @Override
   public void onTrimMemory(final int level) {
     if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
        // app is in background
     }
   }
}

我认为这个问题应该更清楚。什么时候?在哪里?你想知道应用是否处于后台的具体情况是什么?

我只是用我的方法介绍我的解决方案。 我通过在我的应用程序中的每个活动的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;
}

我尝试了推荐的使用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()中检查你的应用程序是否在后台。

我建议你仔细阅读这个页面:http://developer.android.com/reference/android/app/Activity.html

简而言之,你的活动在onStop()被调用后不再可见。

在我看来,许多答案引入了大量的代码,带来了很多复杂性和可读性。

当人们问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从服务发送数据。


除了多窗口支持。这可能很棘手(如果需要,请测试它)…