Android开发小技巧-监听和判断应用在前台还是后台的几种方式

4,374 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

判断App是否在前台的几种方式

平常的开发中常用到这种判断,例如我们IM的消息,如果我们收到别人发给我的消息了,是否应该在顶部通知栏展示,如果在前台,那么就不需要展示通知,如果在后台就需要展示通知。

如何判断应用在前台还是在后台,目前比较好用的有三种方式:

一、RunningTaskInfo的判断

当App在前台的时候,就会在栈顶,我们通过ActivityManager拿到运行中的栈,拿到栈顶的Activity,并判断当前运行的的App包名是否相同。

    public static boolean isBackground(Context context) {

        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
        if (!tasks.isEmpty()) {
            ComponentName topActivity = ((ActivityManager.RunningTaskInfo) tasks.get(0)).topActivity;

            return topActivity != null && !TextUtils.isEmpty(topActivity.getPackageName()) &&
                    !topActivity.getPackageName().equals(context.getPackageName());
        }

        return false;
    }

使用的时候, 先点test1 再点test2 然后返回homne页面等待Log。

   fun test1() {
            YYLogUtils.w("Check Background1: " + BackgroundCheck.isBackground(commContext()))
    }

    fun test2() {
        CommUtils.getHandler().postDelayed({
            YYLogUtils.w("Check Background2:" + BackgroundCheck.isBackground(commContext()))
        },3000)
    }

二、RunningAppProcessInfo的判断

通过 ActivityManager 获取当前运行的进程,遍历找到我们当前应用的进程。判断这个进程是否处于前台。

 public static boolean isBackgroundProcess(Context context) {

        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        boolean isBackground = true;
        String processName = "empty";
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.processName.equals(context.getPackageName())) {
                processName = appProcess.processName;
                if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED) {
                    isBackground = true;
                } else if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                        || appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
                    isBackground = false;
                } else {
                    isBackground = true;
                }
            }
        }

        YYLogUtils.i("是否在后台:" + isBackground + " processName:" + processName);

        return isBackground;
    }

使用的时候, 同样的先点test1 再点test2 然后返回homne页面等待Log。

   fun test1() {
            YYLogUtils.w("Check BackgroundProcess1: " + BackgroundCheck.isBackgroundProcess(commContext()))
    }

    fun test2() {
        CommUtils.getHandler().postDelayed({
            YYLogUtils.w("Check BackgroundProcess2:" + BackgroundCheck.isBackgroundProcess(commContext()))
        },3000)
    }

三、ActivityLifecycleCallbacks的回调

通过监听Application中注册Activity的生命周期回调,通过记录Activity的状态,并通过count来计数,判断是否在前台


public class ForegroundCheck implements Application.ActivityLifecycleCallbacks {

    //单例
    private static final ForegroundCheck instance = new ForegroundCheck();

    private static final int CHECK_DELAY = 500;

    //用于判断是否程序在前台
    private boolean foreground = false, paused = true;
    //handler用于处理切换activity时的短暂时期可能出现的判断错误
    private final Handler handler = new Handler();
    private Runnable check;

    public static void init(Application app) {
        app.registerActivityLifecycleCallbacks(instance);
    }

    public static ForegroundCheck get() {
        return instance;
    }

    private ForegroundCheck() {
    }

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

    }

    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {

    }

    @Override
    public void onActivityPaused(@NonNull Activity activity) {
        paused = true;
        if (check != null)
            handler.removeCallbacks(check);

        handler.postDelayed(check = () -> {
            if (foreground && paused) {
                foreground = false;
            }
        }, CHECK_DELAY);

    }

    @Override
    public void onActivityResumed(@NonNull Activity activity) {
        paused = false;
        foreground = true;
        if (check != null)
            handler.removeCallbacks(check);

    }

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

    @Override
    public void onActivityStarted(@NonNull Activity activity) {
        if (count == 0) {
            //后台切换到前台
            YYLogUtils.i("监听到应用进入前台");
        }
        count++;
    }

    @Override
    public void onActivityStopped(@NonNull Activity activity) {
        count--;
        if (count == 0) {
            //前台切换到后台
            YYLogUtils.i("监听到应用进入后台了");
        }
    }

    //记录Activity的总个数,用于判断监听是在前后还是后台
    public int count = 0;

    /**
     * 判断当前应用在前台还是在后台,如果在后台会启动推送
     */
    public boolean isForeground() {
        return foreground;
    }

}

使用: 必须在Application中初始化,传入application对象去注册生命周期回调。

@HiltAndroidApp
class DemoApplication : BaseApplication() {

    override fun onCreate() {
        super.onCreate()

        ForegroundCheck.init(this)
    }
}

测试:同样的先点test1 再点test2 然后返回homne页面等待Log。

   fun test1() {
        YYLogUtils.w("ForegroundCheck isForeground: " + ForegroundCheck.get().isForeground)
    }

    fun test2() {
        CommUtils.getHandler().postDelayed({
              YYLogUtils.w("ForegroundCheck isForeground: " +  ForegroundCheck.get().isForeground)
          },3000)
    }

四、DefaultLifecycleObserver 实现监听

基于 lifecycle 高版本实现的监听,

需要版本2.4.0及以上版本,依赖版本如下:

androidx.lifecycle:lifecycle-process:2.4.1
androidx.lifecycle:lifecycle-runtime-ktx:2.4.1

然后非常简单的即可实现同样的逻辑

@HiltAndroidApp
class DemoApplication : BaseApplication() {

    override fun onCreate() {
        super.onCreate()

        //必须process版本为2.4.0版本以上 (集成process默认添加startup库)
        ProcessLifecycleOwner.get().lifecycle.addObserver(AutoForegroundObserver())
    }

    //必须common为2.4.0版本以上
    inner class AutoForegroundObserver: DefaultLifecycleObserver {
        override fun onStart(owner: LifecycleOwner) {
            //应用进入前台
            YYLogUtils.w("DemoApplication-应用进入前台")
        }
        override fun onStop(owner: LifecycleOwner) {
            //应用进入后台
            YYLogUtils.w("DemoApplication-应用进入后台")
        }
    }

}

效果:

其本质还是调用的 registerActivityLifecycleCallbacks 监听了所有Activity的onStart onStop ,注意 process 库是在 Application 的 onCreate 方法之前的,内部又依赖到了StartUp库。

总结

目前的几种方法都可以使用,测试平台 Android12 三星A14手机 完美运行。

个人是比较推荐第三种方式,没有使用系统服务,相对更健壮一点。并且我们还能在监听的方法中管理 Activity 对 Activity 的数量统计之类的做操作。不需要高版本的 lifecycle 库。

比如我们定义一个类 ActivityManage 我们可以使用栈 Stack<Activity> 来管理当前的Activity。 在 ForegroundCheck 类中 onActivityCreated 的时候 添加到栈,在 onActivityDestroyed 的时候移除栈。同样的可以实现Activity 的栈管理。

相关的源码都已经在上面贴出,如果还想更方便的可以看源码

完结!