自动抢红包,自动安装原理之AccessibilityService

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前段时间看别人博客的时候偶然间看到了Android微信自动回复功能,最后的效果也很不错,博主在文中提到了<code>AccessibilityService</code>,以前压根没接触过这东西,表示一脸懵逼。也是这个原因我去找了AccessibilityService相关的资料好好的看了一遍,发现这个东西真的太NB了,网上对AccessibilityService的应用还是有不少的文章的,但是详细的介绍资料还是比较少,对于刚刚学习这个的同学看完很多资料还是一脸茫然,于是才有了本文。我相信当你在看完本文之后,再去看前面的那篇文章,我相信会轻松很多。


本文学习目录

1.AccessibilityService是什么
2.AccessibilityService的创建与配置
3.怎么去使用AccessibilityService
4.一步一步构建一个apk自动安装器

1.AccessibilityService是什么

AccessibilityService是什么,官网是这样解释的

Accessibility services are intended to assist users with disabilities in using Android devices and apps. They run in the background and receive callbacks by the system whenAccessibilityEvents are fired.

也就是说这是个辅助功能,目的是辅助人们去使用Android设备和应用。它在后台运行,可以接收系统的回调。

可见AccessibilityService的出现Google的初衷是辅助人们去使用Android设备和应用,但是当你对它足够了解了之后你会发现它的作用不仅仅只是这样。对windows编程有了解的同学肯定知道hook(钩子),AccessibilityService和windows下的hook有那么一点的相似。AccessibilityService可以拦截到系统发出的一些消息(比如窗体状态的改变,通知栏状态的改变,View被点击了等等),当拦截到这些事件我们就可以去做一些我们想做的事儿了~~~
AccessibilityService能做些什么呢? 比如自动化测试、自动抢红包、自动安装等等。在带来便利的同时,也还是有需要注意的地方:当你开启了辅助功能之后会对你的隐私信息带来一些风险,所以还是需要谨慎的去开启第三方的辅助功能。当然这对于我们开发者而言都不是事,因为我们完全可以自己去开发属于我们的辅助功能。

2.AccessibilityService的创建与配置

第一步就是自己去创建一个类继承于AccessibilityService,并实现必须实现的两个方法

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {

    }

    @Override
    public void onInterrupt() {

    }
}

随后就是在res/xml目录下新建一个xml文件(文件名随意),后面会对这个文件作详细的介绍。

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"  
    android:canRetrieveWindowContent="true"
    android:description="@string/my_accessibility_description"
    android:notificationTimeout="100"
    android:packageNames="com.tencent.mobileqq,com.android.packageinstaller" />

最后就是去配置清单文件了

 <service
            android:name=".service.MyAccessibilityService"
            android:enabled="true"
            android:exported="true"
            android:label="@string/app_name"
            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/my_services_config" />//在meta-data里申明配置信息
        </service>

别忘记了添加权限

<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />

到这我们就可以把应用跑到我们的手机上了,然后打开辅助功能,去开启我们刚刚创建的这个辅助功能。如下图:


当然因为什么都没做,开启了也没什么用,下面就去看看怎么使用吧!

3.怎么去使用AccessibilityService

先说说之前创建的那个xml文件中各个属性的含义吧

  • <code>accessibilityEventTypes </code>: 用来设置响应事件的类型,比如typeAllMask就是响应全部事件,typeNotificationStateChanged就是响应通知状态的改变,如果需要响应多种事件类型可以以 ‘ | ’ 隔开。
  • <code>accessibilityFeedbackType </code>: 给用户的反馈方式,比如语音、震动等,这里用处不大。
  • <code>canRetrieveWindowContent</code> :是否可以获取活动窗体的内容,这个设置为true才可以取得窗体中的控件和事件源
  • <code>description</code> :辅助功能的描述
  • <code>notificationTimeout</code> :两个相同类型事件发送到服务的事件间隔,单位毫秒
  • <code>packageNames</code> :指定响应某个应用的事件,取值为应用的包名,多个以‘ , ’ 隔开。没有此属性则表示响应全部应用。这里我填写的是手机qq和系统安装器的包名

然后进入到我们的<code>MyAccessibilityService </code>中,定位到<code>onAccessibilityEvent</code>方法编写如下代码

