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

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

277334 次浏览

我建议阅读这一页:http://developer.android.com/reference/android/app/Activity.html

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

除非你自己跟踪它,否则无法确定你的任何活动是否可见。也许你应该考虑问一个新的StackOverflow问题,解释一下你想从用户体验中获得什么,这样我们也许可以给你其他的实现想法。

根据CommonsWare和Key所说的,你可以扩展Application类,并让你所有的活动在onPause/onResume方法上调用它。这将允许您知道哪些活动是可见的,但这可能会得到更好的处理。

你能详细说明一下你的想法吗?当你说在后台运行,你的意思是简单地让你的应用程序仍然在内存中,即使它目前不在屏幕上?你有没有考虑过使用服务作为一种更持久的方式来管理你的应用程序,当它不在焦点?

检测应用程序是否在后台运行的方法很少,但只有一种是完全可靠的:

  1. 正确的解决方案(学分转到CommonsWareNeTeInStEiN)
    自己使用Activity.onPauseActivity.onResume方法跟踪应用程序的可见性。在其他类中存储“可见性”状态。好的选择是你自己的ApplicationService的实现(如果你想从服务中检查活动可见性,也有这个解决方案的一些变化) , < br > < em > < / em >例子 < br > 实现自定义Application类(注意isActivityVisible()静态方法):

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

    AndroidManifest.xml中注册应用程序类:

    <application
    android:name="your.app.package.MyApplication"
    android:icon="@drawable/icon"
    android:label="@string/app_name" >
    

    onPauseonResume添加到项目中的每个Activity中(如果你愿意,你可以为你的Activities创建一个公共祖先,但如果你的activity已经从MapActivity/ListActivity等扩展,你仍然需要手写以下代码):

    @Override
    protected void onResume() {
    super.onResume();
    MyApplication.activityResumed();
    }
    
    
    @Override
    protected void onPause() {
    super.onPause();
    MyApplication.activityPaused();
    }
    
    < p >, < br > < em > < / em >更新 < br > ActivityLifecycleCallbacks在API级别14 (Android 4.0)中添加。您可以使用它们来跟踪应用程序的某个活动当前是否对用户可见。查看下面的玉米杆的回答了解详细信息。李< / p > < / > <李> < p > 错的人 < br > 我曾经建议以下解决方案:

    你可以用ActivityManager.getRunningAppProcesses()来检测当前的前台/后台应用程序,它会返回一个RunningAppProcessInfo记录的列表。要确定你的应用程序是否在前台,检查RunningAppProcessInfo.importance字段是否等于RunningAppProcessInfo.IMPORTANCE_FOREGROUND,而RunningAppProcessInfo.processName等于你的应用程序包名。

    此外,如果你从应用程序UI线程调用ActivityManager.getRunningAppProcesses(),它将为你的任务返回重要性IMPORTANCE_FOREGROUND,无论它是否实际在前台。在后台线程中调用它(例如通过AsyncTask),它将返回正确的结果。

    虽然这种解决方案可能会起作用(而且在大多数情况下确实有效),但我强烈建议不要使用它。原因如下。正如Dianne Hackborn所写:

    这些api不是为应用程序提供UI流程的基础,而是用于向用户显示正在运行的应用程序或任务管理器等。

    是的,在记忆中有一个关于这些事情的列表。但是,它在另一个进程中关闭了,由与您的进程分开运行的线程管理,并且您不能指望(a)及时看到以做出正确的决定或(b)在返回时拥有一致的图像。另外,关于“下一个”活动的决定总是在切换发生时做出的,直到那个确切的点(活动状态被短暂锁定以进行切换),我们才真正确定下一个活动将是什么。

    这里的实现和全局行为在将来也不能保证保持不变。

    我希望我在SO上发表答案之前读过这篇文章,但希望现在承认我的错误还为时不晚

  2. <李> < p > 另一个错误的解决方案 < br > 其中一个答案中提到的Droid-Fu库使用ActivityManager.getRunningTasks作为其isApplicationBroughtToBackground方法。请看上面Dianne的评论,也不要用那个方法。

Idolon的答案很容易出错,而且更复杂,尽管在这里检查android应用程序是否在前台?和这里从后台任务或服务确定当前的前台应用程序重复

有一个更简单的方法:

所有活动扩展的BaseActivity:

protected static boolean isVisible = false;


@Override
public void onResume()
{
super.onResume();
setVisible(true);
}




@Override
public void onPause()
{
super.onPause();
setVisible(false);
}

