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


当前回答

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

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


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

其他回答

现在回答可能已经太迟了,但如果有人来访,我建议有一个解决方案, 一个应用程序想要知道它的状态是在后台还是在前台的原因可以有很多,有几个是, 1. 当用户在BG时显示祝酒和通知。 2.第一次从BG来的用户执行一些任务,如投票,重画等。

Idolon和其他人的解决方案解决了第一部分,但没有解决第二部分。如果你的应用程序中有多个活动,并且用户在它们之间切换,那么当你处于第二个活动时,可见标志将为假。所以它不能被确定地使用。

我做了一些CommonsWare建议的事情,“如果服务确定没有可见的活动,并且在一段时间内保持这种状态,那么在下一个逻辑停止点停止数据传输。”

粗体部分很重要,可以用来完成第二项。所以我所做的是一旦我得到onActivityPaused(),不改变可见直接为假,而是有一个3秒的定时器(这是下一个活动应该启动的最大值),如果没有onactivityresume()调用在接下来的3秒,改变可见为假。 类似地,在onactivityresume()如果有一个定时器,然后我取消它。 总之,可见变成了isAppInBackground。

对不起,不能复制粘贴代码…

唯一正确的解决方法:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        MyApp.mainActivity = this;
        super.onCreate(savedInstanceState);
        ...
    }

MyApp.java:

public class MyApp extends Application implements LifecycleObserver {

    public static MainActivity mainActivity = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onAppBackgrounded() {
        // app in background
        if (mainActivity != null) {
            ...
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onAppForegrounded() {
        // app in foreground
        if (mainActivity != null) {
            ...
        }
    }

}

这样怎么样:

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;
    }
}

我自己实现了ActivityLifecycleCallbacks。我正在使用SherlockActivity,但对于正常的活动类可能工作。

首先,我创建了一个接口,它有跟踪活动生命周期的所有方法:

public interface ActivityLifecycleCallbacks{
    public void onActivityStopped(Activity activity);
    public void onActivityStarted(Activity activity);
    public void onActivitySaveInstanceState(Activity activity, Bundle outState);
    public void onActivityResumed(Activity activity);
    public void onActivityPaused(Activity activity);
    public void onActivityDestroyed(Activity activity);
    public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}

其次,我在我的应用程序的类中实现了这个接口:

public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{

    @Override
    public void onCreate() {
        super.onCreate();           
    }

    @Override
    public void onActivityStopped(Activity activity) {
        Log.i("Tracking Activity Stopped", activity.getLocalClassName());

    }

    @Override
    public void onActivityStarted(Activity activity) {
        Log.i("Tracking Activity Started", activity.getLocalClassName());

    }

    @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());
    }

    @Override
    public void onActivityPaused(Activity activity) {
        Log.i("Tracking Activity Paused", activity.getLocalClassName());
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        Log.i("Tracking Activity Created", activity.getLocalClassName());
    }
}

第三,我正在创建一个从SherlockActivity扩展的类:

public class MySherlockActivity extends SherlockActivity {

    protected MyApplication nMyApplication;

    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        nMyApplication = (MyApplication) getApplication();
        nMyApplication.onActivityCreated(this, savedInstanceState);
    }

    protected void onResume() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityResumed(this);
        super.onResume();

    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityPaused(this);
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        nMyApplication.onActivityDestroyed(this);
        super.onDestroy();
    }

    @Override
    protected void onStart() {
        nMyApplication.onActivityStarted(this);
        super.onStart();
    }

    @Override
    protected void onStop() {
        nMyApplication.onActivityStopped(this);
        super.onStop();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        nMyApplication.onActivitySaveInstanceState(this, outState);
        super.onSaveInstanceState(outState);
    }   
}

第四,所有从SherlockActivity扩展的类,我替换为MySherlockActivity:

public class MainActivity extends MySherlockActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

}

现在,在日志记录中,您将看到MyApplication中Interface实现中编写的日志。

官方文件:

系统区分前台和后台应用程序。(用于服务限制的后台定义与内存管理使用的定义不同;一个应用程序可能在后台的内存管理,但在前台的能力,启动服务。)一个应用程序被认为是在前台,如果以下任何一个是真的:

它有一个可见的活动,无论该活动是启动还是暂停。 它有一个前台服务。 另一个前台应用通过绑定到它的一个服务或使用它的一个内容提供者来连接到该应用。例如,如果另一个应用绑定到它的: 输入法设置 壁纸服务 通知侦听器 语音或文字服务

如果这些条件都不为真,应用程序被认为是在后台。