标签:uiautomator accessibilityservice uiautomation
根据上一篇文章《UiAutomator源码分析之注入事件》开始时提到的计划,这一篇文章我们要分析的是第二点:
UiObject appsTab = new UiObject(new UiSelector().text("Apps")); appsTab.click()那么这个过程发生了什么呢?这就是我们接下来要说的事情了。
在我没有去分析uiautomator的源代码之前,我一直以为空间查找是在通过UiSelector初始化一个UiObject的时候发生的:
UiObject appsTab = new UiObject(new UiSelector().text("Apps"));
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信息的。
下面我们看下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); /* */ } /* */ }这里做了两个重要的事情:
/* */ 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.
/* */ 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的线程进行点击事件的注入了
标签:uiautomator accessibilityservice uiautomation
原文地址:http://blog.csdn.net/zhubaitian/article/details/40581369