当你需要检查如果有任何应用程序活动在前台,请检查 isVisible();

要理解这种方法,请检查side-by-side活动生命周期的答案:活动并排的生命周期

在我的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)){...}

也许不优雅,但对我来说很管用……

不要用这个答案

user1269737的答案是正确的(谷歌/Android批准)这样做的方法。去读他们的答案,给他们一个+1。

为了子孙后代,我将把我最初的答案留在这里。这在2012年是最好的,但现在Android已经对此提供了适当的支持。

原来的答案

关键是使用ActivityLifecycleCallbacks(注意,这需要Android API级别14 (Android 4.0))。只需检查停止活动的数量是否等于启动活动的数量。如果它们相等,则应用程序处于后台。如果有更多已启动的活动,您的应用程序仍然可见。如果恢复的活动比暂停的活动多,应用程序不仅可见,而且还在前台。您的活动可以处于3种主要状态:可见且在前景中,可见但不在前景中,不可见且不在前景(即在背景中)。

这个方法真正的优点是它不存在getRunningTasks()所存在的异步问题,但你也不必修改应用程序中的每个Activity来设置/取消设置onResumed()/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

这是否适用于配置更改?

缺省情况下,否。你必须在你的清单文件中显式地设置configChanges=orientation|screensize (|与你想要的任何其他内容)并处理配置更改,否则你的活动将被销毁并重新创建。如果你不设置这个,你的活动的方法将会按照这个顺序被调用:onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume。正如您所看到的,没有重叠(通常,在两个活动之间切换时,两个活动会有非常短的重叠,这就是这种背景检测方法的工作方式)。为了解决这个问题,你必须设置configChanges,这样你的活动就不会被破坏。幸运的是,我不得不在我所有的项目中设置configChanges,因为我的整个活动在屏幕旋转/调整大小时被破坏是不可取的,所以我从来没有发现这是有问题的。(感谢dpimka刷新了我的记忆,并纠正了我!)

注意:一个

当我在这个回答中说“背景”时,我的意思是“你的应用不再可见”。Android活动可以是可见的,但不是在前台(例如,如果有一个透明的通知覆盖)。这就是为什么我更新了这个答案来反映这一点。

重要的是要知道Android在切换活动前景中没有任何东西时有一个奇怪的limbo时刻。出于这个原因,如果你在切换活动(在同一个应用程序中)时检查你的应用程序是否在前台,你会被告知你不在前台(即使你的应用程序仍然是活动的应用程序并且可见)。

你可以在你的ActivityonPause()方法 super.onPause()中检查你的应用程序是否在前台。记住我刚才说过的奇怪的边缘状态。

你可以在你的ActivityonStop()方法 super.onStop()中检查你的应用程序是否可见(即如果它不在后台)。

我自己实现了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实现中编写的日志。

我想建议你用另一种方法来做这件事。

我猜你想在程序启动时显示启动屏幕,如果它已经在后端运行,不要显示它。

您的应用程序可以连续地将当前时间写入特定的文件。 当你的应用程序正在启动时,检查最近的时间戳,如果current_time-last_time>是你指定的写入最近时间的时间范围,这意味着你的应用程序已经停止,要么是由系统杀死,要么是用户自己杀死
现在回答可能太迟了,但如果有人来拜访,我建议有一个解决方案, 一个应用程序想要知道它的状态是在后台还是在前台的原因可以有很多,有几个是, 1. 当用户在BG时显示祝酒和通知。 2.第一次执行一些任务的用户来自BG,如投票,重画等

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

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

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

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

我想出的最好的解决办法是使用计时器。

你已经在onPause()中启动了一个定时器,并在onResume()中取消了相同的定时器,有一个定时器的实例(通常在应用程序类中定义)。计时器本身被设置为在2秒后(或您认为合适的任何时间间隔)运行Runnable,当计时器触发时,您设置一个标志,将应用程序标记为在后台。

在取消定时器之前的onResume()方法中,您可以查询后台标志来执行任何启动操作(例如开始下载或启用位置服务)。

这个解决方案允许您在back堆栈上有几个活动,并且不需要任何权限来实现。

如果你也使用事件总线,这个解决方案也很有效,因为你的计时器可以简单地触发一个事件,应用程序的各个部分可以相应地做出响应。

这篇旧文章的另一个解决方案(对那些可能有帮助的人来说):


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


}

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

你应该使用一个共享的首选项来存储属性,并从你的活动中使用服务绑定对它进行操作。如果你只使用绑定(也就是从不使用startService),那么你的服务将只在你绑定它时运行,(绑定onResume和取消绑定onPause),这将使它只在前台运行,如果你确实想在后台工作,你可以使用常规的启动停止服务。

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

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

