美文网首页Android 知识安卓Android收藏集
Android进阶 - 监视活动窗口

Android进阶 - 监视活动窗口

作者: 梦想编织者灬小楠 | 来源:发表于2018-04-02 18:10 被阅读828次

    摘要

    在Android程序的开发维护过程中,我们可能经常需要知道自己所看到的界面处于哪一个Activity中,相信大部分程序员的做法是“在基类里打Log”,很传统没毛病o(╯□╰)o....

    前几天,在应用宝的应用商店内发现了一个很有意思的APP - 当前Activity,可以显示出当前界面上显示的 应用包名Activity类名。然后,一直很好奇是如何实现的,最近抽时间研究了一下。

    下面是研究成果:

    image_example.png

    开源项目CurrentActivity地址:

    https://github.com/sinawangnan7/CurrentActivity

    欢迎Star...(๑•̀ㅂ•́)و✧

    正文

    本文主要讲述两部分内容:

    1、CurrentActivity的使用场景

    2、CurrentActivity的实现思路

    CurrentActivity的使用场景

    1、定位自己APP的页面,获取类名称

    这应该是我们经常碰到的问题,测试人员经常拿着出Bug的页面询问“开发人员”,如果这个页面还不是“此开发人员”写的,估计查起来就费劲了。此工具能帮助开发人员提高查找效率。

    2、分析其他APP命名及功能

    可以查看其他APP的页面路径和命名,给自己开发做个参考(下面以微信支付宝为例)。

    (1) 微信

    wx.png

    观察上图,可以看到 微信 的Activity在命名时是以 "UI" 为后缀进行标识的,类在分包时也放在了ui包目录下。另外,还有一个比较有意思的发现,微信的“主界面”和“聊天界面”用的是同一个Activity

    (2) 支付宝

    alipay.png

    笔者公司开发的APP和支付宝APP类似,也是做 互联网金融 的。所以,产品经理在设计功能和流程时经常会“参考”支付宝是如何实现的,比如需要确定 哪些页面用原生做,哪些需要用H5来做。但是,支付宝的原生和H5在使用体验上很难感知出来(这里支付宝做得很赞),这时这个工具就可以派上用场了,支付宝展示H5的页面用的是H5Activity

    最近,笔者需要做一个类似支付宝的支付键盘,一开始以为是纯Dialog实现的,最后发现实际上用Activity包了一层。这其实也并不意外,第三方APP在吊起“支付宝支付”时,用户如果安装了支付宝,吊起的也是这个FlyBirdWindowActivity。

    CurrentActivity的实现思路

    这个监视活动的窗口是如何实现的?

    本文只讲述 思路和核心代码,完整代码请参看GitHub开源项目:
    CurrentActivity

    实现这个监视窗口需要解决两个问题:

    • 1、如何向界面顶部添加一个显示窗口?

    • 2、如何监听应用切换、Activity切换?


    第一个问题很简单,通过WindowManager添加一个TextView即可, 但是需要注意下悬浮窗权限问题,这个问题很多博客都给出了解决方案,笔者简单说下,就不在赘述了。

    笔者做了一个简单的窗口视图管理器:

    完整代码:WindowViewContainer.java

    创建窗口的核心代码如下:

    /**
     * 添加窗口视图
     */
    private void addView() {
        // 创建布局参数
        mParams = new WindowManager.LayoutParams();
        // 获取窗口管理器
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        // 设置类型
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Android O 以上,使用TYPE_APPLICATION_OVERLAY弹窗类型
            mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            // Android O 以下,使用TYPE_SYSTEM_ALERT弹窗类型
            mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }
        // 设置标签(FLAG_NOT_FOCUSABLE表示窗口不会获取焦点;FLAG_NOT_TOUCHABLE表示窗口不会接收Touch事件,即将Touch事件向下层分发)
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        // 设置位图模式 (PixelFormat.RGBA_8888可以使背景透明。不设置默认PixelFormat.OPAQUE,即不透明)
        mParams.format = PixelFormat.RGBA_8888;
        // 设置分布位置(距左对齐 + 距顶对齐)
        mParams.gravity = Gravity.LEFT | Gravity.TOP;
        // 设置布局宽/高为自适应
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        // 添加TextView
        mWindowManager.addView(mTextView, mParams);
        // 记录视图已被添加、显示
        isAdded = true;
        isShow = true;
    }
    
    

    提醒:
    1.注意悬浮窗权限问题,可参考 AndroidManifest.xml 中的权限声明。
    2.适配Android 8.0,请使用TYPE_APPLICATION_OVERLAY弹窗类型。


    第二个问题:如何监听应用切换、Activity切换?笔者查了很长时间,最后发现了一个叫 AccessibilityService(辅助服务)的东西,了解完 AccessibilityService 感觉好像发现了新大陆,其实这个“辅助服务”使用最为出名的是“微信自动抢红包插件”。但是,今天笔者只是简单介绍下AccessibilityService(辅助服务)的入门使用 - 监听窗口改变


    辅助服务创建步骤及使用流程

    1.创建自定义AccessibilityService,核心代码如下:

    /**
     * @ClassName: MAccessibilityService
     * @Description: 辅助服务
     * @Author wangnan7
     * @Date: 2018/4/1
     */
    
    public class MAccessibilityService extends AccessibilityService {
    
        ......
    
        /**
         * 服务连接完成
         */
        @Override
        protected void onServiceConnected() {
            // 添加窗口
            mWindowViewContainer = WindowViewContainer.getInstance(this);
            mWindowViewContainer.addWindowView();
            ......
        }
    
        ......
    
        /**
         * 接收辅助服务事件
         */
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            if (event == null) {
                return;
            }
            switch (event.getEventType()) {
                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: // 窗口状态改变
                    if (event.getPackageName() != null && event.getClassName() != null) {
                        // 更新窗口视图
                        mWindowViewContainer.updateWindowView(event.getPackageName() + "\n" + event.getClassName());
                    }
                    break;
                default:
                    break;
            }
        }
    
        /**
         * 服务中断
         */
        @Override
        public void onInterrupt() {
        }
    
    
        /**
         * 服务退出
         */
        @Override
        public void onDestroy() {
            // 移除窗口,销毁视图容器
            mWindowViewContainer.destory();
            ......
        }
    }
    

    完整代码:MAccessibilityService.java

    整个功能的核心都在onAccessibilityEvent(AccessibilityEvent event)这个方法中,如果开启辅助服务,只要服务不死,进程不挂,当前窗口的每次改变我们都能监听到。(如果你查看了完整代码,会看到笔者添加了一个Notification,把辅助服务提升为了前台服务,这么做只是简单的做下服务保活)


    2.在AndroidManifest.xml文件中声明该服务, 核心代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.wangnan.currentactivity">
    
        ......
    
        <application
            
            ......
    
            <!-- 辅助服务 -->
            <service
                android:name=".service.MAccessibilityService"
                android:label="当前Activity(辅助工具)"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService" />
                </intent-filter>
                <meta-data
                    android:name="android.accessibilityservice"
                    android:resource="@xml/accessibility"/>
            </service>
            ......
        </application>
    
    </manifest>
    

    完整代码:AndroidManifest.xml

    详细说下辅助服务的配置声明:

    1.android:name 用于指定服务名称,前面加“.”表示书写时省略掉了包名。

    2.android:lable 用于指定外部显示的服务名称。

    3.android:permission 表明服务向系统请求那些权限

    4.intent-filteraction 表明传递给该服务的哪些Intent需要过滤出来进行处理

    可能会有人疑问为什么要配置3和4,其实这是源码文档要求的,是必须配置的,如下图所示:

    source_code.png

    5.meta-data 元数据,用于配置辅助服务的详情信息

    其实这一项在manifest文件中是可配可不配的,只是文档支持在manifest文件里进行配置,如果不在这配置就需要到辅助服务的Java代码里去配置了,源码解释如下:

    source_code2.png

    接下来,看下笔者的元数据配置文件(android:resource="@xml/accessibility")

    完整代码:accessibility.xml

    元数据配置文件说明,如下图所示:

    source_code3.png

    3.开启辅助服务

    开启辅助服务不能用startService()这种方式打开,因为使用辅助服务是有一定风险的,需要用户主动授权(同意开启)

    辅助服务打开流程:

    安装APP(含有辅助服务) -> 辅助功能 -> 服务(点击需要开启的服务) -> 开启

    以笔者的手机(360N5)和 CurrentActivity 为例,打开流程如下图所示:

    image_open.png

    4.关闭辅助服务

    辅助功能 -> 服务(点击需要关闭的服务) -> 关闭

    以笔者的手机(360N5)和 CurrentActivity 为例,关闭流程如下图所示:

    image_close.png

    CurrentActivity的详细使用方法和源码请参看Github:

    https://github.com/sinawangnan7/CurrentActivity

    其他

    1.AccessibilityService位于在系统设置里,有些手机翻译成“辅助服务”,有些手机翻译成“无障碍”

    2.AccessibilityService被用户授权开启后并不是一直保活的,也有可能被系统销毁。

    题外话

    辅助服务其实还有很多使用场景。比如,从上图笔者手机应用列表里看到的云服务360手机助手(下载自动安装)、京东金融(手环社交软件提醒)。另外,还有我们经常听说的微信自动抢红包微信自动回复...各位如果有兴趣可以研究下。Good Luck~

    最后的福利(2018年4月16号添加):
    写完这篇博客后,笔者又研究了下微信抢红包原理,写了一个【微信自动抢红包】插件,原理也是使用辅助服务,开源项目地址:
    https://github.com/sinawangnan7/WXGiftMoney
    欢迎Star...(๑•̀ㅂ•́)و✧

    相关文章

      网友评论

      • markRao:Activity包了一层??? 求解,不太明白啥意思
        水煮米茶:支付时的页面都是一个Activity, 包括键盘/输入页
        梦想编织者灬小楠:一开始看到支付宝的支付控件UI上方是半透明的,以为是一个Dialog,后来才知道实际上是一个Activity。:grin:

      本文标题:Android进阶 - 监视活动窗口

      本文链接:https://www.haomeiwen.com/subject/ftsdbxtx.html