自己和Android的自动化测试已经打了3年交道有余,却一直没有详细了解一下robotium,最近终于抽出时间阅读了其源码,把收获好好记录一番。
众所周知,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
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。
/** * 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[]
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<View>) 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,并将其所有子控件引入。
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[i] = view; i++; } } } return decorViews; }
对于Activity/Dialog的筛选,Robotium采取对比DrawingTime的方法选出最后绘制的DecorView,其即为最上层Activity/Dialog的DecorView:
/** * 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[i]; 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函数:
private void addChildren(ArrayList<View> 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是怎么判断的:
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; }
相关推荐
robotium 5.0.1 源码 直接将源码包含进测试项目中使用,不用加载jar包,或者直接自定义更改源码适应指定要求。
robotium 最新版本5.0.1 robotium-solo-5.0.1.jar
这是spring5.0.1的源码包,不是spring的依赖jar包,请务必不要直接放到项目中
ExtJs5.0.1官方源码,方便找不到历史版本代码的同学。
骑士cms(74CMS)高效人才招聘系统 v5.0.1 正式版 更新日志 新增:后台增加跳转页面 新增:短信预警提醒,方便客户及时充值 新增:企业与个人账号通用 新增:未付费且登录的企业单独呈现,方便追踪客户 新增:...
android 5.0.1 源码
PotaPoco 5.0.1最新版源码。 如:Insert时返回的不是true,而是1了。
ffmpeg-5.0.1
robotium 最新版本5.0.1 robotium-solo-5.0.1.jar
android5.0.1-api21 官网源码
Net实战商用源码---最新TourEx旅游系统v5.0.1多城市版源码 旅游网源码。
elasticsearch5.0.1源码中elasticsearch-5.0.1\core\src\main文件中的源码,已通过gradle工具转换到了eclipse可以直接打开的工程
I函数简介获取变量如果要获取的变量类型是 get、post 或 put,可以统一用 param 变量类型,param 变量类型是框架特有的支持自动判断当前请求类
骑士cms人才系统是一项基于PHP+MYSQL为核心开发的一套 + 开源专业人才招聘系统。软件具执行效率高、模板自由切换、后台管理功能方便等诸多优秀特点。全部代码都为骑士网络原创
LoraWAN V5.0.1版本源码。有需要的朋友可以下载研究研究。
VNC-5.0.1.rar VNC-5.0.1.rar VNC-5.0.1.rar VNC-5.0.1.rar
Elasticsearch-5.0.1-src源码 CSDN下载会快很多!
Hp-Socket For E 5.0.1 beta1 源码+模块+例程帮助文档,较全面的,有需要的下载吧,自己用易语言开发用到的
robotium-solo-3.4.1.jar,
ACDSee5.0.1精简美化