我尝试了推荐的解决方案,使用应用程序。ActivityLifecycleCallbacks和许多其他,但他们没有像预期的那样工作。感谢军士,我想出了一个相当简单和直接的解决方案,我将在下面描述。

解决方案的关键是理解这样一个事实:如果我们有ActivityA和ActivityB,并且我们从ActivityA调用ActivityB(而不是调用ActivityA.finish),那么ActivityB的onStart()将被称为之前 ActivityA onStop()

这也是onStop()onPause()之间的主要差异,在我读过的文章中没有人提到过。

因此,基于这个活动的生命周期行为,你可以简单地计算在你的程序中调用onStart()onPause()的次数。注意,你的程序的为每一个 Activity,你必须重写onStart()onStop(),以便增加/减少用于计数的静态变量。下面是实现此逻辑的代码。注意,我使用的是一个扩展了Application的类,所以不要忘记在Application标签中的Manifest.xml上声明:android:name=".Utilities",尽管它也可以使用一个简单的自定义类实现。

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

现在,在程序的每个Activity上,我们应该重写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();
}

根据这种逻辑,有两种可能的情况:

  1. stateCounter = 0:停止的数量与启动的活动数量相等,这意味着应用程序正在后台运行。
  2. stateCounter > 0:启动的数量大于停止的数量,这意味着应用程序正在前台运行。

注意:stateCounter < 0将意味着有更多停止的活动而不是启动的活动,这是不可能的。如果您遇到这种情况,那么这意味着您没有像应该的那样增加/减少计数器。

你已经准备好了。你应该检查你的应用程序是否在后台onStop()中。

当一个对话框出现在活动上方时,活动将被暂停,因此所有推荐的解决方案都是半解决方案。您还需要为对话框创建钩子。

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

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

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

public abstract void onTrimMemory (int level)

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

你可以将这个接口实现为activityservice等对象。

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

自从Android API 16以来,有一个简单的方法来检查应用程序是否在前台。这可能不是万无一失的,但Android上没有万无一失的方法。当你的服务接收到来自服务器的更新,并且必须决定是否显示通知时,这个方法就足够好了(因为如果UI是前台,用户会注意到更新而不通知)。

RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

在@玉米秆回答的基础上,包括一些有用的功能。

额外的功能:

  • 引入了单例模式,所以你可以在应用程序的任何地方这样做:
  • 增加了重复事件的处理(见注释//对可见性的变化采取一些行动和//对前景的变化采取一些行动)

App.java

public class App extends Application {
@Override
public void onCreate() {
super.onCreate();


registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}

AppLifecycleHandler.java

public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;


private final String DebugName = "AppLifecycleHandler";


private boolean isVisible = false;
private boolean isInForeground = false;


private static AppLifecycleHandler instance;


public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}


return instance;
}


private AppLifecycleHandler() {
}


@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}


@Override
public void onActivityDestroyed(Activity activity) {
}


@Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}


@Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}


@Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}


@Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}


private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}


// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);


// take some action on change of visibility
}


private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}


// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);


// take some action on change of in foreground


}


public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}


public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}

如果你打开开发人员设置“不要保留活动”-只检查创建的活动数量不够。你还必须检查isSaveInstanceState。我的自定义方法isApplicationRunning ()检查是否是android应用程序正在运行:

下面是我的工作代码:

public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
private int created;
private boolean isSaveInstanceState;
private static AppLifecycleService instance;


private final static String TAG = AppLifecycleService.class.getName();


public static AppLifecycleService getInstance() {
if (instance == null) {
instance = new AppLifecycleService();
}
return instance;
}


public static boolean isApplicationRunning() {
boolean isApplicationRunning = true;
if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
isApplicationRunning = false;
}
return isApplicationRunning;
}


public static boolean isSaveInstanceState() {
return AppLifecycleService.getInstance().isSaveInstanceState;
}


public static int getCountCreatedActvities() {
return AppLifecycleService.getInstance().created;
}


private AppLifecycleService() {
}


@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
this.isSaveInstanceState = true;
}


@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
++created;
}


@Override
public void onActivityDestroyed(Activity activity) {
--created;
}


@Override
public void onActivityResumed(Activity activity) {   }


@Override
public void onActivityPaused(Activity activity) { }




@Override
public void onActivityStarted(Activity activity) { }


@Override
public void onActivityStopped(Activity activity) { }


}

