文档章节

【Android】前后台切换监听

xesam
 xesam
发布于 2016/03/07 21:57
字数 1224
阅读 1259
收藏 3

【Android】前后台切换监听

Android 本身并有提供这样的监听,所以就只能走偏门了。

首先,需要定义一下,什么叫“前台”,什么叫“后台”。本文定义如下:

前台

Activity 处在 FOREGROUND 优先级

后台

App进程没有停止,除去在“前台”的所有情况

所以,退到后台的方式太多了,大致有:

  1. 按Home键
  2. 按“最近任务”键
  3. 从通知栏启动其他应用
  4. 从应用内部启动其他应用
  5. 关掉屏幕

既然是监听变化,所以肯定是在相关生命周期的回调中来进行处理。ActivityA 启动 ActivityB 的生命周期方法调用顺序如下:

ActivityA#onPause -> ActivityB#onStart -> ActivityB#onResume -> ActivityA#onStop

从 ActivityB 回退到 ActivityA 的生命周期方法调用顺序如下:

ActivityB#onPause -> ActivityA#onStart -> ActivityA#onResume -> ActivityB#onStop -> ActivityB#onDestroy

一个思路就是通过统计当前活动的 Activity 数目来计算。

在 Activity#onStart 中来检测前一个状态是否是“后台”,如果是,则触发“切换到前台”事件,并将活动 Activity 数目加1。 在 Activity#onStop 中并将活动 Activity 数目减1。如果活动的 Activity 数目等于0, 就认为当前应用处于“后台”状态, 并触发“切换到后台”事件。

所以,一个初步方案大致是,实现一个基类 BaseActivity,并重写以下 onStart 和 onStop 回调方法:

    private static int compatStartCount = 0;
    private static boolean isCompatForeground = true;

    @Override
    public void onStart() {
        super.onStart();
        compatStartCount++;
        if (!isCompatForeground) {
            isCompatForeground = true;
            onBackgroundToForeground(activity);
        }
    }
    
    @Override
    public void onStop() {
        super.onStop();
        compatStartCount--;
        if (compatStartCount == 0) {
            isCompatForeground = false;
            onForegroundToBackground(activity);
        }
    }

开始趟坑

  1. 在关掉/点亮屏幕的情况下,android3 之前不会触发 onStart 和 onStop 回调。只会触发 onPause 和 onStop。所以以上代码失效。 按理来说,这应该是 Android 的含糊之处,onStop 的触发时机定义如下:

     Called when you are no longer visible to the user
    

