热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Accessibilityservice学习

accessibilityservice是android上用来做辅助功能的一个API,主要用来相应用户发送AccessibilityEvent的事件,具

accessibilityservice是android上用来做辅助功能的一个API,主要用来相应用户发送AccessibilityEvent的事件,具体可以google或者baidu一下,不过只支持Android 4.1之后的手机这里我写下具体的实现流程。



实现步骤

如果我们自己写一个基于Accessibility的应用,主要分为下面几步:


编写服务类


  • 编写自己的服务类,需要继承自AccessibilityService
    MyPocketService.java

public class MyPocketService extends AccessibilityService {private static final String TAG = "POCKET";@Overrideprotected void onServiceConnected() {super.onServiceConnected();}public MyPocketService() {}@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {int eventType = event.getEventType();
// Log.d(TAG,"the eventType is :"+eventType);// AccessibilityNodeInfo accessibilityNodeInfo = event.getSource(); //得到被点击的对象// getRootInActiveWindow(); 获得整个窗口对象if (eventType== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { //当当前窗口发生变化时候,重新遍历,否则后面的控件找不到AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow();if (rootInActiveWindow == null) {Log.d(TAG,"the rootInActiveWindow is null");} else {recycle(rootInActiveWindow);}}}public void recycle(AccessibilityNodeInfo rootInActiveWindow) {//只有一个子元素if (rootInActiveWindow.getChildCount() == 0) {Log.d(TAG,"the rootInActiveWindow count is : 0");} else {// 遍历所有子元素for (int i = 0; i "the child "+i+"is :"+nodeInfo+"\n");}}}@Overridepublic void onInterrupt() {}
}

上面的MyPocketService主要实现了下面的一些方法:

//响应AccessibilityEvent的事件
public void onAccessibilityEvent(AccessibilityEvent event)// 打断获取事件的过程,可以用来关闭资源
public void onInterrupt() // 用来绑定服务的方法,可以做一些关于AccessibilityServiceInfo 的初始配置
protected void onServiceConnected()

注册当前Service

在manifest里注册当前Service

<service android:name&#61;".MyPocketService"android:permission&#61;"android.permission.BIND_ACCESSIBILITY_SERVICE"android:label&#61;"get pocket"><intent-filter><action android:name&#61;"android.accessibilityservice.AccessibilityService" />intent-filter><meta-data android:name&#61;"android.accessibilityservice" android:resource&#61;"&#64;xml/accessibilityservice" />service>

上面的写法基本上是固定的。必须添加权限android.permission.BIND_ACCESSIBILITY_SERVICE, 另外action的name也必须是android.accessibilityservice.AccessibilityService ,这里通过meta-data配置accessibility-service需要相应的事件以及其他属性。


添加配置文件

这里的配置文件名称需要和meta-data中配置的一样&#xff0c;在res/xml中新建一个accessibilityservice.xml&#xff0c;如下&#xff1a;


<accessibility-service xmlns:android&#61;"http://schemas.android.com/apk/res/android"android:description&#61;"&#64;string/pocket_description"android:accessibilityEventTypes&#61;"typeAllMask"android:packageNames&#61;"com.android.settings"android:accessibilityFeedbackType&#61;"feedbackGeneric"android:notificationTimeout&#61;"100"android:accessibilityFlags&#61;"flagDefault"android:canRetrieveWindowContent&#61;"true"android:canRequestTouchExplorationMode&#61;"true"android:canRequestEnhancedWebAccessibility&#61;"true"/>

上面的属性简单说明一下&#xff1a;


  • packageNames&#xff1a; 指定要监听的应用的包名
  • description&#xff1a; 这里的description是显示给用户看的提示语&#xff0c;即打开服务的时候的提示
  • accessibilityEventTypes
    指定在监听窗口中可以模拟那些事件&#xff0c;这里我填写的是typeAllMask表示所有事件&#xff0c;有如下事件&#xff1a;

/*** Mask for {&#64;link AccessibilityEvent} all types.** &#64;see #TYPE_VIEW_CLICKED* &#64;see #TYPE_VIEW_LONG_CLICKED* &#64;see #TYPE_VIEW_SELECTED* &#64;see #TYPE_VIEW_FOCUSED* &#64;see #TYPE_VIEW_TEXT_CHANGED* &#64;see #TYPE_WINDOW_STATE_CHANGED* &#64;see #TYPE_NOTIFICATION_STATE_CHANGED* &#64;see #TYPE_VIEW_HOVER_ENTER* &#64;see #TYPE_VIEW_HOVER_EXIT* &#64;see #TYPE_TOUCH_EXPLORATION_GESTURE_START* &#64;see #TYPE_TOUCH_EXPLORATION_GESTURE_END* &#64;see #TYPE_WINDOW_CONTENT_CHANGED* &#64;see #TYPE_VIEW_SCROLLED* &#64;see #TYPE_VIEW_TEXT_SELECTION_CHANGED* &#64;see #TYPE_ANNOUNCEMENT* &#64;see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY* &#64;see #TYPE_GESTURE_DETECTION_START* &#64;see #TYPE_GESTURE_DETECTION_END* &#64;see #TYPE_TOUCH_INTERACTION_START* &#64;see #TYPE_TOUCH_INTERACTION_END* &#64;see #TYPE_WINDOWS_CHANGED* &#64;see #TYPE_VIEW_CONTEXT_CLICKED*/public static final int TYPES_ALL_MASK &#61; 0xFFFFFFFF;

