UiAutomator注入和查找事件源码浅析

今天浅析的一个自动化框架是UiAutomator这个框架,那么从网上下了源码,就来分析一下数据流的走向吧,主要内容有两个,第一个是如果查找控件,第二个是事件是如何注入的。本人水平有限,所以只能是个浅析了。

首先我们要有一个概念,UiAutomator这个框架最终是利用了Android中的辅助功能服务,accessibilityservice(对于4.3以前的则是用了InputManager注入事件),为什么要用这个呢,参考了官方文档,总结了一下:

  • 不会为控制服务的生命周期提供钩子函数,安全
  • 不会暴露一些直接用来用户界面测试自动化的APIs
  • 可以跨应用,不像Instrumentation只能在一个进程内
  • Accessibilityservice获取控件树速度较快,只需要调用一次

那么accessibilityservice是个什么呢,原来这是个辅助服务,可以直接获取到用户到界面上的一些操作,并且这个服务是用户手动在辅助功能开启的,不支持通过startService方式直接启动服务(抢红包插件,原理可以利用这个辅助服务获取notification信息,然后自动点击,获取控件树最后自动抢红包,打算后期自己实现这个小软件)
Accessibilityservice使用方法不多说,但是要记住的是继承Accessibilityservice这个类的服务重写的方法里面有一个方法onAccessibilityEvent,当屏幕发生某些事件的时候会发送AccessibilityEvent,然后这个方法会被不断被回调,也就可以做相应的处理了,比如触发通知栏事件并匹配红包文字然后就可以一键抢红包了。
好了,接下来进入正文吧。

事件注入:

安卓事件分为TouchEvent和KeyEvent,其他的无外乎都是这两个的衍生而已,而UiAutomator对这两个事件最后都会调用一个injectEventSync()函数,然后跑到UiAutomation类用并用了远程通信把要触发的事件给到accessibilityservice服务中。

这里以UiDevice的click方法为例子追踪一下数据流:
UiAutomator注入和查找事件源码浅析_第1张图片
Return方法的调用是这样的:
InstrumentationUiAutomatorBridge—>InteractionController
InstrumentationUiAutomatorBridge继承自UiAutomatorBridge(我理解这个类是大总管),这个类负责获取一些系统信息,比如屏幕是否点亮,旋转方向,长宽高等…
InteractionController这个是封装了一些注入事件
接下来看下InteractionController的clickNoSync这个方法,这个方法实际是触发了down和up事件,对应事件如下
这里写图片描述
然后把事件封装成MotionEvent最后传入injectEventSync方法(终于inject了),那么这个inject通过UiAutomatorBridge最终传到UiAutomation当中了。
UiAutomator注入和查找事件源码浅析_第2张图片
再之后就是定义的AIDL实现远程通信了,所以inject注入到UiAutomation就结束了,之后和accessibilityservice怎么办我也不知道了。

总结:UiDevice>InstrumentationUiAutomatorBridge(UiAutomatorBridge子类)>
InteractionController>UiAutomatorBridge>UiAutomation
所以事件注入最终都是走向UiAutomation这个google4.3新增加的框架上的,框架还定义了一个AIDL接口,用来和accessibilityservice联系,那么注入就分析到这里吧,下面看看查找控件。

控件查找:

先看看我们是怎么查找控件的
这里写图片描述
恩,从搜索我的名字开始。所以这里讲的是UiSelector这个对象是怎么走的,也就知道了控件是怎么查找的了。
围绕上面的语句,new UiSelector()返回的是UiSelector对象,包含某个控件的筛选条件,传入到UiObject的构造方法中,并把UiSelector赋值给成员变量
这里写图片描述
然后定义了get方法取到UiSelector对象
这里写图片描述
这语句到这里就结束了,并没有发现什么查找控件的地方,但是我们知道不定位控件怎么操作啊,怎么做自动化啊。于是想了想,会不会是在对这个UiObject做操作才会查找呢?我们来个最简单的点击事件click,如下图
UiAutomator注入和查找事件源码浅析_第3张图片
通过取得对应某个节点AccessibilityNodeInfo对象(第二个参数传入10s超时时间,也就是找不到控件10s后就报错了或者触发了UiWatch),得到所需要的控件info,然后再转换为对应的x和y坐标,最后通过UiAutomation inject对应的坐标点,这样一个流程简单就走完了。所以重点就在于是如何拿到对应条件的AccessibilityNodeInfo对象。也就是findAccessibilityNodeInfo这个方法做了什么。
UiAutomator注入和查找事件源码浅析_第4张图片
当然,这个findAccessibilityNodeInfo方法也是定义在UiObject里面的,并把Uiselector对象传入。实际上调用的是QueryController这个类的findAccessibilityNodeInfo方法,QueryController这玩意就是专门查找控件的一个类。
UiAutomator注入和查找事件源码浅析_第5张图片
在这个方法中获取了根视图rootNode,之后调用translateCompoundSelector方法实现从根视图查找需要的控件,并最终返回单个控件信息。由于水平原因,translateCompoundSelector方法里面的算法就没分析了,只知道传入UiSelector和rootNode进去,从根节点开始查找所需的控件。好了,查找控件就分析完了。

总结:生成UiObject是定义,只有对object操作才会去正真查找并获取视图,获取需要的控件返回后,通过注入事件去实现自动化。
UiObject>QueryController

写在最后:
那么注入和查找就分析完了,因为是第一次分析框架,还是有很多不懂的,慢慢来吧。

你可能感兴趣的:(android,框架,自动化)