依据上一篇文章《UiAutomator源代码分析之注入事件》開始时提到的计划,这一篇文章我们要分析的是第二点:

  • 怎样获取控件信息
我们在測试脚本中初始化一个UiObject的时候一般是像下面这个样子:
UiObject appsTab = new UiObject(new UiSelector().text("Apps"));
appsTab.click()

那么这个过程发生了什么呢?这就是我们接下来要说的事情了。


1. 获取控件信息顺序图

这里依旧是一个手画的不规范的顺序图,描写叙述了UiObject尝试获得一个控件的过程中与相关的类的交互,这些类的关系在《UiAutomator源代码分析之UiAutomatorBridge框架》中已经进行了描写叙述。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1YmFpdGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

这里整一个过程并不复杂,简单说明下就这几点:
  • UiObject对象几经周折通过不同的类终于联系上UiAutomation,然后通知UiAutomation对象它想取得当前活动窗体的全部元素的AccessibilityNodeInfo类型的根节点
  • AccessibilityNodeInfo代表了屏幕中控件元素的一个节点。同一时候它也拥有一些成员方法能够以当前节点为基础来获得其它目标节点。

    能够把屏幕上的节点想像成是通过类似xml的格式组织起来的。所以一旦知道根节点和由选择子UiSelector指定的目标控件信息,我们就能够遍历整个窗体控件

  • QueryController对象获得Root Node之后,就是调用tranlateCompoundSelector这种方法来遍历窗体全部控件。直到找到选择子UiSelector指定的那个控件为止。
  • 注意一个AccessibilityNodeInfo仅仅代表一个控件,遍历的时候一旦需要下一个控件的信息是必需要再次通过UiAutomation去获取的。

2.触发控件查找真正发生的地方

在我没有去分析uiautomator的源码之前。我一直以为空间查找是在通过UiSelector初始化一个UiObject的时候发生的:

UiObject appsTab = new UiObject(new UiSelector().text("Apps"));

这让我有一种先入为主的感觉,一个控件对象初始化好后应该就已经得到了该控件所代表的节点的全部信息了。但看了源代码后发现事实并不是如此,以上所做的事情仅仅是以一定的格式准备好UiSelector选择子而已。真正触发uiautomator去获取控件节点信息的是在触发控件事件的时候。比方:

appsTab.click()

我们进入到代表一个控件的UiObject相应的操作控件的方法去看下就清楚了,以上面的click为例:

/*      */   public boolean click()
/* */ throws UiObjectNotFoundException
/* */ {
/* 389 */ Tracer.trace(new Object[0]);
/* 390 */ AccessibilityNodeInfo node = findAccessibilityNodeInfo(this.mConfig.getWaitForSelectorTimeout());
/* 391 */ if (node == null) {
/* 392 */ throw new UiObjectNotFoundException(getSelector().toString());
/* */ }
/* 394 */ Rect rect = getVisibleBounds(node);
/* 395 */ return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(), this.mConfig.getActionAcknowledgmentTimeout());
/* */ }

正式290行的调用触发uiautomator去调用UiAutomation去获取到我们想要的控件节点AccessibilityNodeInfo信息的。

3.获得根节点

以下我们看下uiautomator是怎么去获取到代表窗体全部控件的根的Root Node的,我们进入UiObject的findAccessibilityNodeInfo这种方法:

/*      */   protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout)
/* */ {
/* 164 */ AccessibilityNodeInfo node = null;
/* 165 */ long startMills = SystemClock.uptimeMillis();
/* 166 */ long currentMills = 0L;
/* 167 */ while (currentMills <= timeout) {
/* 168 */ node = getQueryController().findAccessibilityNodeInfo(getSelector());
/* 169 */ if (node != null) {
/* */ break;
/* */ }
/* */
/* 173 */ UiDevice.getInstance().runWatchers();
/* */
/* 175 */ currentMills = SystemClock.uptimeMillis() - startMills;
/* 176 */ if (timeout > 0L) {
/* 177 */ SystemClock.sleep(1000L);
/* */ }
/* */ }
/* 180 */ return node;
/* */ }

UiObject对象会首先去获得一个QueryController对象,然后调用该对象的findAccessibilityNodeInfo同名方法:

/*     */   protected AccessibilityNodeInfo findAccessibilityNodeInfo(UiSelector selector, boolean isCounting)
/* */ {
/* 143 */ this.mUiAutomatorBridge.waitForIdle();
/* 144 */ initializeNewSearch();
/* */
/* 146 */ if (DEBUG) {
/* 147 */ Log.d(LOG_TAG, "Searching: " + selector);
/* */ }
/* 149 */ synchronized (this.mLock) {
/* 150 */ AccessibilityNodeInfo rootNode = getRootNode();
/* 151 */ if (rootNode == null) {
/* 152 */ Log.e(LOG_TAG, "Cannot proceed when root node is null. Aborted search");
/* 153 */ return null;
/* */ }
/* */
/* */
/* 157 */ UiSelector uiSelector = new UiSelector(selector);
/* 158 */ return translateCompoundSelector(uiSelector, rootNode, isCounting);
/* */ }
/* */ }

这里做了两个重要的事情:

  • 150行:通过调用getRootNode来获得根节点,这个就是我们这个章节的重点
  • 158行:通过调用translateCompoundSelector来依据用户指定的UiSelector格式从上面获得根节点開始遍历窗体控件树,以获得我们的目标控件
好。我们继续往下进入getRootNode:
/*     */   protected AccessibilityNodeInfo getRootNode()
/* */ {
/* 168 */ int maxRetry = 4;
/* 169 */ long waitInterval = 250L;
/* 170 */ AccessibilityNodeInfo rootNode = null;
/* 171 */ for (int x = 0; x < 4; x++) {
/* 172 */ rootNode = this.mUiAutomatorBridge.getRootInActiveWindow();
/* 173 */ if (rootNode != null) {
/* 174 */ return rootNode;
/* */ }
/* 176 */ if (x < 3) {
/* 177 */ Log.e(LOG_TAG, "Got null root node from accessibility - Retrying...");
/* 178 */ SystemClock.sleep(250L);
/* */ }
/* */ }
/* 181 */ return rootNode;
/* */ }

172调用的是UiAutomatorBridge对象的方法,通过我们上面的几篇文章我们知道UiAutomatorBridge提供的方法大部分都是直接调用UiAutomation的方法的,我们进去看看是否如此:

/*     */   public AccessibilityNodeInfo getRootInActiveWindow() {
/* 66 */ return this.mUiAutomation.getRootInActiveWindow();
/* */ }

果不其然,终于简单明了的直接调用UiAutomation的getRootInActiveWindow来获得根AccessibilityNodeInfo.

4.遍历根节点获得选择子UiSelector指定的控件

如前所述,QueryController的方法findAccessibilityNodeInfo在获得根节点后下来做的第二个事情:

  • 158行:通过调用translateCompoundSelector来依据用户指定的UiSelector格式从上面获得根节点開始遍历窗体控件树,以获得我们的目标控件
里面的算法细节我就不打算去研究了,里面考虑到选择子嵌套的情况,分析起来也比較费力,且了解了它的算法对我去立交uiautomator的执行原理并没有很大的帮助,我仅仅须要知道给定一棵树的根,然后制定了我想要的叶子的属性,那么我遍历整棵树肯定是能够找到我想要的那个/些满足要求的控件的。大家由兴趣了解其算法的话还是自行去研究吧。


5.终于还是通过坐标点来点击控件

上面UiObject的Click方法通过UiAutomation这个高大上的新框架获得了代表我们目标控件的AccessibilityNodeInfo后,跟着是不是就直接调用这个节点的Click方法进行点击了呢?事实上不是的,首先AccessibilityNodeInfo并没有click这种方法,我们继续看代码:

/*      */   public boolean click()
/* */ throws UiObjectNotFoundException
/* */ {
/* 389 */ Tracer.trace(new Object[0]);
/* 390 */ AccessibilityNodeInfo node = findAccessibilityNodeInfo(this.mConfig.getWaitForSelectorTimeout());
/* 391 */ if (node == null) {
/* 392 */ throw new UiObjectNotFoundException(getSelector().toString());
/* */ }
/* 394 */ Rect rect = getVisibleBounds(node);
/* 395 */ return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(), this.mConfig.getActionAcknowledgmentTimeout());
/* */ }

从395行能够看到,终于还是把控件节点的信息转换成控件的坐标点进行点击的。至于怎么点击。大家能够參照上一篇文章。无非就是通过建立一个runnable的线程进行点击事件的注入了


6.系列结语

UiAutomator源代码分析这个系列到了这篇文章算是完结了,从启动执行,到核心的UiAutomatorBridge架构。到实例解剖,通过这些文章我相信大家已经非常清楚uiautomator这个运用了UiAutomation框架与AccessibilityService通信的測试框架是怎么回事了。置于uiautomator那5个专供測试用例调用的类是怎么回事。网上可获得的信息不少。我这里就没有必要做从新造轮子的事情了,况且这些已经不是uiautomator这个框架的核心了,它们仅仅是运用了UiAutomatorBridge这个核心的一些类而已。

 

