Android自动化框架uiautomator简介
liunx下运行./uiautomatorviewer 启动该工具。
2.uiautomator APIs
uiautomator是一个包含一套UI测试API,和支持运行测试程序的JAR包。该JAR包位于sdk/platforms/android-* /uiautomator.jar. 使用时需要注意自己的SDK版本需要大于16, SDK Tools版本需要大于21.Android版本需要高于4.3。一、UiSelector作用
按照一定的条件(例如控件的text值,资源id),定位界面上的元素。UiSelector对象的最终目的是去构造一个UiObject对象。
二、元素定位
1、根据text定位:
函数返回值 | 函数体 | 说明 | 用法 |
UiSelector | text(String text) | 根据“控件text属性的内容”构造出UiSelector对象 | 例如,一个控件text的值是“发现”,UiSelector s = new UiSelector().text("发现"); |
UiSelector | textContains(String text) | 根据“控件text属性包含的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textContains("现"); |
UiSelector | textMatches(String regex) | 根据“控件text属性正则表达式的内容”构造出UiSelector对象 | 正则表达式语法参考网上资料即可。 |
UiSelector | textStartsWith(String text) | 根据“控件text属性开始的内容”构造出UiSelector对象 | 同上例子:UiSelector s = new UiSelector().textStartsWith("发"); |
比较常用,准确度也比较高,中文查找的时候,有时候text元素是中文的,比如例如,一个控件text的值是“发现”,UiSelector s = new UiSelector().text("发现");此时运行的时候可能会报错 “UiOjbectNotFoundException” ,这时候只要把项目的编码格式改为utf-8就可以了。
2、根据description构造:
UiSelector | description(String desc) | 根据“控件content-desc属性的内容”构造出UiSelector对象 |
UiSelector | descriptionContains(String desc) | 包含** |
UiSelector | descriptionMatches(String regex) | 正则 |
UiSelector | descriptionStartsWith(String desc) | 以**开始 |
同text的用法基本一致,也是比较靠谱的一种方式。
3、根据资源id:
UiSelector | resourceId(String id) | 根据资源id获取对象,例如:UiSelector s = new UiSelector().resourceId("com.tencent.mm:id/b8m") |
UiSelector | resourceIdMatches(String regex) | 根据资源id的正则表达式获取对象 |
4、根据类:
1)UiSelector className(String className):
根据控件的类名来找到UiSelector对象。如图
这时候会出现问题:
因为一般Android布局的时候,同样的控件类名都是一样的。
因此我在微信的登录界面调用: UiSelector s = new UiSelector().className("android.widget.TextView"),它得到的就是我左上开始算第一个class名称为“android.widget.TextView”的控件。
2)UiSelector instance (int instance):
上面提到的假如我们想获取屏幕上电话号码的那个TextView使用这样方法,就可以使用instance:
UiSelector s = new UiSelector().className("android.widget.TextView").instance(1);
3)UiSelector index(int index):
用法和上面的instance差不多,谷歌的原文说这个方法是unreliable的,推荐使用instance方法。
4)UiSelector childSelector(UiSelector selector):
有的时候假如子控件不好获得,而其父控件比较好获得的时候,我们通常采用这样的方式,例如下面:
如图,LinearLayout就是ImageView和EditText的父控件,当子空间text、resource-id为空的时候,这种时候子控件定位比较困难。很明显,父控件id已经给定,那我们就可以先定位到父控件,再定位到子控件这种方法。
在它的父控件的childSelector方法中传入一个带有一定特征的UiSelector对象,即可得到子控件
UiObject wx_input= new UiObject(new UiSelector().className("android.widget.RelativeLayout").childSelector(new UiSelector().className("android.widget.EditText")));
5)UiSelector fromParent(UiSelector selector):
有的时候父控件也不好获得,而是同级的控件(同属一个parent)比较好获取,那么使用这样方法,还拿上面的举例:
我们先得到EditText的UiSelector对象:UiSelector s1 = new UiSelector().resourceId("com.tencent.mm:id/axc");
得到和它同样一个父控件的ImageView的UiSelector对象:UiSelector s2 = fromParent( new UiSelector().className("android.widget.ImageView") );
更多详情见:https://developer.android.com/reference/android/support/test/uiautomator/UiSelector.html
UiObject类:安卓组件对象,类似于webdriver中的webelement队形,提供了元素的属性获取和元素的各种操作。对象有许多模拟实际操作手机的方法和属性。比如文本的编辑、点击、输入、手势操作等。
1、点击与长按
(1)相关API
返回值 | API | 说明 |
boolean | click() | 点击对象 |
boolean | clickAndWaitForNewWindow(long timeout) | 点击对象,等待新窗口出现,参数为等待超时时长 |
boolean | clickAndWaitForNewWindow() | 点击对象,等待新窗口出现 |
boolean | clickBottomRight() | 点击对象的右下角 |
boolean | clickTopLeft() | 点击对象的左上角 |
boolean | longClick() | 长按对象,对对象执行长按操作 |
boolean | longClickBottomRight() | 长按对象的右下角 |
boolean | longClickTopLeft() | 长按对象的左上角 |
(2)示例
new UiObject(new Selector().resourceId("xxxxx")).click();//对指定资源id的组件执行点击操作
2、拖拽与滑动
(1)区别
拖拽:将控件从当前位置移动到指定位置
滑动:向某一方向(上、下、左、右)移动一小段距离
(2)相关API
返回值 | API | 说明 |
boolean | dragTo(UiObject destObj, int steps) | 拖拽对象到另一个对象位置上,步长可设置拖动的速度 |
boolean | dragTo(int destX, int destY, int steps) | 拖拽对象到屏幕某个坐标位置上,步长可设置拖动速度 |
boolean | swipeDown(int steps) | 拖动对象往下滑动 |
boolean | swipeLeft(int steps) | 拖动对象往左滑动 |
boolean | swipeRight(int steps) | 拖动对象往右滑动 |
boolean | swipeUp(int steps) | 拖动对象往上滑动 |
3、输入文本与清除文本
(1)相关API
返回值 | API | 说明 |
boolean | setText(String text) | 在对象中输入文本(实现方式:先清除文本再输入) |
void | clearTextField() | 清除编辑框中的文本(实现方式:长按再清除) |
(2)补充说明
clearTextField()的内部实现方式是先长按文本框然后全选删除,导致有些编辑框无法通过调用该方法清除文本内容。这时最好自己写代码实现清除文本功能。
示例代码:
//将光标移动到行尾,使用backspace进行逐个删除
UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_MOVE_END);
//判断条件中w是编辑框为空时所显示的hint文本对象;当hint出现时,说明该编辑框内的文本已清空
while(!w.exists()){
UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_DEL);
}
4、获取对象的属性与属性的判断
(1)获取对象的属性-相关API
返回值 | API | 说明 |
Rect | getBounds() | 获得对象矩形坐标,矩形左上角坐标与右下角坐标 |
int | getChildCount() | 获得下一级子类数量 |
String | getClassName() | 获得对象类名属性的类名文本 |
String | getContentDescription() | 获得对象的描述属性的描述文本 |
String | getPackageName() | 获得对象包名属性的包名文本 |
String | getText() | 获得对象的文本属性中的文本 |
Rect | getVisibleBounds() | 返回可见视图的范围,如果视图的部分是可见的,只有可见部分报告的范围 |
(2)获取父类与子类节点-相关API
返回值 | API | 说明 |
UiObject | getChild(UiSelector selector) | 获得对象的子类对象,可以递归获取子孙当中某个对象 |
UiObject | getFromParent(UiSelector selector) | 从父类获取子类,按照UiSeletor获取兄弟类(递归) |
(3)属性的判断-相关API
返回值 | API | 说明 |
boolean | isCheckable() | 检查对象的checkable属性是否为true |
boolean | isChecked() | 检查对象的checked属性是否为true |
boolean | isClickable() | 检查对象的clickable属性是否为true |
boolean | isEnabled() | 检查对象的enabled属性是否为true |
boolean | isFocusable() | 检查对象的focusable属性是否为true |
boolean | isFocused() | 检查对象的focused属性是否为true |
boolean | isLongClickable() | 检查对象的longclickable属性是否为true |
boolean | isScrollable() | 检查对象的scrollable属性是否为true |
boolean | isSelected() | 检查对象的selected属性是否为true |
5、手势的操作
(1)手势相关操作
两指平移
多指平移
两指合拢 o---> <---o
两指扩张 <---oo--->
(2)相关API
返回值 | API | 说明 |
boolean | performMultiPointerGesture(PointerCoords[]... touches) | 执行单手指触控手势,可定义任意手势,与形状 |
boolean |
performTwoPointerGesture(Point startPoint1, Point startPoint2, |
执行任意两个手指触控手势,模拟两个手指手势 |
boolean | pinchIn(int percent, int steps) | 手势操作,两点向内收缩 |
boolean | pinchOut(int percent, int steps) | 手势操作,两点向外张开 |
6、判断对象是否存在
(1)相关API
返回值 | API | 说明 |
boolean | waitForExists(long timeout) | 等待对象出现 |
boolean | waitUntilGone(long timeout) | 等待对象消失 |
boolean | exists() | 检查对象是否存在 |
UiDevices类:提供了一些列方法和属性来模拟在手机上的实际操作,获取设备信息:屏幕分辨率、选装状态、亮屏、灭屏...操作:按键、坐标操作、滑动、拖拽、截图
1、UiDevice代表设备状态。如屏幕的大小、旋转方向、按压各种按键等。
2、UiDevice为单例模式,可有2种方式获取其实例。
(1)UiDevice.getInstance();--->推荐
(2)getUiDevice.pressHome();---->在类A中封装方法,方法被类B调用的时候会出现空指针异常
(1)获取设备信息:屏幕分辨率、选装状态、亮灭屏......
(2)操作:按键、坐标操作、滑动、拖拽、截图......
(3)监听器功能
二、按键与KEYCODE使用
1、点击按键 相关API
UiDevice实例调用以下方法即可实现点击按键操作。
返回值 | 方法名 | 描述 |
boolean | pressBack() | 模拟短按返回back键 |
boolean | pressDPadCenter() | 模拟按轨迹球中点按键 |
boolean | pressDPadDown() | 模拟轨迹球向下按键 |
boolean | pressDPadLeft() | 模拟轨迹球向左按键 |
boolean | pressDPadRight() | 模拟轨迹球向右按键 |
boolean | pressDPadUp() | 模拟轨迹球向上按键 |
boolean | pressDelete() | 模拟短按删除delete按键 |
boolean | pressEnter() | 模拟短按回车键 |
boolean | pressHome() | 模拟短按HOME键 |
boolean | pressKeyCode(int keyCode, int metaState) | 模拟短按键盘代码keycode |
boolean | pressKeyCode(int keyCode) | 模拟短按键盘代码keycode |
boolean | pressMenu() | 模拟短按menu键 |
boolean | pressRecentApps() | 模拟短按最近使用程序 |
boolean | pressSearch() | 模拟短按搜索键 |
2、KEYCODE 键盘映射码
(1)KeyEvent:按键事件,每个键盘映射码都保存在keyEvent的常量中。(2)META Key
<1>辅助功能键: ALT、SHIFT、CAPS_LOCK
<2>辅助功能键的状态
激活状态 | metaState | |
base | META_key未被激活 | 0 |
caps | SHIFT或CAPS_LOCK被激活时 | 1 |
fn | ALT被激活 | 2 |
caps_fn | ALT,SHIFT或CAPS_LOCK同时被激活时 | 3 |
<3>示例
UiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_A);//点击输入小写字母aUiDevice.getInstance().pressKeyCode(KeyEvent.KEYCODE_A,1);//点击输入大写字母A
三、获取坐标与坐标点击
1、坐标相关知识
(1)手机屏幕坐标:从左上角(0,0)开始到右下角(X,Y)(2)dp:设备独立像素,例如,320像素显示到640像素上要拉伸一倍
(3)Point:代表一个点(x,y)
2、坐标相关API
UiDevice实例调用以下方法即可实现获取坐标和点击操作。返回值 | 方法名 | 描述 |
boolean | click(int x, int y) | 使用坐标点击屏幕 |
int | getDisplayHeight() | 获取屏幕高度 |
Point | getDisplaySizeDp() | 获取显示尺寸返回显示大小(设备独立像素);屏幕旋转返回的显示大小调整 |
int | getDisplayWidth() |
获得屏幕宽度 |
3、Uiautomator Viewer
(1)功能:获取屏幕快照,并通过快照获取到控件的属性。(2)启用方式
方式一、启动路径:adt/sdk/tools/UiautomatorViewer
方式二、Eclipse/DDMS/Devices/UiautomatorViewer
(3)屏幕快照参数之控件坐标
通过UiautomatorViewer获取屏幕快照,选中目标控件,通过Node Detail / bounds[左上角顶点坐标][右下角顶点坐标] 可获取到控件的位置。
4、示例
(1)
UiDevice.getInstance().click(399,355);//点击[399,355]坐标点
(2)int h = UiDevice.getInstance().getDisplayHeight();
int w = UiDevice.getInstance().getDisplayWidth();
UiDevice.getInstance().click(w/2,h/2); //点击屏幕的中心点
(3)
UiObject object = new UiObject(new UiSelector.resoutceId("com.andr....控件Id"));//根据ID获取控件
Rect r = object.getBounds();//获取控件对应的矩形区域相关属性r.left; //矩形左上角顶点X坐标
r.top; //矩形左上角顶点Y坐标
r.right; //矩形右下角顶点X坐标
r.bottom; //矩形右下角顶点Y坐标
r.centerX(); //矩形的中心点X坐标
r.centerY(); //矩形的中心点Y坐标
四、拖拽与滑动
1、相关概念
(1)拖拽:将一个组件从一个坐标移动到另一个坐标处。
(2)滑动:手指从一个坐标点移动到另一个坐标点。
(3)步长:从一点滑动到另一点使用的时间,步长越短说明移动越快。
2、拖拽与滑动相关API
返回值 | 方法名 | 描述 |
boolean | drag(int startX, int startY, int endX, int endY, int steps) | 拖动对象从一个坐标拖动到另一个坐标,step表示拖拽过程使用的时间(ms) |
boolean | swipe(Point[] segments, int segmentSteps) | 在点阵列中滑动,5ms一步 |
boolean | swipe(int startX, int startY, int endX, int endY, int steps) | 通过坐标滑动屏幕 |
3、示例:按照一个矩形的路径滑动
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
Point p4 = new Point();
p1.x =277;
p1.y =318;
...... //设置四个顶点的横纵坐标
Point[] pp = {p1,p2,p3,p4};
UiDevice.getInstance().swipe(pp,50); //每50毫秒走一步(从一个点滑动到下一个点)
五、旋转屏幕
1 、旋转屏幕相关知识
(1)旋转方向:4个方向,分别为 0度,90度,180度,270度
(2)重力感应器
(3)固定位置与物理旋转
固定位置:屏幕的方向固定在0、90、180、270度;
物理旋转:与重力感应器连接,关闭了物理旋转就是关闭了重力感应器。
2、屏幕旋转相关API
返回值 | 方法名 | 描述 |
void | setOrientationLeft() | 通过禁用传感器,然后模拟设备向左转,并且固定位置 |
void | setOrientationNatural() | 通过禁用传感器,然后模拟设备转到其自然默认的方向,并且固定位置 |
void | setOrientationRight() | 通过禁用传感器,然后模拟设备向右转,并且固定位置 |
void | unfreezeRotation() | 重新启用传感器和允许物理旋转 |
boolean | isNaturalOrientation() | 检测设置是否处于默认旋转状态 |
int | getDisplayRotation() | 返回当前的显示旋转,0度,90度,180度,270度值分别为:0(Surface.ROTATION_0)、1(Surface.ROTATION_90)、2(类推)、3(类推) |
void | freezeRotation() | 禁用传感器和冻结装置物理旋转在其当前旋转状态 |
六、灭屏与唤醒
灭屏与唤醒相关API
返回值 | 方法名 | 描述 |
void | wakeUp() | 模拟按电源键,如果屏幕是唤醒的没有任何作用 |
void | sleep() | 模拟按电源键,如果屏幕已经是关闭的则没有任何作用 |
boolean | isScreenOn() | 检查屏幕是否亮屏 |
七、截图与等待空闲
1 、截图与等待空闲相关知识
(1)图片缩放比例:如缩小1/2,即将100*100px的图片长宽都缩小为原来的1/2,50*50px。
(2)图片质量:一般是指图片的大小,质量越高图片越大。
(3)File 类:文件或者文件夹。
(4)图片格式 :截图的格式都是PNG。
(5)空闲状态:窗口没有更新或界面无动作。
(6)窗口更新事件。
2、截图相关API
返回值 | 方法名 | 描述 |
boolean | takeScreenshot(File storePath) | 把当前窗口截图并将其存储为png默认1.0f的规模(原尺寸)和90%质量,参数为file类的文件路径。 |
boolean | takeScreenshot(File storePath, float scale, int quality) | 把当前窗口截图为png格式图片,可以自定义缩放比例与图片质量。 |
参数说明:
storePath:存储路径,必须为png格式。
scale:缩放比例,1.0为原图。
quality:图片压缩质量,范围为0-100。
3、等待空闲相关API
返回值 | 方法名 | 描述 |
void | waitForIdle(long timeout) | 自定义超时等待当前应用处于空闲状态 |
void | waitForIdle() | 等待当前应用处于空闲状态,默认等待10s;即10s后还不处于空闲状态则报错,程序在该句代码处中断;10s内程序处于空闲状态,则该句代码执行完毕。 |
boolean | waitForWindowUpdate(String packageName, long timeout) | 等待窗口内容更新事件的发生 |
八、获取包名&开启通知栏&快速设置&获取布局文件
1、相关知识
包名:应用的唯一标识。
通知栏:从手机顶部下滑,出现的下拉界面即通知栏。
快速设置:即通知栏中的快速设置控件,快速设置界面可设置网络、屏幕亮度、飞行模式等。
2、相关API
返回值 | 方法名 | 描述 |
void | getCurrentPackageName() | 获取当前界面的包名,即目前处于手机前台的应用的包名 |
void | dumpWindowHierarchy(String fileName) | 获取当前界面的布局文件,fileName给布局文件命名如“layout.xml”,保存在/data/local/tmp/目录下 |
boolean | openNotification() | 打开通知栏 |
boolean | openQuickSettings() | 打开快速设置 |
更多具体用法见官方文档:https://developer.android.google.cn/training/testing/ui-automator#ui-automator-apis