log("-------------------------------------------------------------");
        int eventType = event.getEventType();//事件类型
        log("packageName:" + event.getPackageName() + "");//响应事件的包名,也就是哪个应用才响应了这个事件
        log("source:" + event.getSource() + "");//事件源信息
        log("source class:" + event.getClassName() + "");//事件源的类名,比如android.widget.TextView
        log("event type(int):" + eventType + "");

        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知栏事件
                log("event type:TYPE_NOTIFICATION_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED://窗体状态改变
                log("event type:TYPE_WINDOW_STATE_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED://View获取到焦点
                log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
                log("event type:TYPE_VIEW_ACCESSIBILITY_FOCUSED");
                break;
            case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
                log("event type:TYPE_GESTURE_DETECTION_END");
                break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                log("event type:TYPE_WINDOW_CONTENT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                log("event type:TYPE_VIEW_CLICKED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                log("event type:TYPE_VIEW_TEXT_CHANGED");
                break;
            case AccessibilityEvent.TYPE_VIEW_SCROLLED:
                log("event type:TYPE_VIEW_SCROLLED");
                break;
            case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED:
                log("event type:TYPE_VIEW_TEXT_SELECTION_CHANGED");
                break;
        }

        for (CharSequence txt : event.getText()) {
            log("text:" + txt);//输出当前事件包含的文本信息
        }

        log("-------------------------------------------------------------");

log方法就是打印信息用的,是对Log的一个简单封装。

在运行一次程序,打开我们的手机qq可以看见输出日志如下

可以很清晰的看见当我们打开qq或触发很多的事件,第一个响应的事件是<code>TYPE_WINDOW_STATE_CHANGED</code>,触发此事件的事件源是<code>com.tencent.mobileqq.activity.SplashActivity</code>

接着,当我点击最上面的 ‘ 电话 ’,会得到如下日志:

我就不一一截图操作的日志了,下来大家可以自行尝试。到这里你用该对<code>onAccessibilityEvent</code>有了进一步的了解。

4.一步一步构建一个apk自动安装器

这一节来个实战的应用:做一个apk的自动安装器,点击apk文件即可开始自动安装。

国内的rom厂商大家都很清楚:百(sang)花(xin)齐(bing)放(kuang),已经把原生的rom改的面目全非。所以想做一个面对全部手机的apk的自动安装器还是比较麻烦的。我的手机是魅族的,下图是魅族手机apk安装的步骤(其他手机可能会略有不同,下来自己更改不同部分即可,这里主要讲解原理):


可以看见点击安装包之后会弹出一个确认框,点击继续之后会出现下一步,点击了下一步就可以点击安装了。我们要实现自动安装无非就是用程序的方式会对相关按钮的自动点击,难点就在于怎么去找到这些按钮并执行点击操作。

好了,下面我们可以来创建一个自动安装的服务了,步骤和第一节描述的一样将<code>packageNames</code>指定成<code>com.android.packageinstaller</code>就可以了。这里我们先别着急去实现功能(就算你想去实现,也摸不着头脑),我们还是像第二节那样去输出日志信息,查看日志输出信息(对响应事件信息的打印)是使用AccessibilityService的重点,看完日志的输出信息,然后对其分析,才会知道具体的操作。
我们来看看在安装apk文件时候输出的部分日志信息:

从日志信息中可以看出:
1.当我们点击了apk文件的时候,如果该文件已经存在,就会弹出一个对话框并且会响应<code>TYPE_WINDOW_STATE_CHANGE</code>事件
2.当我们点击了继续之后就会响应<code>TYPE_VIEW_CLICKED</code>事件,并且继续这个可以点击的View是个Button
3.接着点击下一步,同样也会响应<code>TYPE_VIEW_CLICKED</code>事件,并且继续这个可以点击的View是个TextView。最后就可以点击安装了(安装那步忘了截图,和第3步相应的事件是一样的)

经过上面几步的分析,我想你应该对后面的逻辑还是比较清楚了,直接上代码


public class AutoInstallService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        PrintUtils.printEvent(event);
        findAndPerformActionButton("继续");
        findAndPerformActionTextView("下一步");
        findAndPerformActionTextView("安装");
    }


    private void findAndPerformActionButton(String text) {
        if (getRootInActiveWindow() == null)//取得当前激活窗体的根节点
            return;
        //通过文字找到当前的节点
        List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
        for (int i = 0; i < nodes.size(); i++) {
            AccessibilityNodeInfo node = nodes.get(i);
            // 执行点击行为
            if (node.getClassName().equals("android.widget.Button") && node.isEnabled()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    private void findAndPerformActionTextView(String text) {
        if (getRootInActiveWindow() == null)
            return;
        //通过文字找到当前的节点
        List<AccessibilityNodeInfo> nodes = getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
        for (int i = 0; i < nodes.size(); i++) {
            AccessibilityNodeInfo node = nodes.get(i);
            // 执行按钮点击行为
            if (node.getClassName().equals("android.widget.TextView") && node.isEnabled()) {
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }
}

例子很简单,所以也没去对eventType做判断,当有其他需求的时候是需要对eventType做判断了,代码已经注释的比较详细,我就不再对代码作更多的讲解了,有疑问可以留言。

到这里你应该对AccessibilityService有了进一步的认识,会发现原来红包助手之类软件实现的原理就是利用AccessibilityService检测通知栏的状态,然后再去做一些处理。是不是有点自己去做一个红包助手的想法了?现在再去看看文章开始的那篇文章你的收获会更大:Android微信自动回复功能

补充说明

AccessibilityService中还有几个常用的方法 onServiceConnected、onInterrupt、onGesture。看名字大致也知道什么时候会被调用。

在onServiceConnected中可以去配置AccessibilityService的一些信息,也就是之前在xml文件可以在这里通过代码配置,不过这里没法配置canRetrieveWindowContent属性,刚开始也被这个坑了很久

@Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        PrintUtils.log("onServiceConnected");
//        //可用代码配置当前Service的信息
//        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
//        info.packageNames = new String[]{"com.android.packageinstaller", "com.tencent.mobileqq", "com.trs.gygdapp"}; //监听过滤的包名
//        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; //监听哪些行为
//        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; //反馈
//        info.notificationTimeout = 100; //通知的时间
//        setServiceInfo(info);
    }

官方文档

本文源码

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,800评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,773评论 6 342
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 春秋流转 流年共谁度? 茗一盏清茶 听一耳风雨 以思念下酒 以岁月入味 我在这里 等你带一场梦来 浮世离欢 早已沉...
    弘慧阅读 301评论 0 0