作者

自主博客

微信

CSDN

天地会珠海分舵

http://techgogogo.com

服务号:TechGoGoGo

扫描码:

http://blog.csdn.net/zhubaitian


UiAutomator源代码分析之获取控件信息的更多相关文章

  1. UiAutomator源码分析之获取控件信息

    根据上一篇文章<UiAutomator源码分析之注入事件>开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下 ...

  2. Android自动化测试中AccessibilityService获取控件信息(1)

    Android自动化测试中AccessibilityService获取控件信息(1) 分类: android自动化测试2014-03-24 15:31 3455人阅读 评论(16) 收藏 举报 and ...

  3. Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比

    Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比   上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...

  4. android自动化测试中hierarchyviewer和uiautomatorviewer获取控件信息的方式比对

    http://blog.csdn.net/itfootball/article/details/21777835 http://blog.csdn.net/chenbang110/article/de ...

  5. 第14章8节《MonkeyRunner源代码剖析》 HierarchyViewer实现原理-获取控件列表并建立控件树

    在上几节的描写叙述中,我们把HierarchyViewer初始化好.也把ViewServer给装备好了.那如今距离获得一个控件去操作它是万事具备仅仅欠东风了,欠了那一股春风了?欠了的是建立控件树这个东 ...

  6. 巧用Handler获取View控件信息

    众所周知,在Android实际开发中,对于某些复杂多变的情况,控件的位置摆放.大小控制并非是xml类型的layout文件完全可以搞定的.此时,我们通常会使用Java代码来通过动态计算,将指定的控件摆放 ...

  7. appium获取APP控件信息

    uiautomatorviewer.bat 该文件位于SDK安装目录tools下,如笔者在“C:\Program Files (x86)\Android\android-sdk\tools”下,双击u ...

  8. UiAutomator源代码分析之UiAutomatorBridge框架

    上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...

  9. 老李推荐:第14章8节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-获取控件列表并建立控件树 5

    看这段代码之前还是请回到“图13-6-1 NotesList控件列表”中重温一下一个控件的每个属性名和值是怎么组织起来的: android.widget.FrameLayout@41901ab0 dr ...

随机推荐

  1. 远征(expedition)

    [题目描述] 寒枫将军将要带领他的部队去圣雪山消灭那里的冰龙. 部队分成了若干个小队,属于同一个小队的人兵种相同.寒枫将军有着杰出的指挥能力,在战斗的时候,寒枫将军能够让所有相同兵种的人互相配合,使t ...

  2. 如何在requirejs下引用bootstrap

    原本以为只要require过来就能用 require(['jquery','underscore','bootstrap','cache'],function($,U,B,C){ 但发现会报错,类似未 ...

  3. 慕课爬虫实战 爬取百度百科Python词条相关1000个页面数据

    http://www.imooc.com/learn/563 spider_main.py #!/usr/bin/python # coding=utf-8 #from baike_spider im ...

  4. linux环境内存分配原理 mallocinfo【转】

    转自:http://www.cnblogs.com/dongzhiquan/p/5621906.html Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和fr ...

  5. 在Ubuntu / Ubuntu Kylin下安装和卸载 Nodepadqq

    在Ubuntu / Ubuntu Kylin下安装和卸载 Nodepadqq         对于Ubuntu发行版本可以通过PPA安装,命令如下: sudo add-apt-repository p ...

  6. PhPStorm 快捷键使用(转载)

    PhPStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具,PhpStorm可随时帮助用户对其编码进行调整,运行单元测试或者提供可视化debug功能.Phpstrom的一款名 ...

  7. AC日记——爱改名的小融3 codevs 3156

    3156 爱改名的小融 3  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description Wikioi上有个人叫小融,他喜 ...

  8. Java爬虫系列二:使用HttpClient抓取页面HTML

    爬虫要想爬取需要的信息,首先第一步就要抓取到页面html内容,然后对html进行分析,获取想要的内容.上一篇随笔<Java爬虫系列一:写在开始前>中提到了HttpClient可以抓取页面内 ...

  9. 转载:P2P技术原理及应用(1)

    转帖allen303allen的空间 作 者:金海 廖小飞 摘要:对等网络(P2P)有3种主要的组织结构:分布式哈希表(DHT)结构.树形结构.网状结构.P2P技术已 经延伸到几乎所有的网络应用领域, ...

  10. NEERC15

    2015-2016 ACM-ICPC Northeastern European Regional Contest 再开一个新坑吧 目前姿势有限,C.H.I仍然处于弃坑状态 代码戳这里 Problem ...