按理来说,屏幕关闭的时候也符合条件,但是 android3 之前并未按照如此定义而来。所以需要 hack 一下:

    private static boolean isCompatLockStop = false;

    private static boolean isStandard() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
    }
    
    public static boolean isInteractive(Context context) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT_WATCH) {
            return pm.isInteractive();
        } else {
            return pm.isScreenOn();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (isStandard()) {
            //np
        } else {
            if (isCompatLockStop) {
                isCompatLockStop = false;
                onStart();
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (isStandard()) {
            //np
        } else {
            if (!isInteractive(activity)) { //锁屏触发
                isCompatLockStop = true;
                onStop();
            }
        }
    }

对于 android3 之前的系统,我们在 onPause 中处理 锁屏问题,如果 onPause 被触发的时候,手机处于 “非交互” 状态,就认为是按下了电源键(或者其他锁屏方式),触发“后台”状态。 在 onResume 中判断如果是从锁屏界面恢复,则回到“前台”状态。

存在问题:点亮屏幕的时候就被认为回到“前台”状态,这个暂未找到好的方法避免。

  1. 快速锁屏/点亮的情况下,会多次触发生命周期回调。

这个问题在 nexus 5 (Android 6.0) 上稳定重现,重现步骤:关掉屏幕后快速点亮再快速关闭。本来预期的表现应该是:

onPause
onStop

但是实际的表现如下:

onPause
onStop
onStart
onResume
onPause
onStop

所以,最终结果就是:

前台 -> 后台
后台 -> 前台 
前台 -> 后台

多了一个周期。

这个问题我还没有解决。尝试过解决办法有:

1.在 onStart 中判断 inKeyguardRestrictedInputMode 状态:

    public static boolean inKeyguardRestrictedInputMode(Context context) {
        KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        return keyguardManager.inKeyguardRestrictedInputMode();
    }

在锁屏状态下就不触发计数。但是这个判断根本不可信,在快速切换的情况下,在非锁屏情况下返回 true 的几率也比较大。

  1. 网上有一种判断应用是否在前台的方法
    public boolean isAppOnForeground() {
    
        ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); 
        String packageName = getApplicationContext().getPackageName(); 
        
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager 
            .getRunningAppProcesses(); 
        if (appProcesses == null) 
          return false; 
        
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { 
          // The name of the process that this object is associated with. 
          if (appProcess.processName.equals(packageName) 
              && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { 
            return true; 
          } 
        } 
        return false; 
    } 

但是这种测试方法并不靠谱,比如在魅族手机上,锁屏状态下也是返回 true。

  1. 监听 ACTION_USER_PRESENT, ACTION_SCREEN_ON, ACTION_SCREEN_OFF 等事件。

回调事件比较晚,在生命周期中已经跑完好几圈之后,广播才接收到。而且在各个生命周期内无法判断是否因为锁屏导致的周期变化。

顺便说一下, ACTION_USER_PRESENT 和 ACTION_SCREEN_ON 存在下面几种情况:

  1. 只触发 ACTION_SCREEN_ON
  2. 先触发 ACTION_SCREEN_ON,再触发 ACTION_USER_PRESENT (电源键,然后解锁)
  3. 先触发 ACTION_USER_PRESENT,再触发 ACTION_SCREEN_ON (指纹解锁)

这样的话,无法通过简单的计数来判断,需要加入 hashCode 来处理。

所以最终这种快速切换的情况被我忽略了,因为无非就是多了一个周期而已。

不完美实现

对于 API>=14 的系统上,可以直接增加 ActivityLifecycleCallbacks 监听,这样可以省去定义 BaseActivity 的步骤,减少侵入性。

所以在 API>=14 的系统上,直接使用 ActivityLifecycleCallbacks, 如果需要兼容 2.3,那么还是需要抽象出 BaseActivity。

演示代码地址 : https://github.com/xesam/AppMonitor

##Q群:315658668

© 著作权归作者所有

xesam
粉丝 30
博文 77
码字总数 44843
作品 0
武汉
程序员
私信 提问
android 后台服务如何监听主屏切换事件

在app开发中,我有一个后台服务,需要监听用户的主屏切换事件,比如用户滑动桌面,从桌面1切换到桌面1,这时候后台服务需要知道用户做了这个动作. 查看了api,好像 广播事件里并没有切换屏幕这个事...

xxcxy
2013/05/06
2.1K
2
Android程序开机启动&&监听情景模式切换

要求:设置一个android应用程序开机启动一个服务,此服务用来监听情景模式的切换。 首先要知道在android中开机启动程序是通过广播机制实现的,在android手机启动完成之后,系统会发送一个名叫...

晨曦之光
2012/03/05
1K
0
视频|手机锁屏了APP能不能窃听?我亲自试了试

文章作者为网络尖刀团队创始人曲子龙,授权雷锋网编辑及转载 最近这几天关于通过手机监听用户敏感信息,通过NLP处理传到云端做大数据分析的事儿吵的沸沸扬扬的。搞技术的人忙着撕逼能不能监听...

又田
03/22
0
0
Android 插件化 02 —— 提线木偶

回顾: 上一篇通过一个小实验,验证了容器项目能够通过一些偏门的方法获取插件apk的资源,并且显示插件简单的Activity界面。 但插件作为一个独立的应用,不会像Demo那样只有一个简单的界面。...

飛飛萨
2017/06/07
0
0
onSaveInstanceState和onRestoreInstanceState的用处

很多不明白Activity类中包含的onSaveInstanceState和onRestoreInstanceState有什么用,首先 Android123声明下使用这两个方法时一定要注意情况和了解Activity的生命周期,否则有的时候 onSave...

鉴客
2011/12/19
297
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot系列教程JPA之新增记录使用姿势

SpringBoot系列教程JPA之新增记录使用姿势 上一篇文章介绍了如何快速的搭建一个JPA的项目环境,并给出了一个简单的演示demo,接下来我们开始业务教程,也就是我们常说的CURD,接下来进入第一...

小灰灰Blog
32分钟前
2
0
大话文本检测经典模型:Pixel-Anchor

文本检测是深度学习中一项非常重要的应用,在前面的文章中已经介绍过了很多文本检测的方法,包括CTPN(详见文章:大话文本检测经典模型CTPN)、SegLink(详见文章:大话文本检测经典模型Seg...

雪饼
35分钟前
0
0
手把手教你写一个RPC

1.1 RPC 是什么 定义:RPC(Remote Procedure Call Protocol)——远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC...

我最喜欢三大框架
44分钟前
1
0
系统监控-SpringBoot四大神器之Actuator

1. 为什么要使用Actuator来监控SpringBoot项目? 首先之前博客提到过使用JDK自带的JVM监控工具、Psi-Probe Tomcat监控工具以及Javamelody,也提到了Psi-Probe的强大,但是Psi-Probe针对jar包...

秋日芒草
45分钟前
5
0
pip通过setup.py和git仓库安装package

安装setup.py配置文件中的包 进入到setup.py所在目录 pip install -e . 1 安装git仓库中的包 pip install git+git clone 仓库地址.git 1 python代码打包为whl格式 python setup.py bdist_wh...

dillonxiao
47分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部