如何判断Android应用程序是否在前台运行?

时间:2011-03-31 18:36:33

标签: android android-activity

我正在我的Android应用程序中执行由c2dm触发的状态栏通知。如果应用程序正在运行,我不想显示通知。您如何确定应用程序是否正在运行且位于前台?

18 个答案:

答案 0 :(得分:111)

或者,您可以使用ActivityManager方法检查getRunningTasks正在运行的任务。然后检查返回的任务列表中的第一个任务(前台任务),如果这是您的任务。
这是代码示例:

public Notification buildNotification(String arg0, Map<String, String> arg1) {

    ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> services = activityManager
            .getRunningTasks(Integer.MAX_VALUE);
    boolean isActivityFound = false;

    if (services.get(0).topActivity.getPackageName().toString()
            .equalsIgnoreCase(appContext.getPackageName().toString())) {
        isActivityFound = true;
    }

    if (isActivityFound) {
        return null;
    } else {
        // write your code to build a notification.
        // return the notification you built here
    }

}

并且不要忘记在 manifest.xml 文件中添加GET_TASKS权限,以便能够在上面的代码中运行getRunningTasks()方法:

<uses-permission android:name="android.permission.GET_TASKS" />

p / s:如果同意这种方式,请注意,现在已弃用此权限。

答案 1 :(得分:52)

制作一个全球变量,例如private boolean mIsInForegroundMode;,并在false中分配onPause()值,在true分配onResume()值。

示例代码:

private boolean mIsInForegroundMode;

@Override
protected void onPause() {
    super.onPause();
    mIsInForegroundMode = false;
}

@Override
protected void onResume() {
    super.onResume();
    mIsInForegroundMode = true;
}

// Some function.
public boolean isInForeground() {
    return mIsInForegroundMode;
}

答案 2 :(得分:46)

这是一篇非常古老的帖子,但仍然非常相关。以上接受的解决方案可能有效但错误。 As Dianne Hackborn wrote:

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

是的,这些东西在内存中保留了一个列表。但是,它是在另一个进程中关闭的,由与你的线程分开运行的线程管理,而不是你可以依赖的东西(a)及时看到做出正确的决定,或者(b)在你返回时有一致的画面。此外,关于“下一个”活动的内容的决定总是在切换发生的时刻完成,直到确切的点(活动状态被短暂锁定以进行切换),我们才实际上知道接下来会发生什么。

此处的实施和全球行为不能保证在未来保持不变。

正确的解决方案是实施:ActivityLifeCycleCallbacks

这基本上需要一个Application Class,并且可以在那里设置处理程序以识别应用程序中活动的状态。

答案 3 :(得分:23)

正如Vinay所说,可能最好的解决方案(支持更新的Android版本,14岁以上)是在ActivityLifecycleCallbacks类实现中使用Application

package com.telcel.contenedor.appdelegate;

import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;

/** Determines global app lifecycle states. 
 * 
 * The following is the reference of activities states:
 * 
 * The <b>visible</b> lifetime of an activity happens between a call to onStart()
 * until a corresponding call to onStop(). During this time the user can see the
 * activity on-screen, though it may not be in the foreground and interacting with 
 * the user. The onStart() and onStop() methods can be called multiple times, as 
 * the activity becomes visible and hidden to the user.
 * 
 * The <b>foreground</b> lifetime of an activity happens between a call to onResume()
 * until a corresponding call to onPause(). During this time the activity is in front
 * of all other activities and interacting with the user. An activity can frequently
 * go between the resumed and paused states -- for example when the device goes to
 * sleep, when an activity result is delivered, when a new intent is delivered -- 
 * so the code in these methods should be fairly lightweight. 
 * 
 * */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {

    /** Manages the state of opened vs closed activities, should be 0 or 1. 
     * It will be 2 if this value is checked between activity B onStart() and
     * activity A onStop().
     * It could be greater if the top activities are not fullscreen or have
     * transparent backgrounds.
     */
    private static int visibleActivityCount = 0;

    /** Manages the state of opened vs closed activities, should be 0 or 1
     * because only one can be in foreground at a time. It will be 2 if this 
     * value is checked between activity B onResume() and activity A onPause().
     */
    private static int foregroundActivityCount = 0;

    /** Returns true if app has foreground */
    public static boolean isAppInForeground(){
        return foregroundActivityCount > 0;
    }

    /** Returns true if any activity of app is visible (or device is sleep when
     * an activity was visible) */
    public static boolean isAppVisible(){
        return visibleActivityCount > 0;
    }

    public void onActivityCreated(Activity activity, Bundle bundle) {
    }

    public void onActivityDestroyed(Activity activity) {
    }

    public void onActivityResumed(Activity activity) {
        foregroundActivityCount ++;
    }

    public void onActivityPaused(Activity activity) {
        foregroundActivityCount --;
    }


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

    public void onActivityStarted(Activity activity) {
        visibleActivityCount ++;
    }

    public void onActivityStopped(Activity activity) {
        visibleActivityCount --;
    }
}

