检查 Android 应用程序是否在前台?

128

我查看了很多关于这个问题的答案,但它们都只涉及单个活动。如何检查整个应用程序是否在前台运行?


1
ProcessLifecycleOwner是最新的解决方案。 - S.R
15个回答

2

最近的Android版本中,所有基于getRunningTasks()的解决方案都无法使用,因为在API 21级别中已经被弃用了。即使仍在使用,它也不能返回足够的信息来确定应用程序是否在前台。

相反,扩展Application类并使用Application.ActivityLifecycleCallbacks来跟踪应用程序可见性状态。

public class MyApplication extends Application {
    static final String APP_STATE_FOREGROUND = "com.xxx.appstate.FOREGROUND";
    static final String APP_STATE_BACKGROUND = "com.xxx.appstate.BACKGROUND";
    private static int m_foreground = -1;
    private Handler m_handler = new Handler();
    private Runnable m_guard;

    public static boolean isForeground() {
        return m_foreground == 1;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {

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

            @Override
            public void onActivityStarted(Activity activity) {
            }

            @Override
            public void onActivityResumed(Activity activity) {
                if(m_guard != null) {
                    m_handler.removeCallbacks(m_guard);
                    m_guard = null;
                }
                if(m_foreground == 1)
                    return;
                m_foreground = 1;
                sendBroadcast(new Intent(APP_STATE_FOREGROUND));
            }

            @Override
            public void onActivityPaused(Activity activity) {
                if(m_foreground == 0)
                    return;
                /*
                 * Use a 400ms guard to protect against jitter
                 * when switching between two activities
                 * in the same app
                 */
                m_guard = new Runnable() {
                    @Override
                    public void run() {
                        if(m_foreground == 1) {
                            m_foreground = 0;
                            sendBroadcast(new Intent(APP_STATE_BACKGROUND));
                        }
                    }
                };
                m_handler.postDelayed(m_guard, 400);
            }

            @Override
            public void onActivityStopped(Activity activity) {
            }

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

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

使用400ms守卫计时器可以消除在同一个应用程序中切换活动时背景状态的误检。可以随时查询后台/前台状态,方法如下:

MyApplication.isForeground();

如果一个类对状态转换感兴趣,它也可以监听广播事件:

private static IntentFilter m_appStateFilter;

static {
    m_appStateFilter = new IntentFilter();
    m_appStateFilter.addAction(MyApplication.APP_STATE_FOREGROUND);
    m_appStateFilter.addAction(MyApplication.APP_STATE_BACKGROUND);
}

private BroadcastReceiver m_appStateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(MyApplication.APP_STATE_FOREGROUND)) {
            /* application entered foreground */
        } else if (action.equals(MyApplication.APP_STATE_BACKGROUND)) {
            /* application entered background */
        }
    }
};
registerReceiver(m_appStateReceiver, m_appStateFilter);

1
尝试在您的Application类中使用ActivityLifecycleCallbacks。

1
从Android 19开始,您可以在Application类的onCreate()中注册应用程序生命周期回调,如下所示:
@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(new AppLifecycleCallback());
}

AppLifecycleCallback的代码如下:

class AppLifecycleCallback implements Application.ActivityLifecycleCallbacks {
    private int numStarted = 0;

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

    }

    @Override
    public void onActivityStarted(Activity activity) {
        if (numStarted == 0) {
           //app went to foreground
        }
        numStarted++;
    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {
        numStarted--;
        if (numStarted == 0) {
            // app went to background
        }
    }

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

    }

    @Override
    public void onActivityDestroyed(Activity activity) {

    }
}

4
为什么要用计数器而不是简单地使用布尔变量? - Alice Van Der Land

1
以下解决方案适用于API级别14+。
后台运行 ComponentCallbacks2 - 查看文档不是100%清楚如何使用此方法。但是,仔细查看,您会注意到onTrimMemory方法传递了一个标志。这些标志通常与内存可用性有关,但我们关心的是TRIM_MEMORY_UI_HIDDEN。通过检查UI是否隐藏,我们可以假设应用程序现在在后台。虽然不是很明显,但应该有效。
前台运行 ActivityLifecycleCallbacks - 我们可以通过覆盖onActivityResumed并跟踪当前应用程序状态(前台/后台)来使用它来检测前台。
创建我们的接口,将由自定义应用程序类实现。
interface LifecycleDelegate {
    fun onAppBackgrounded()
    fun onAppForegrounded()
}

创建一个类,该类将实现ActivityLifecycleCallbacks和ComponentCallbacks2接口,并覆盖onActivityResumed和onTrimMemory方法。
// Take an instance of our lifecycleHandler as a constructor parameter
class AppLifecycleHandler(private val lifecycleDelegate: LifecycleDelegate) 
: Application.ActivityLifecycleCallbacks, ComponentCallbacks2 // <-- Implement these 
  {
private var appInForeground = false

      // Override from Application.ActivityLifecycleCallbacks
    override fun onActivityResumed(p0: Activity?) {
       if (!appInForeground) {
          appInForeground = true
          lifecycleDelegate.onAppForegrounded()
       }
    }

      // Override from ComponentCallbacks2
    override fun onTrimMemory(level: Int) { 
       if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
       // lifecycleDelegate instance was passed in on the constructor
          lifecycleDelegate.onAppBackgrounded()
       }
    }
}

现在我们只需要让自定义的Application类实现我们的LifecycleDelegate接口并进行注册即可。
class App : Application(), LifeCycleDelegate {

    override fun onCreate() {
        super.onCreate()
        val lifeCycleHandler = AppLifecycleHandler(this)
        registerLifecycleHandler(lifeCycleHandler)
    }

    override fun onAppBackgrounded() {
        Log.d("Awww", "App in background")
    }

    override fun onAppForegrounded() {
        Log.d("Yeeey", "App in foreground")
    }

    private fun registerLifecycleHandler(lifeCycleHandler: AppLifecycleHandler) {
        registerActivityLifecycleCallbacks(lifeCycleHandler)
        registerComponentCallbacks(lifeCycleHandler)
    }

}

在清单文件中设置CustomApplicationClass。
<application
        android:name=".App"

似乎appInForeground再也不会变成false了。 - android developer

1
以下是最新Android SDK的更新解决方案。
String PackageName = context.getPackageName();
        ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
        ComponentName componentInfo;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            List<ActivityManager.AppTask> tasks = manager.getAppTasks();
            componentInfo = tasks.get(0).getTaskInfo().topActivity;
        }
        else
        {
            List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
            componentInfo = tasks.get(0).topActivity;
        }

        if (componentInfo.getPackageName().equals(PackageName))
            return true;
        return false;

希望这有所帮助,谢谢。

无法在安卓6.0.1的CT50设备上运行。 - Ayaz Alifov
@Krzysztof Huminski,getTaskInfo()和getTaskInfo()自api 21(Lollipop)起可用,而不是api 23(M)。 - user2167877

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接