从Appium扯到了UiAutomator再扯到AccessibilityService服务,好像有点扯远了。而为什么要扯到AccessibilityService服务,是由于在几乎所有资料中都有一个方向就是UiAutomator基于AccessibilityService服务,而到了UiAutomator2.0又基于Instrumentation,有兴趣看到这篇文章的朋友可以注意下,在有UiAutomator涉及的文章中很多都会提到,由于不解,所以需要去理解一下AccessibilityService与Instrumentation,而学习思路是从UiAutomator1.0开始,所以涉及UiAutomator2.0的Instrumentation到后面再说,本文先说AccessibilityService服务。
一、什么是AccessibilityService服务
辅助功能(AccessibilityService)其实是一个Android系统提供的一种服务,它是继承Service类的。这个服务提供了增强的用户界面,旨在帮助残障人士或者可能暂时无法与设备充分交互的人们。从概念来看懵懵懂懂,接下来看实操。
二、AccessibilityService服务使用
AccessibilityService服务是android服务,所以要在android项目中使用,前面文章一直在使用Eclipse写代码运行,接下来放弃Eclipse开始在Google Android强烈推荐的Android Studio IDE中写代码运行。
1.新建Android项目
简单的写了几个控件,便于后面的AccessibilityService服务操作,布局文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请选择:"/> <CheckBox android:id="@+id/select" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请输入:"/> <EditText android:id="@+id/insert" android:layout_width="300dp" android:layout_height="wrap_content" /> </LinearLayout> <Button android:id="@+id/clickButton" android:layout_width="400dp" android:layout_height="wrap_content" android:text="Test"/> </LinearLayout>
2.新建AccessibilityServiceSample类
创建一个类AccessibilityServiceSample,继承AccessibilityService,然后复写AccessibilityService的几个方法,如下:
public class AccessibilityServiceSample extends AccessibilityService { @Override public void onServiceConnected(){ super.onServiceConnected(); } @Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { } @Override public void onInterrupt() { } }
(1)onServiceConnected
系统成功连接到辅助功能服务时调用,可以执行任何一次性设置步骤,包括连接到用户反馈系统服务,如音频管理器或设备振动器。还可以调用setServiceInfo()设置服务配置。
(2)onAccessibilityEvent
当系统检测到与Accessibility服务指定的事件过滤参数匹配的AccessibilityEvent时调用。这是必须实现的方法,通常需要在该方法中根据AccessibilityEvent作出判断并执行一些处理。
(3)onInterrupt
当系统想要中断服务提供的反馈时调用,通常是响应用户操作,如将焦点移动到其他控件。
(4)onUnbind与onDestroy
当系统即将关闭辅助功能服务时调用,可以执行任何一次性关机程序,包括取消分配用户反馈系统服务,例如音频管理器或设备振动器。
3.新建AccessibilityService服务配置文件
在res目录下新建Android Resource Directory文件夹
名字跟类型输入或选择xml,点击OK后在res下面生成xml文件夹。
然后在选中xml文件夹,新建xml文件,自定义名字accessibility_service_config,内容如下:
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:packageNames="com.main" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagDefault" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:canRetrieveWindowContent="true" />
(1)android:packageNames
指定监听应用程序事件的包名,可指定多个用逗号分隔,不指定则监听所有应用产生的事件。
(2)android:accessibilityEventTypes
服务要监控的事件类型,如通知、窗口改变、点击、焦点改变等等,如果有多个可以用 | 连起来,typeAllMask代表所有类型。
(3)android:accessibilityFeedbackType
服务反馈的方式,如语音、震动等等,feedbackAllMask代表所有类型。
(4)android:notificationTimeout
接受事件的通知超时时间(毫秒)
(5)android:canRetrieveWindowContent
服务能否获取窗口里面的内容,这里必须要设置true,否则后面没办法获取界面内容
这些配置除了在xml里面写之外,还可以在代码中建立一个AccessibilityServiceInfo对象,然后通过setServiceInfo()来设置,下面再说。
4.在AndroidManifest.xml中声明AccessibilityService服务
打开AndroidManifest.xml文件,在application标签里面生命AccessibilityService服务的使用,代码如下:
<service android:name=".AccessibilityServiceSample"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/accessibility_service_config" /> </service>
(1)android:name
指定AccessibilityService服务的子类,也就是继承了AccessibilityService的类。
(2)android:label
标签表示服务的名字,应用安装后会在手机辅助功能的列表中显示,若没定义标签则不显示。如文中用的是项目名,当安装这个APK后,在手机辅助功能中可以看到该服务。
(3)android:permission
添加系统权限,一定不要漏了这句话,否则android系统会忽略监听
(4)intent-filter标签组添加过滤器,与上面一样,必须添加
(5)meta-data指定声明AccessibilityService服务配置的配置文件位置。
5.在代码中定义AccessibilityService服务配置
回到AccessibilityServiceSample类中,在上面说可以在代码中通过setServiceInfo()方法设置AccessibilityService服务,就是在onServiceConnected()方法中定义配置,配置的内容与上面xml配置是一样的;2种方法配置任选其一。
@Override public void onServiceConnected(){ AccessibilityServiceInfo asinfo = getServiceInfo(); asinfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; asinfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; asinfo.notificationTimeout = 100; asinfo.packageNames = new String[]{"com.main"}; setServiceInfo(asinfo); super.onServiceConnected(); }
6.onAccessibilityEvent()方法
回到AccessibilityServiceSample类中,在配置中设置的是监控全部事件,所以当系统监控到事件时,会调用onAccessibilityEvent()方法。
@Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if(nodeInfo != null && "com.main.accessibilityservice".equals(nodeInfo.getPackageName().toString())) { List<AccessibilityNodeInfo> nodes = nodeInfo.findAccessibilityNodeInfosByViewId("com.main.accessibilityservice:id/select"); List<AccessibilityNodeInfo> nodesInsert = nodeInfo.findAccessibilityNodeInfosByViewId("com.main.accessibilityservice:id/insert"); //回收ondeInfo,避免重复创建 nodeInfo.recycle(); //点击操作 AccessibilityNodeInfo checkinfo = nodes.get(0); if(!checkinfo.isChecked()){ checkinfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); checkinfo.recycle(); } //文本输入内容 Bundle arguments = new Bundle(); arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "测试"); nodesInsert.get(0).performAction(AccessibilityNodeInfo.ACTION_SET_TEXT,arguments); } }
(1)AccessibilityEvent
服务监控的事件对象,对象包含很多信息,包含监控的事件所在的包名、事件类型、事件发生时间、事件类名等等信息
(2)AccessibilityNodeInfo
窗口内容对象,通过getRootInActiveWindow()方法获取主窗口内容
(3)findAccessibilityNodeInfosByViewId
通过id查找对象,返回的是一个List列表;当然AccessibilityNodeInfo对象还提供findAccessibilityNodeInfosByText()方法,根据文本值内容匹配得到对象。
(4)performAction
执行操作,AccessibilityNodeInfo.ACTION_CLICK表示点击操作,如果需要输入内容,需要通过Bundle对象设置输入内容。
(5)recycle
recycle方法表示回收AccessibilityNodeInfo对象
上面代码主要是先判断当前应该包名,如果匹配则进入开始执行点击、输入操作
7.打包Apk安装
打包过程这里不做叙述,通过加密打包APK后安装在手机设备中,安装完成后,在设置中找到辅助功能
并把服务开启,开启之后,打开监控安装好的APK应用,就会自动进入onAccessibilityEvent方法,执行方法中的代码。这样一个简单的AccessibilityService过程演示完毕。
从过程中可以很清晰的知道AccessibilityService是一种服务监控行为的操作,当进入服务监控到事件后进入onAccessibilityEvent方法运行,达到辅助完成的效果。而现实中也很多应用使用AccessibilityService辅助功能完成的,比如很流行的微信自动抢红包,就是通过AccessibilityService服务监控完成的。虽然整个AccessibilityService简单使用说过了,但是并没有看到有关UiAutomator任何基于AccessibilityService的一点点信息,那UiAutomator基于AccessibilityService的说法成立吗?下一篇继续说。