在上面一篇文章中介绍了Android模拟点击按键的技术,但是对于自动化测试来说,使用坐标来编写执行脚本是很难维护的,所以需要一种更灵活的方式。
获取控件信息
通过Android sdk提供的工具,hierachyviewer处理工具能获取到已连接Android设备的当前界面的控件树信息,并且对每个控件的基础信息进行了导出,其中有一部分的控件信息是控件的rect,通过这个rect我们可以获取到控件的中心坐标。
但是这个方法获取的速度太慢了,所以在Android4.4后出现了uiautomatorviewer能生成较为简单的控件树信息,通过查看uiautomator的源码:
可以看到使用了Accessbility这个Class,通过api查看这也是Android手机上辅助服务的核心方法,主要是用来帮助盲人读取界面文字语音播放的。正好可以通过这个方法来实现我们的自动化测试用来识别控件。
所以需要自定义一个辅助服务,并且开启服务,首先在manifest注册服务
获取nodes时同时判断是否连接服务,如果没有连接上则开启连接,其中重连10次如果一直不能连接上则放弃该操作:
/**
* update the infos list
*/
@SuppressLint("NewApi")
public void getNodes() {
AccessibilityNodeInfo rootNode = null;
rootNode = getRootNode();
if (rootNode == null) {
System.err.println("ERROR: null root node returned by AccessibilityNodeInfosObtain.");
}
AccessibilityNodeInfoDumper dumper = new AccessibilityNodeInfoDumper(mContext);
infos = dumper.dumpWindow(rootNode);
}
/**
* Get root node.
*
* @return AccessibilityNodeInfo
*/
private AccessibilityNodeInfo getRootNode() {
AccessibilityNodeInfo rootNode = null;
int i = 0;
while (i < 10) {
AccessibilityEventService eventService = AccessibilityEventService.getService();
if (eventService != null) {
rootNode = eventService.getRootInActiveWindow();
}
if (rootNode == null) {
if (DEBUG)
Log.e(TAG, "try to connect:" + i);
i++;
openAccessibilityServiceIfNotStart();
SystemClock.sleep(500);
} else {
break;
}
}
return rootNode;
}
private void openAccessibilityServiceIfNotStart() {
openService(NAME);
Intent intent = new Intent();
intent.setClass(mContext, AccessibilityEventService.class);
// If not start ,to start
if (!mAssert.isMyServiceRunning(AccessibilityEventService.class.getName())) {
mContext.startService(intent);
}
}
在上面用到了AccessibilityNodeInfoDumper这个class,为了加快界面遍历速度,将其中不可点击的控件进行过滤,这样使得得到的控件信息速度更快。
通过AccessibilityNodeInfoDumper将得到AccessibilityNodeInfo
的数组数据。这样我们在编写脚本的时候不再写具体的坐标值,而是去将界面的文字或其它信息封装成点击方法,如下我们可以通过text来遍历nodes数组去找到相应的控件并点击:
/**
*
* 2015-1-19 hwy
*
* @param text
* the ui with the text to click
* @param waitTime
* the wait time after clcik(ms)
* @return
*/
public boolean clickOnText(String text, int waitTime) {
mWaitTime = waitTime;
getNodes();
if (infos == null || infos.size() == 0) {
return false;
}
for (int i = 0; i < infos.size(); i++) {
String title = infos.get(i).getText() + "";
if (title.equals(text)) {
return click(i);
}
}
Log.e(TAG, "clickOnText false!");
return false;
}
当然还可以通过描述、id等方式去扩展,这样我们自动化测试软件的脚本适配难度将大大降低。