在Application onCreate()方法中:

registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());

然后ApplicationLifecycleManager.isAppVisible()ApplicationLifecycleManager.isAppInForeground()将用于了解所需的状态。

答案 4 :(得分:17)

从API 16开始,你可以这样做:

static boolean shouldShowNotification(Context context) {
    RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
    ActivityManager.getMyMemoryState(myProcess);
    if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
        return true;

    KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
    // app is in foreground, but if screen is locked show notification anyway
    return km.inKeyguardRestrictedInputMode();
}

答案 5 :(得分:15)

仅供参考,如果您使用Gadenkan解决方案(这太棒!!)请不要忘记添加

<uses-permission android:name="android.permission.GET_TASKS" />

到清单。

答案 6 :(得分:14)

稍微清理过的Gadenkan's solution版本。把它放在任何Activity上,或者也可以作为你所有活动的基类。

protected boolean isRunningInForeground() {
    ActivityManager manager = 
         (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
    if (tasks.isEmpty()) {
        return false;
    }
    String topActivityName = tasks.get(0).topActivity.getPackageName();
    return topActivityName.equalsIgnoreCase(getPackageName());
}

为了能够致电getRunningTasks(),您需要在AndroidManifest.xml中添加此内容:

<uses-permission android:name="android.permission.GET_TASKS"/>

请注意Javadoc所说的ActivityManager.getRunningTasks()

  

注意:此方法仅用于调试和呈现任务   管理用户界面。这绝不应该用于核心逻辑   在应用程序中,例如基于不同行为之间的决定   在这里找到的信息。这种用途不受支持,并且会支持   可能在未来破裂。

更新(2015年2月)

请注意,{1}}在API级别21 中已被弃用!

  

getRunningTasks()开始,这个   方法不再适用于第三方应用程序:   引入以文档为中心的最近意味着它可以泄漏人   给呼叫者的信息。为了向后兼容,它仍然会   返回其数据的一小部分:至少是调用者自己的任务,   可能还有一些其他的任务,例如家里已知的不是   敏感。

所以我之前写的内容更具相关性:

在许多情况下,您可能会想出更好的解决方案。例如,在LOLLIPOPonPause()中执行某些操作,可能在所有活动的BaseActivity中执行操作。

(在我们的例子中,如果我们不在前台,我们不希望启动离线警报活动,因此在BaseActivity onResume()中,我们只需取消订阅RxJava onPause()离线“信号。”

答案 7 :(得分:9)

跟进Gadenkan的回复我需要这样的东西,所以我可以判断我的应用程序是不是在前台运行,但我需要一些应用程序范围内的东西,并且不要求我在整个应用程序中设置/取消设置标记。

Gadenkan的代码几乎击中了头部,但它不是我自己的风格,并觉得它可能更整洁,所以在我的应用程序中它浓缩到这个。

if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}

(旁注:你可以删除!如果你想让支票反过来工作)

虽然采用这种方法,但您需要GET_TASKS权限。

答案 8 :(得分:2)

我想补充一点更安全的方法 - 在创建通知之前检查你的应用是否在后台 - 只是分别禁用和启用广播接收器onPause()和onResume()。

此方法可让您更好地控制实际应用程序逻辑,并且将来不太可能更改。

@Override
protected void onPause() {
    unregisterReceiver(mHandleMessageReceiver);
    super.onPause();
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}

答案 9 :(得分:2)

通过将活动映射到布尔值,我找到了一种更简单,更准确的方法来检查应用程序是在前台还是后台。

检查完整的要点here

答案 10 :(得分:2)

仅当您希望在活动开始时执行某些操作以及要检查应用是否位于前台或后台时,此功能才有用。

您可以通过代码执行一项简单的技巧,而不是使用活动管理器。 如果密切观察活动周期,两个活动之间的流程和前景到背景的流程如下。 假设A和B是两个活动。

从A过渡到B时:     1.调用A的onPause()     2.调用B的onResume()     3.当B完全恢复时,调用A的onStop()

当应用进入后台时:     1.调用A的onPause()     2. A的onStop()称为

您只需在活动中放置一个标记即可检测您的背景事件。

制作抽象活动并将其从其他活动中扩展出来,这样就无需在任何需要背景事件的地方复制粘贴所有其他活动的代码。

在抽象活动中,创建标志isAppInBackground。

在onCreate()方法中:   isAppInBackground = false;

在onPause()方法中:   isAppInBackground = false;

在onStop()方法中:   isAppInBackground = true;

如果isAppInBackground为true,您只需要检入onResume()。 n检查完你的标志后再设置isAppInBackground = false

对于两个活动之间的转换,因为onstop()的第一个将在第二个actvity恢复后始终被调用,flag将永远不会为true,当app在后台时,onStop()活动将在onPause之后立即被调用,因此该标志将当你稍后打开应用程序时,这是真的。