由于没有提到它,我将建议读者探索通过Android架构组件可用的ProcessLifecycleOwner

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

当人们问SO如何在ServiceActivity之间通信时,我通常建议使用LocalBroadcastManager


为什么?

我引用一下医生的话:

  • 你知道你广播的数据不会离开你的应用程序,所以不需要担心泄露私人数据。

  • 其他应用程序不可能将这些广播发送到你的应用程序,所以你不需要担心他们可以利用的安全漏洞。

  • 这比通过系统发送全球广播更有效。

文档里没有:

  • 它不需要外部库
  • 代码是最少的
  • 它易于实现和理解
  • 没有自定义实现的回调/超单例/进程内 李模式无论如何…< / >
  • 没有强引用 on ActivityApplication,…

描述

因此,你想检查是否有Activity当前在前台。通常在ServiceApplication类中这样做。

这意味着,你的Activity对象成为一个信号的发送者(I'm on / I'm off)。另一方面,你的Service变成了Receiver

两个时刻,你的Activity告诉你它是在前台还是在后台(是的,只有两个…不是6)。

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

Activity放到后面时,将调用onPause()

在这些时刻,你的Activity应该向你的Service发送信号来描述它的状态。

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

所以情况是:*

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

Service / Application将简单地监听这些信号并进行相应的操作。


代码(TLDR)

你的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是否在前台。只要从你的Service通过LocalBroadcastManager发送数据。如果Activity是打开的,那么它将做出响应并采取行动。

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

因此,在你的Activity中创建Receiver。在onResume()中注册,在onPause()中取消注册。不需要使用其他生命周期方法

onReceive()中定义Receiver行为(更新ListView,做这个,做那个,…)

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

在多个Activity的情况下,无论哪个Activity都将响应(如果它们也实现了Receiver)。

如果所有人都在后台,没有人会回应,信号就会丢失。

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


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

官方文档: .

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

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

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

谷歌解决方案 -不是一个黑客,像以前的解决方案。使用ProcessLifecycleOwner < br > < br > 芬兰湾的科特林:

class ArchLifecycleApp : Application(), LifecycleObserver {


override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
}


@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
// App in foreground
}


}

< br > < br > Java:

public class ArchLifecycleApp extends Application implements LifecycleObserver {


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


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded() {
//App in background
}


@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded() {
// App in foreground
}
}

在app.gradle

dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.0"


//New Android X dependency is this -
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
    

}


allprojects {
repositories {
...
google()
jcenter()
maven { url 'https://maven.google.com' }
}
}

你可以在这里阅读更多关于生命周期相关的体系结构组件——https://developer.android.com/topic/libraries/architecture/lifecycle

唯一正确的解决方法:

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) {
...
}
}


}

从支持库版本26开始,你可以使用ProcessLifecycleOwner,只需要像描述的在这里那样将它添加到你的依赖项中,例如:

dependencies {
def lifecycle_version = "1.1.1"


// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
//     Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}

然后只要在你想要应用程序状态的时候查询ProcessLifecycleOwner,示例:

// Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;


// Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
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
}

如果你想知道一个特定的活动是否在前台,如果你是一个没有直接访问应用程序的SDK,那么没有一个答案非常适合特定的情况。对我来说,我在后台线程刚刚收到一个新的聊天消息的推送通知,只想在聊天屏幕不在前台时显示系统通知。

使用在其他答案中推荐的ActivityLifecycleCallbacks,我已经创建了一个小的util类,用于存放MyActivity是否在前台的逻辑。

class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {


private var isMyActivityInForeground = false


init {
(context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}


fun isMyActivityForeground() = isMyActivityInForeground


override fun onActivityPaused(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = false
}
}


override fun onActivityResumed(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = true
}
}

这段代码将在任何条件下检查foregroundbackground:

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

芬兰湾的科特林代码:

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
}

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

只需将此方法添加到应用程序类中,并在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) {}
})
}

AFAIK,你甚至可以使isInBackground静态,这样你就可以在没有上下文的情况下使用伴生对象访问它

没有任何解决方案适合我,但我提出了一个原始的解决方案。这应该有用。如果isAppBackground返回,那么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;
}

这样怎么样:

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

简单直接的回答:

override fun onPause() {
Log.i("APP LIFECYCLE", "App Enter BACKground")
isForeground = false
super.onPause()
}


override fun onResume() {
Log.i("APP LIFECYCLE", "App Enter FOREground")
isForeground = true
super.onResume()
}

然后只需使用活动的isForeground属性来检查状态。