Robotium 控件搜索原理

转载 2015年07月10日 16:56:21
众所周知,Robotium是基于Android的单元测试框架Instrumentation,而robotium对于Instrumentation封装的比较强的地方便是控件搜索,因此首先先来了解一下在robotium中控件的搜索原理,这部分的源码主要位于ViewFetcher.java中。

 1.mViews的获取    要先搜索控件,必须先得到Activity的rootView。在Android中,对于一般的Activity或其对话框,其rootView叫做DecorView,其实就是Activity和Dialog外面的那层框(关于Activity或dialog的层次可以用HierarchyViewer来查看)。

虽然通过Activity类的getWindow().getDecorView可以获取到Activity自身的DecorView,但是无法获取到对话框的,因此Robotium中界面控件是从WindowManagerGlobal(或WindowManagerImpl)中的mViews获取到的。当然mViews中不但包含DecorView,还包含同进程内的所有界面的根节(如悬浮框的根节点)。mView的值的获取过程主要如下:

1) 确定mViews所在类:android 4.2之前,获取类为android.view.WindowManagerImpl,4.2及之后,获取类为WindowManagerGlobal

Java代码 [url=][/url]

String windowManagerClassName;
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerClassName = “android.view.WindowManagerGlobal”;
} else {
windowManagerClassName = “android.view.WindowManagerImpl”;
}
windowManager = Class.forName(windowManagerClassName)

2). 获得类的实例:此类是个单例类,有直接的静态变量可以获取到其实例, 4.2及之后的版本其变量名为sDefaultWindowManager,3.2至4.1,其变量名为sWindowManager,3.2之前,其变量名为mWindowManager。

Java代码 [url=][/url]

/**
* Sets the window manager string.
*/
private void setWindowManagerString(){
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerString = “sDefaultWindowManager”;
} else if(android.os.Build.VERSION.SDK_INT >= 13) {
windowManagerString = “sWindowManager”;
} else {
windowManagerString = “mWindowManager”;
}
}

3). 获取mViews变量的值了,从4.4开始类型变为ArrayList<View>,之前为View[]

Java代码 [url=][/url]

viewsField = windowManager.getDeclaredField(“mViews”);
instanceField = windowManager.getDeclaredField(windowManagerString);
viewsField.setAccessible(true);
instanceField.setAccessible(true);
Object instance = instanceField.get(null);
View[] result;
if (android.os.Build.VERSION.SDK_INT >= 19) {
result = ((ArrayList) viewsField.get(instance)).toArray(new View[0]);
} else {
result = (View[]) viewsField.get(instance);
}

2.mViews的过滤 mViews中会包含三种类型的View:
1) 当前显示的以及没有显示的Activity的DecorView
2) 当前对话框的DecorView
3) 悬浮框View等其他不属于DecorView的独立View

 在搜索控件时,显然需要在最上层界面中搜索,所以搜索范围为: 
          最上层的Activity/Dialog + 悬浮框

 对于悬浮框,robotium中的处理是找出mViews中不属于DecorView类的View,并将其所有子控件引入。

Java代码 [url=][/url]

private final View[] getNonDecorViews(View[] views) {
View[] decorViews = null;
if(views != null) {
decorViews = new View[views.length];
int i = 0;
View view;
for (int j = 0; j < views.length; j++) {
view = views[j];
if (view != null && !(view.getClass().getName()
.equals(“com.android.internal.policy.impl.PhoneWindow$DecorView”))) {
decorViews = view;
i++;
}
}
}
return decorViews;
}

对于Activity/Dialog的筛选,Robotium采取对比DrawingTime的方法选出最后绘制的DecorView,其即为最上层Activity/Dialog的DecorView:

Java代码 [url=][/url]

/**
* Returns the most recent view container
*
* @param views the views to check
* @return the most recent view container
*/
private final View getRecentContainer(View[] views) {
View container = null;
long drawingTime = 0;
View view;
for(int i = 0; i < views.length; i++){
view = views;
if (view != null && view.isShown() && view.hasWindowFocus() && view.getDrawingTime() > drawingTime) {
container = view;
drawingTime = view.getDrawingTime();
}
}
return container;
}

3.控件过滤&控件列表生成 得到悬浮框的根节点和最上层的DecorView后,robotium会将所有View统一添加到一个ArrayList中生成控件列表。添加方法本身很简单,就是一个简单的递归,但需要注意的是此处有一个onlySufficientlyVisible的判断。onlySufficientlyVisible是ViewFetcher中最常见的一个变量,其表示是否过滤掉显示不完全的控件,即onlySufficientlyVisible为true时表示只在显示完全的控件中搜索目标,为false时表示在所有控件中搜索目标。具体代码为下面的addChildren函数:

Java代码 [url=][/url]

private void addChildren(ArrayList views, ViewGroup viewGroup, boolean onlySufficientlyVisible) {
if(viewGroup != null){
for (int i = 0; i < viewGroup.getChildCount(); i++) {
final View child = viewGroup.getChildAt(i);
if(onlySufficientlyVisible && isViewSufficientlyShown(child))
views.add(child);
else if(!onlySufficientlyVisible)
views.add(child);
if (child instanceof ViewGroup) {
addChildren(views, (ViewGroup) child, onlySufficientlyVisible);
}
}
}
}

从上面的代码可以看出,当onlySufficientlyVisible为true时,robotium会对控件的可见不可见进行检查。不过这里的可见不可见不是指Visible或Invisible(Robotium过滤Invisible控件的方法是RobotiumUtils.removeInvisibleViews,原理是利用view.isShown()方法),而是指由于界面滚动而导致的没有显示或显示不完全。继续看Robotium对SufficientlyVisible是怎么判断的:

Java代码 [url=][/url]

public final boolean isViewSufficientlyShown(View view){
final int[] xyView = new int[2];
final int[] xyParent = new int[2];
if(view == null)
return false;
final float viewHeight = view.getHeight();
final View parent = getScrollOrListParent(view);
view.getLocationOnScreen(xyView);
if(parent == null){
xyParent[1] = 0;
}
else{
parent.getLocationOnScreen(xyParent);
}
if(xyView[1] + (viewHeight/2.0f) > getScrollListWindowHeight(view))
return false;
else if(xyView[1] + (viewHeight/2.0f) < xyParent[1])
return false;
return true;
}

代码中getScrollOrListParent是获取控件所属的ListView或ScrollView,可能是控件本身也可能是空。getScrollListWindowHeight函数用于获取控件所属的ListView或ScrollView最下面边界的Y坐标。因此Java代码  [url=][/url]

xyView[1] + (viewHeight/2.0f) > getScrollListWindowHeight(view)

这个判断就表示控件有超过一半的面积被隐藏在了父控件的下方,而
Java代码 [url=][/url]

(xyView[1] + (viewHeight/2.0f) < xyParent[1]

则表示控件有超过一半的面积被隐藏在了父控件的上方,这两种情况都被Robotium判断为不满足SufficientlyVisible的(不过好像没有判断横向的?)。

根据onlySufficientlyVisible过滤掉相应控件后,robotium便完成了控件列表的生成工作,之后的搜索就可直接在列表中进行查找了。

有的时候要搜索指定类型的控件,可以按照类型对控件列表进行再一次的过滤,ViewFetcher中的代码如下:

Java代码 [url=][/url]

public ArrayList getCurrentViews(Class classToFilterBy, View parent) {
ArrayList filteredViews = new ArrayList();
List allViews = getViews(parent, true);
for(View view : allViews){
if (view != null && classToFilterBy.isAssignableFrom(view.getClass())) {
filteredViews.add(classToFilterBy.cast(view));
}
}
allViews = null;
return filteredViews;
}

可以看到,robotium直接利用了Class. isAssignableFrom进行类型的匹配。

4.文本搜索获得了控件列表,可以开始搜索指定的目标控件了,先从我们最常用的文本搜索开始,看看robotium的搜索流程。搜索过程的代码主要位于Searcher.java中,主要功能在两个searchFor函数中实现,通过嵌套完成目标的搜索。
第一层

Java代码 [url=][/url]

public T searchFor(final Class viewClass, final String regex, int expectedMinimumNumberOfMatches, final long timeout, final boolean scroll, final boolean onlyVisible) {
//修正非法的expectedMinimumNumberOfMatches
if(expectedMinimumNumberOfMatches < 1) {
expectedMinimumNumberOfMatches = 1;
}
//定义一个Callable给下层searchFor使用,可以直接获取到符合条件的控件列表
final Callable

相关文章推荐

Robtium Scroller

package com.robotium.solo;import java.util.ArrayList; import com.robotium.solo.Solo.Config; import...

Robotium API 翻译(五)——web控件测试方法,WebElement和By类

在Robotium中,可以通过id、index、控件名称或者对应的text正则表达式来定位一个本地的控件。那么对于外部的web控件来说,在你的程序里面无法像本地控件一样找到id、index什么的,怎么...

MonkenRunner通过HierarchyViewer定位控件的方法和建议(Appium/UIAutomator/Robotium姊妹篇)

1. 背景 在使用MonkeyRunner的时候我们经常会用到Chimchat下面的HierarchyViewer模块来获取目标控件的一些信息来辅助我们测试,但在MonkeyRunner的官网上是没有...

Robotium API 翻译(五)——web控件测试方法,WebElement和By类

在Robotium中,可以通过id、index、控件名称或者对应的text正则表达式来定位一个本地的控件。那么对于外部的web控件来说,在你的程序里面无法像本地控件一样找到id、index什么的,怎么...

Android结合robotium实现自定义waitForText(String text),解决控件无法找到问题

Android结合robotium实现自定义waitForText(String text),解决控件无法找到问题 作者 HAO.SHEN 可能有人会问,robotium自带了wait...

Robotium API 翻译(五)——web控件测试方法,WebElement和By类

在Robotium中,可以通过id、index、控件名称或者对应的text正则表达式来定位一个本地的控件。那么对于外部的web控件来说,在你的程序里面无法像本地控件一样找到id、index什么的,怎么...

Robotium之Android控件定位实践和建议(Appium/UIAutomator姊妹篇)

本人之前曾经撰文描述Appium和UIAutomator框架是如何定位Android界面上的控件的。 UIAutomator定位Android控件的方法实践和建议 Appium基于安卓的各种Fin...

Robotium原理初步--Android自动化测试学习历程

章节:自动化基础篇——Robotium原理初步(第四讲)     主要讲解内容与笔记: 一、基于控件 1、spinner——下拉菜单 2、TabHost——左右滑动选择菜单,类似电话本 ...

Robotium_android apk签名打包 原理 用处 方法

在Android系统中,所有安装到系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程序之间建立信任关系,如果一个permission的protectionLevel为sign...

Robotium原理初探

测试框架图: Android测试环境的核心是Instrumentation框架,Instrumentation框架通过将主程序和测试程序在同一个进程来实现这些功能。在测试工程的m...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)