Android进阶 - 监视活动窗口

摘要

在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...(๑•̀ㅂ•́)و✧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容