Android 监听APP进入后台或切换到前台方案对比

Stella981
• 阅读 978

在我们开发的过程中,经常会遇到需要我们判断app进入后台,或者切换到前台的情况。比如我们想判断app切换到前台时,显示一个解锁界面,要求用户输入解锁密码才能继续进行操作;我们想判断app切换到后台,记录一下log;或者当用户切换回前台时,我们想刷新一下页面的数据等等......

android里面监听app前后台的方案很多(这还是得归根于安卓提供了丰富的api和强大的架构支撑,呵呵~),比如可以通过ActivityManager提供的getRunningAppProcesses()获取系统当前运行的app,从而判断app是否处于前台。或者通过监听点击Home键,判断app是否回到了后台。下面将针对笔者已知的几种方案,进行对比分析。

方案一:利用ActivityManager的RunningAppProcessInfo类
ActivityManager在整个系统里面起着非常重要的作用,主要为系统中运行着的activity交互提供接口,其中RunningAppProcessInfo类则封装了正在运行着的进程信息,当然也包含了正在运行的app的包名,因此我们可以activitymanager.getRunningAppProcesses()获取当前运行的app列表,对比自身的包名,来判断本身app是否处于前台运行。

这打断一下,ActivityManager框架是Android系统十分重要的一部分,在以后有时间,笔者会好好学习整理ActivityManager框架的分析。

回到这里,下面给出部分关键代码。

/**
     * App前后台状态
     */
    public boolean isForeground = false;
    @Override
    protected void onResume() {
        ......
        if (isForeground == false) {
            //由后台切换到前台
            isForeground = true;
        }
    }
 
    @Override
    protected void onPause() {
        ......
        if (!isAppOnForeground()) {
            //由前台切换到后台
            isForeground = false;
        }
    }
    /**
     * 判断app是否处于前台
     *
     * @return
     */
    public boolean isAppOnForeground() {
 
        ActivityManager activityManager = (ActivityManager) getApplicationContext()
                .getSystemService(Context.ACTIVITY_SERVICE);
        String packageName = getApplicationContext().getPackageName();
        /**
         * 获取Android设备中所有正在运行的App
         */
        List<RunningAppProcessInfo> appProcesses = activityManager
                .getRunningAppProcesses();
        if (appProcesses == null)
            return false;
 
        for (RunningAppProcessInfo appProcess : appProcesses) {
            // The name of the process that this object is associated with.
            if (appProcess.processName.equals(packageName)
                    && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
 
        return false;
    }

小结:通过ActivityManager来获取当前运行的app列表,然后判断我们的app是否处于前台,能基本达到我们的预期需求。但如果将上面代码放到每一个activity,或者activity基类里面,这消耗还是挺大的。而且而且,ActivityManager通过.getRunningAppProcesses()获取当前运行列表这个方法,在5.0以后已经被deprecated掉了....(心中万马奔腾...)

方案二:监听Home键点击

说起home键的监听,也算是android里面的一个梗。这看上去简单的功能,实际上实现起来却十分的曲折,这跟android系统的设计有很大的关系。当home键被点击的时候,会发出一个系统广播,在系统收到这个广播以后,会在framework层做一系列操作将当前的app退到后台,然后把事件消费掉不传给应用层,所以这时候 onKeyDown事件也接收不到了..用官方的解释就是——“Home key. This key is handled by the framework and is never delivered to applications.”。实际上这也是为了安全性的考虑,不然每家的app都监听home键,然后禁掉响应,不都成了流氓软件了。

官方不支持,可是这难不到我们万能的攻城狮们的,毕竟有很多想我们正规的开发者,还是需要监听home键来做一些如写日志之类的操作的。网上谷歌百度会有很多类似的解决方案,在这里就不展开了。(不过这里可以推荐一下)

小结:我们能监听到home键点击,当然就知道app处于前台还是后台了。但毕竟这个方案是基于官方不支持的前提下的,而且home键的监听在很多设备都会有兼容性的问题,因此我们不大推荐这样做。

方案三:利用ActivityLifecycleCallbacks监听所有activity的生命周期

通过监听所有activity的onStart、onStop调用,然后统计当前是不是所有的activity都调用了onStop,确实可以判断app处于了后台,不过让我们重写每一个activity的onStop,并加这段奇怪的代码,实在不大优雅,即使在activity基类里面统一写,那天如果忘了或者不需要继承基类的activity,就不大好了。

因此,这里推荐一个新的接口ActivityLifecycleCallbacks,说新也不新,其实早在API 14 (android 4.0)就已经推出了。ActivityLifecycleCallbacks接口在Application类里面,因此需要我们自己继承Application,自定义一个MyApplication,然后注册接口。ActivityLifecycleCallbacks为application提供了对所有activity生命周期的监听,因此我们通过重写ActivityLifecycleCallbacks的onActivityStarted和onActivityStopped方法,定义一个变量,来统计当前有几个activity处于前台。

/**
     * 当前Acitity个数
     */
    private int activityAount = 0;
    
    @Override
    public void onCreate() {
        ......
        registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
        ......
    }
 
    /**
     * Activity 生命周期监听,用于监控app前后台状态切换
     */
    ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }
 
        @Override
        public void onActivityStarted(Activity activity) {
//            if (activityAount == 0) {
//                //app回到前台
//                isForeground = true;
//            }
            activityAount++;
        }
 
        @Override
        public void onActivityResumed(Activity activity) {
        }
        @Override
        public void onActivityPaused(Activity activity) {
        }
 
        @Override
        public void onActivityStopped(Activity activity) {
            activityAount--;
            if (activityAount == 0) {
                isForeground = false;
            }
        }
 
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }
        @Override
        public void onActivityDestroyed(Activity activity) {
        }
    };

以上代码写在MyApplication里面。怎么样,是不是很简单快捷!而且准确无误。

总结:
1、利用ActivityManager的RunningAppProcessInfo类,直接粗暴,官方摒弃,不推荐;
2、监听Home键点击,官方不支持,兼容性差,不稳定,不推荐;
3、利用ActivityLifecycleCallbacks监听所有activity的生命周期,官方指定饮品,哦,不对,官方指定接口,大力推荐!我们举一反三,利用ActivityLifecycleCallbacks监听,我们还能控制我们的activity堆栈,甚至还可以在里面做日志统计...想想还是很强大的。

PS:虽则利用ActivityLifecycleCallbacks接口监听的方案最优,但这毕竟是4.0以后的产品,因此对于4.0以下的,可以考虑增加方案一判断。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
thinkcmf+jsapi 实现微信支付
首先从小程序端接收订单号、金额等参数,然后后台进行统一下单,把微信支付的订单号返回,在把订单号发送给前台,前台拉起支付,返回参数后更改支付状态。。。回调publicfunctionnotify(){$wechatDb::name('wechat')where('status',1)find();
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这