  • accessibilityFlags&#xff1a; 可以指定一些附加参数
  • canRetrieveWindowContent&#xff1a; 是否允许当前程序读取窗口节点的内容
  • notificationTimeout&#xff1a; 设置响应时间

这里记录一下小插曲&#xff0c;Android studio中设置快捷键提示&#xff1a;


ctrl&#43;alt&#43;s
在左侧的导航框中点击 KeyMap。
接着在右边的树型框中选择 Main menu –> Code –> Completion.
接着需要做两件事&#xff1a;
1. 移除原来的Cycle Expand Word 的 Alt&#43;斜杠 快捷键绑定。
2. 在 Basic 上点击右键,去除原来的 Ctrl&#43;空格 绑定,然后添加 Alt &#43; 斜杠 快捷键。


此时运行我们的程序&#xff0c;在setting下打开辅助功能&#xff0c;找到我们的程序&#xff0c;允许其使用辅助功能。
这里写图片描述
允许使用辅助功能之后&#xff0c;此时运行程序&#xff0c;由于我们上面监听的是setting应用&#xff0c;所以此时打开setting&#xff0c;控制台就会打印出当前AccessibilityNodeInfo的信息&#xff0c;如下&#xff1a;
这里写图片描述


使用Accessibility实现静默安装

可以看到360和UC都是使用Accessibility来实现的自动安装功能&#xff0c;我们也来模仿实现一个自动安装的功能&#xff0c;在正式开始之前&#xff0c;先看下AccessibilityNodeInfo主要的方法&#xff0c;AccessibilityNodeInfo就是界面当中的每一个节点。

AccessibilityNodeInfo rootInActiveWindow
rootInActiveWindow.getChildCount(); //获得子元素的个数
rootInActiveWindow.getChild(0); //根据索引获取子元素
rootInActiveWindow.getText(); // 获得当前元素的文本内容
rootInActiveWindow.canOpenPopup(); //是否可以打开dialog
rootInActiveWindow.describeContents();rootInActiveWindow.findAccessibilityNodeInfosByText(); //根据文字内容获得当前AccessibilityNodeInfo对象
rootInActiveWindow.getClassName();
rootInActiveWindow.getPackageName();rootInActiveWindow.findAccessibilityNodeInfosByViewId(); // 根据view的id获取当前AccessibilityNodeInfo对象

这里由于我们监听的是packageinstall的应用&#xff0c;因此需要将配置文件进行更改如下&#xff1a;

android:packageNames&#61;"com.android.packageinstaller"

先来看下点击install的时候&#xff0c;使用UIAutomator找到需要点击按钮的id&#xff0c;如下图&#xff1a;
这里写图片描述
ok,先关id找到以后&#xff0c;开始实现啦&#xff1a;首先要做的就是使当前界面跳转到安装应用的界面&#xff0c;使用下面的代码即可&#xff1a;

//APK_PATH为当前apk的全路径
Uri uri &#61; Uri.fromFile(new File(APK_PATH));
Intent localIntent &#61; new Intent(Intent.ACTION_VIEW);
localIntent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(localIntent);

当当前界面显示的是“com.android.packageinstaller”包所在的界面&#xff0c;此时就会回调onAccessibilityEvent方法&#xff0c;我们在该方法里实现自动点击需要点击的button

if (eventType&#61;&#61; AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||eventType &#61;&#61; AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { //当当前窗口发生变化时候&#xff0c;重新遍历&#xff0c;否则后面的控件找不到AccessibilityNodeInfo rootInActiveWindow &#61; getRootInActiveWindow();if (rootInActiveWindow &#61;&#61; null) {Log.d(TAG,"the rootInActiveWindow is null");} else {recycle(rootInActiveWindow);}}

可以看到上面的代码主要使用recycle方法来不断遍历当前界面的view找到需要点击的button&#xff0c;在进行模拟人为点击&#xff0c;recycle方法如下&#xff1a;

public void recycle(AccessibilityNodeInfo rootInActiveWindow) {if (rootInActiveWindow.getChildCount() &#61;&#61; 0) { //只有一个子元素Log.d(TAG,"the rootInActiveWindow count is : 0");} else {for (int i &#61; 0; i < rootInActiveWindow.getChildCount(); i&#43;&#43;) {AccessibilityNodeInfo nodeInfo &#61; rootInActiveWindow.getChild(i);// 这里根据UIAutomator来判断&#xff0c;我们当前需要点击的是buttonif (nodeInfo.getClassName().equals("android.widget.Button")) {String content &#61; nodeInfo.getText().toString();List<AccessibilityNodeInfo> okList &#61; nodeInfo.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/ok_button");List<AccessibilityNodeInfo> openList &#61; nodeInfo.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/launch_button");//if ((null !&#61; okList && okList.size() > 0)) {Log.d(TAG, "the node content is :" &#43; content &#43; "&#61;&#61;the okbutton is :" &#43; okList.get(0));// 通过performAction来实现模拟点击事件okList.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);}// 安装完成之后点击open按钮&#xff0c;打开当前应用if (null !&#61; openList && openList.size() > 0) {Log.d(TAG, "the node content is :" &#43; content &#43; "&#61;&#61;the openButton is :" &#43; openList.get(0));openList.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);}String cancelDelete &#61; nodeInfo.getText().toString();//这里的取消是我的荣耀手机上的是否删除软件安装包的&#xff0c;这里选择取消if ("取消".equals(cancelDelete)) {nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}}}

此时运行效果如下&#xff1a;
这里写图片描述

源码下载


推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
author-avatar
逍遥子2502897751
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有