这种方法还有一个方案。 如果您的任何应用程序屏幕已经打开并且您将移动设备置于空闲状态,那么一段时间后移动设备将进入睡眠模式,当您解锁移动设备时,它将在后台事件中处理。

答案 11 :(得分:2)

这是我使用的方法(和支持方法):

private boolean checkIfAppIsRunningInForeground() {
    ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
        if(appProcessInfo.processName.contains(this.getPackageName())) {
            return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
        }
    }
    return false;
}

private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
    switch (appImportance) {
        //user is aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
            return true;
        //user is not aware of app
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
        case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
        default:
            return false;
    }
}

答案 12 :(得分:1)

根据各种答案和评论,这里有一个更加内联的版本,您可以添加到帮助程序类:

public static boolean isAppInForeground(Context context) {
  List<RunningTaskInfo> task =
      ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
          .getRunningTasks(1);
  if (task.isEmpty()) {
    return false;
  }
  return task
      .get(0)
      .topActivity
      .getPackageName()
      .equalsIgnoreCase(context.getPackageName());
}

如其他答案中所述,您需要向AndroidManifest.xml添加以下权限。

<uses-permission android:name="android.permission.GET_TASKS"/>

答案 13 :(得分:1)

以下是@ user2690455描述的上述简单解决方案的代码。虽然它看起来有点冗长,但你会看到它实际上非常轻量级

在我的情况下,我们也使用AppCompatActivity,所以我必须有2个基类。

public class BaseActivity extends Activity {

    /**
     * Let field be set only in base class
     * All callers must use accessors,
     * and then it's not up to them to manage state.
     *
     * Making it static since ..
     * 1. It needs to be used across two base classes
     * 2. It's a singleton state in the app
     */
    private static boolean IS_APP_IN_BACKGROUND = false;

    @Override
    protected void onResume() {
        super.onResume();

        BaseActivity.onResumeAppTracking(this);

        BaseActivity.setAppInBackgroundFalse();
    }

    @Override
    protected void onStop() {
        super.onStop();

        BaseActivity.setAppInBackgroundTrue();
    }

    @Override
    protected void onPause() {
        super.onPause();

        BaseActivity.setAppInBackgroundFalse();
    }

    protected static void onResumeAppTracking(Activity activity) {

        if (BaseActivity.isAppInBackground()) {

            // do requirements for returning app to foreground
        }

    }

    protected static void setAppInBackgroundFalse() {

        IS_APP_IN_BACKGROUND = false;
    }

    protected static void setAppInBackgroundTrue() {

        IS_APP_IN_BACKGROUND = true;
    }

    protected static boolean isAppInBackground() {

        return IS_APP_IN_BACKGROUND;
    }
}

答案 14 :(得分:0)

从支持库版本26开始,您可以使用ProcessLifecycleOwner来确定应用程序的当前状态,只需将其添加到您所描述的here这样的依赖项中即可,例如:

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,例如,要检查应用程序是否在前台运行,只需执行以下操作:

 boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
 if(!isAppInForeground)
    //Show Notification in status bar

答案 15 :(得分:0)

此处提到的先前方法并非最佳。基于任务的方法需要一个可能不需要的权限,并且&#34;布尔&#34;方法很容易并发修改混乱。

我使用的方法(我相信)在大多数情况下效果很好:

拥有&#34; MainApplication&#34;用于跟踪 AtomicInteger 中的活动计数的课程:

import android.app.Application;

import java.util.concurrent.atomic.AtomicInteger;

public class MainApplication extends Application {
    static class ActivityCounter {
        private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);

        public static boolean isAppActive() {
            return ACTIVITY_COUNT.get() > 0;
        }

        public static void activityStarted() {
            ACTIVITY_COUNT.incrementAndGet();
        }

        public static void activityStopped() {
            ACTIVITY_COUNT.decrementAndGet();
        }
    }
}

并创建一个其他活动将扩展的基本Activity类:

import android.app.Activity;
import android.support.annotation.CallSuper;

public class TestActivity extends Activity {
    @Override
    @CallSuper
    protected void onStart() {
        MainApplication.ActivityCounter.activityStarted();
        super.onStart();
    }

    @Override
    @CallSuper
    protected void onStop() {
        MainApplication.ActivityCounter.activityStopped();
        super.onStop();
    }
}

答案 16 :(得分:0)

     public static boolean isAppRunning(Context context) {

 // check with the first task(task in the foreground)
 // in the returned list of tasks

   ActivityManager activityManager = (ActivityManager)
   context.getSystemService(Context.ACTIVITY_SERVICE);
 List<RunningTaskInfo> services =
 activityManager.getRunningTasks(Integer.MAX_VALUE);
     if
     (services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
     {
     return true;
     }
     return false;
     }

答案 17 :(得分:0)

此处没有全局回调,但对于每个活动,它都是onStop()。你不需要搞乱一个原子int。只需要一个包含已启动活动数的全局int,在每个活动中增加onStart()并在onStop()中递减它。

关注this