zoukankan      html  css  js  c++  java
  • Python-uiautomator使用说明文档

    https://github.com/xiaocong/uiautomator

    这个Python库是基于Android自带的uiautomator测试框架的一个python封包。适用于Android 4.1以上版本,需要通过adb连接Android设备。

    from uiautomator import device as d
    d . screen.on()
    d(text="Clock").click()

    安装

    pip install uiautomator

    前置条件
    安装 Android的SDK,设置Android_home环境以正确的路径。
    启动 adb 并且通过usb数据线将电脑与Android设备相连接。
    设置Android设备,在开发者选项中允许未知来源应用安装。

    导入uiautomator
    如果仅有一台设备或在 Android_serial 环境变量中配置。

    from uiautomator import device as d

    通过设备的设备ID来确定设备对象:

    from uiautomator import Device
    d=Device('014E05DE0F02000E')

    通过其他计算机的端口及连接运行adb服务
    虽然 adb 支持sdk 4.3的 -a选项 ,但是现在它有问题。在所有非本地服务上启动adb监听服务使用
    adb -a -P 5037 fork-server server &

    from uiautomator import Device
    d=Device('014E05DE0F02000E', adb_server_host='192.168.1.68', adb_server_port=5037)

    注:在下面的例子中,我们使用d代表Android设备对象。

    基本API用法

    此部分通过一些简单的示例显示设备的正常操作。
    检索设备信息

    d.info

    以下是我这里得到的信息:

    u'displayRotation': 0,
      u'displaySizeDpY': 640,
      u'displaySizeDpX': 360,
      u'currentPackageName': u'com.android.launcher',
      u'productName': u'takju',
      u'displayWidth': 720,
      u'sdkInt': 18,
      u'displayHeight': 1184,
      u'naturalOrientation': True

    设备的关键时间操作

    打开/关闭屏幕

    #在屏幕上打开 
    d.screen.on() 
    #关闭屏幕 
    d.screen.off()

    替代方法是:

    #唤醒设备 
    d.wakeup() 
    #睡眠设备,一样关闭屏幕。
    d.sleep()

    按硬/软键

    #按home键 
    d.press.home() 
    #按返回键 
    d.press.back()
    #正常的方式按返回键 
    d.press( “back”)
    #按下键码0×07(‘0’)与ALT (0X02) 
    d.press(0x07,0X02)

    目前下列按键支持上述方法

    home
    back
    left
    right
    home
    back
    left
    right
    up
    down
    center
    menu
    search
    enter
    delete(or del)
    recent(recent apps)
    volume_up
    volume_down
    volume_mute
    camera
    power
    down
    center
    menu
    search
    enter
    delete(or del)
    recent(recent apps)
    volume_up
    volume_down
    volume_mute
    camera
    power

    你可以找到所有的键值定义在Android KeyEvent连接如下:
    developer.android.com/reference/android/view/KeyEvent.html

    在设备上模拟手势交互

    #点击屏幕
    # click (x, y) on screen
    d.click(x, y)
    #长按屏幕
    # long click (x, y) on screen
    d.long_click(x, y)

    滑动

    # swipe from (sx, sy) to (ex, ey)
    #从sx,sy坐标滑动至ex,ey坐标
    d.swipe(sx, sy, ex, ey)
    # swipe from (sx, sy) to (ex, ey) with 10 steps
    d.swipe(sx, sy, ex, ey, steps=10)

    拖动

    # drag from (sx, sy) to (ex, ey)
    d.drag(sx, sy, ex, ey)
    # drag from (sx, sy) to (ex, ey) with 10 steps
    d.drag(sx, sy, ex, ey, steps=10)

    设备屏幕的事件
    有以下几种属性:
    natural 或者 n 代替
    left 或者 l 代替
    right 或者 r 代替
    upsidedown或 u(不能设定)

    #获取orientation(方向),可能是上述中的任意一种
    orientation = d.orientation
    #设置定向和冻结旋转。
    #说明:"upsidedown"不能用于Android 4.3 以前的版本
    d.orientation="l"
    d.orientation="r"
    d.orientation="n"

    锁定/解除旋转

    #锁定旋转
    d.freeze rotation()
    #解除旋转锁定
    d.freeze_rotation(False)

    屏幕截图

    获取屏幕截图并且将其存本机地址中,命名为home.png,不能被用于Android 4.2以前的版本

    d.screenshot("home.png")

    将屏幕结构储存(就是使用uiautomatorviewer看到的那个结构,以xml格式保存)

    #将当前屏幕结构保存在本机并命名为"heierarchy.xml"
    
    d.dump("hierarchy.xml")
    
    #或者将存储值作为结果返回
    xml=d.dump()

    打开通知消息栏或快速设置

    #打开通知消息栏,不能用于Android 4.3以前的版本
    d.notification()
    #打开快速设置栏,不能用于Android 4.3以前的版本
    d.open.quick_settings()

    等待空闲或者窗口刷新

    # 等待当前窗口空闲
    d.wait.idle()
    #等待直到窗口发生刷新事件
    d.wait.update()

    监视器
    You can register watcher to perform some actions when a selector can not find a match.
    当选择器无法找到匹配时,您可以注册观察器来执行一些操作。

    注册监视器

    当一个选择器找不到匹配时,uiautomator 会运行全部已经注册的观察者

    条件匹配时点击目标

    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") 
                                 .click(text="Force Close")
    # d.watcher(name) ## creates a new named watcher.
    (创建一个新的监视器,并将其命名为“name”)
    #  .when(condition)  ## the UiSelector condition of the watcher.
    (为监视器添加添加一个Uiselector条件)
    #  .click(target)  ## perform click action on the target UiSelector.
    (执行对目标Uiselector点击动作)

    条件匹配时按下按键

    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") 
                                 .press.back.home()
    # Alternative way to define it as below
    (可以将其定义为以下方法)
    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") 
                                 .press("back", "home")
    # d.watcher(name) ## creates a new named watcher.
    (创建一个监视器)
    #  .when(condition)  ## the UiSelector condition of the watcher.
    (为监视器添加一个Uiselector条件)
    #  .press.<keyname>.....<keyname>.()  ## press keys one by one in sequence.
    (按顺序依次按下key)
    #  Alternative way defining key sequence is press(<keybname>, ..., <keyname>)
    (定义另一种键值序列的方法是‘press(<keybname>, ..., <keyname>)’)

    检查监视器是否触发
    一个监视器触发意味着这个监视器所有条件都匹配并且监视器运行。

    d.watcher("watcher_name").triggered
    # true in case of the specified watcher triggered, else false
    (监视器被触发为‘真’,反之则为‘假’)

    删除监视器命名

    # remove the watcher
    (删除监视器)
    d.watcher("watcher_name").remove()

    列出所有的监视器

    d.watchers
    # a list of all registered wachers' names
    (一个已注册的监视器名称列表)

    检查是否有监视器被触发

    d.watchers.triggered
    #  true in case of any watcher triggered
    (在任何监视器被触发的情况下)

    重置所有已触发的监视器

    # reset all triggered watchers, after that, d.watchers.triggered will be false.
    (重置所有已触发的监视器,并且将d.watchers.triggered 参数变为false)
    d.watchers.reset()

    删除监视器

    # remove all registered watchers
    (删除所有已触发的监视器)
    d.watchers.remove()
    # remove the named watcher, same as d.watcher("watcher_name").remove()
    (删除特定名称的监视器,与d.watcher("watcher_name").remove() 相同)
    d.watchers.remove("watcher_name")

    强制运行所有的监视器

    # force to run all registered watchers
    (强制运行所有已注册的监视器)
    d.watchers.run()

    处理程序

    处理程序的功能与Watcher相同,只是它实现了我们的Android uiautomator。
    处理程序和观察程序之间最不同的用法是,处理程序可以使用自定义的回调函数

    def fc_close(device):
      if device(text='Force Close').exists:
        device(text='Force Close').click()
      return True  
    # return True means to break the loop of handler callback functions.
    (返回True来中断处理程序回调函数的循环)
    
    # turn on the handler callback function(打开处理程序返回函数)
    d.handlers.on(fc_close)
    
    # turn off the handler callback function(关闭处理程序返回函数)
    d.handlers.off(fc_close)

    选择器

    选择器是标识当前窗口中的特定UI对象

    # To seleted the object ,text is 'Clock' and its className is 'android.widget.TextView'
    (对于选择器对象,文字是“Clock”和它的类名是“Android.widget.TextView”)
    d(text='Clock', className='android.widget.TextView')

    选择器支持以下参数。请参阅UiSelector DOC java的详细信息。

    text,textContains,textMatches,textStartsWith
    className, classNameMatches
    description,descriptionContains,descriptionMatches,descriptionStartsWith
    checkable,checked,clickable,longClickable
    scrollable,enabled,focusable,focused,selected
    packageName, packageNameMatches
    resourceId, resourceIdMatches
    index, instance

    子对象和同级UI对象
    子对象

    # get the child or grandchild(得到子类或复子类)
    d(className="android.widget.ListView").child(text="Bluetooth")

    同级对象

    # get sibling or child of sibling(得到同级对象或子类)
    d(text="Google").sibling(className="android.widget.ImageView")

    子类文本或描述或实例

    # get the child match className="android.widget.LinearLayout"
    (获取子类匹配的 className="android.widget.LinerarLayout")
    # and also it or its child or grandchild contains text "Bluetooth"
    (而且它或它的子类和复子类包含文本“Bluetooth”)
    d(className="android.widget.ListView", resourceId="android:id/list") 
     .child_by_text("Bluetooth", className="android.widget.LinearLayout")
    
    # allow scroll search to get the child
    (允许滚动搜索获得子类)
    d(className="android.widget.ListView", resourceId="android:id/list") 
     .child_by_text(
        "Bluetooth",
        allow_scroll_search=True,
        className="android.widget.LinearLayout"
      )

    child_by_description是找到哪个子类或复子类包含与child_by_text相同的描述说明。
    child_by_instance查找一个目标类中子类中特定的UI元素,用于没有滚动可见视图上执行。

    详情请参阅以下链接:

    UiScrollable, getChildByDescription, getChildByText, getChildByInstance
    UiCollection, getChildByDescription, getChildByText, getChildByInstance

    上面的方法支持链接调用,例如对于下面的层次结构

    <node index="0" text="" resource-id="android:id/list" class="android.widget.ListView" ...>
      <node index="0" text="WIRELESS & NETWORKS" resource-id="" class="android.widget.TextView" .../>
      <node index="1" text="" resource-id="" class="android.widget.LinearLayout" ...>
        <node index="1" text="" resource-id="" class="android.widget.RelativeLayout" ...>
          <node index="0" text="Wi‑Fi" resource-id="android:id/title" class="android.widget.TextView" .../>
        </node>
        <node index="2" text="ON" resource-id="com.android.settings:id/switchWidget" class="android.widget.Switch" .../>
      </node>
      ...
    </node>

    我们要点击文本“Wi-Fi”右侧的开关打开/打开Wi-Fi。因为有几个开关,几乎相同的属性,所以我们不能使用类似d(className=”android.widget.Switch”)选择的UI对象。相反,我们可以使用下面的代码来选择它。

    d(className="android.widget.ListView", resourceId="android:id/list") 
      .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") 
      .child(className="android.widget.Switch") 
      .click()

    相对位置
    此外,我们可以用相对位置的方法来获取视图:left,right,top,bottom。

    d(A).left(B),意味着在左侧选择B。
    d(A).right(B),表示选择A右侧的B.
    d(A).up(B),表示选择B以上的A.
    d(A).down(B),表示在A下选择B.
    

    所以对于上面的情况,我们可以写代码:

    ## select "switch" on the right side of "Wi-Fi"
    (选择无线网络连接右侧的开关)
    d(text="Wi‑Fi").right(className="android.widget.Switch").click()

    多个实例

    有时,屏幕可能包含多个视图与相同的例如文本,那么你将不得不使用选择器中的“实例”属性,
    如下所示:

    d(text="Add new", instance=0)  
    # which means the first instance with text "Add new"
    (这意味着第一个实例是文字“Add new”)

    但是,uiautomator提供了类似的方法来使用它。

    # get the count of views with text "Add new" on current screen
    (统计当前界面中使用文本“Add new”)
    d(text="Add new").count
    
    # same as count property
    (统计相同的属性)
    len(d(text="Add new"))
    
    # get the instance via index
    (通过索引获取实例)
    d(text="Add new")[0]
    d(text="Add new")[1]
    ...
    
    # iterator
    (迭代器)
    for view in d(text="Add new"):
        view.info  # ...

    注意:当您使用选择器像一个列表时,你必须确保屏幕保持不变,否则你可能会得到UI未找到错误。

    获取所选的ui对象状态及其信息

    检查特定ui对象是否存在

    d(text="Settings").info

    以下是可能的结果:

    { u'contentDescription': u'',
      u'checked': False,
      u'scrollable': False,
      u'text': u'Settings',
      u'packageName': u'com.android.launcher',
      u'selected': False,
      u'enabled': True,
      u'bounds': {u'top': 385,
                  u'right': 360,
                  u'bottom': 585,
                  u'left': 200},
      u'className': u'android.widget.TextView',
      u'focused': False,
      u'focusable': True,
      u'clickable': True,
      u'chileCount': 0,
      u'longClickable': True,
      u'visibleBounds': {u'top': 385,
                         u'right': 360,
                         u'bottom': 585,
                         u'left': 200},
      u'checkable': False
    }

    设置/清除可编辑字段的文本

    d(text="Settings").clear_text()  # clear the text(清除文本信息)
    d(text="Settings").set_text("My text...")  # set the text(设置文本信息)

    对选中的ui对象执行单击操作

    点击特定的ui对象

    # click on the center of the specific ui object
    (点击特定的UI对象的中心)
    d(text="Settings").click0()
    # click on the bottomright corner of the specific ui object
    (点击具体的的UI对象的右下角)
    d(text="Settings").click.bottomright()
    # click on the topleft corner of the specific ui object
    (点击具体的UI对象的左上角)
    d(text="Settings").click.topleft()
    # click and wait until the new window update
    (等待更新后点击)
    d(text="Settings").click.wait()

    长时间点击特定的ui对象

    # long click on the center of the specific ui object
    (长按特定的UI对象的中心)
    d(text="Settings").long_click()
    # long click on the bottomright corner of the specific ui object
    (长按特定的UI对象的右下角)
    d(text="Settings").long_click.bottomright()
    # long click on the topleft corner of the specific ui object
    (长按特定UI对象的左上角)
    d(text="Settings").long_click.topleft()

    针对特定UI对象

    # notes : drag can not be set until Android 4.3.
    (注:不能用于Android4.3以下的版本)
    # drag the ui object to point (x, y)
    (拖拽UI对象至点X,Y)
    d(text="Settings").drag.to(x, y, steps=100)
    # drag the ui object to another ui object(center)
    (拖拽某一UI对象到另外一个UI对象(中心))
    d(text="Settings").drag.to(text="Clock", steps=50)

    从UI对象的中心滑动到UI对象的边缘
    滑动支持4个方向:
    left
    right
    top
    bottom

    d(text="Settings").swipe.right()
    d(text="Settings").swipe.left(steps=10)
    d(text="Settings").swipe.up(steps=10)
    d(text="Settings").swipe.down()

    模拟两点同时移动

    d(text="Settings").gesture((sx1, sy1), (sx2, sy2)) 
                      .to((ex1, ey1), (ex2, ey2))

    模拟两点在特定的UI对象的上的操作
    In,从边到中心
    Out,从中心到边缘

    # notes : pinch can not be set until Android 4.3.
    (注:不能用于Android4.3以下的版本)
    # from edge to center. here is "In" not "in"
    (从边缘到中心这里使用的是“In”不是“in”)
    d(text="Settings").pinch.In(percent=100, steps=10)
    # from center to edge
    (从中心到边缘)
    d(text="Settings").pinch.Out()

    同时三点手势模拟

    d().gestureM((sx1, sy1), (sx2, sy2),(sx3, sy3)) 
                      .to((ex1, ey1), (ex2, ey2),(ex3,ey3))
    d().gestureM((100,200),(300,200),(600,200),(100,600),(300,600),(600,900))

    等待特定ui对象出现或消失

    # wait until the ui object appears
    (等待UI对象出现)
    d(text="Settings").wait.exists(timeout=3000)
    # wait until the ui object gone
    (等待UI对象消失)
    d(text="Settings").wait.gone(timeout=1000)

    对特定的UI对象执行抛出操作(可滚动)
    可能的属性

    horiz or vert
    (水平或者是垂直的)
    forward or backward or toBeginning or toEnd
    (向前或向后,去开始位置或者去结束位置)

    # fling forward(default) vertically(default) 
    (向前抛出(默认),在垂直方向(默认))
    d(scrollable=True).fling()
    # fling forward horizentally
    (向前抛出,在水平方向)
    d(scrollable=True).fling.horiz.forward()
    # fling backward vertically
    (向后抛出,在垂直方向)
    d(scrollable=True).fling.vert.backward()
    # fling to beginning horizentally
    (向开始位置抛出,在水平方向)
    d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
    # fling to end vertically
    (向结束方向抛出,在垂直方向)
    d(scrollable=True).fling.toEnd()

    在特定的UI对象上滚动(可滚动)
    horiz or vert
    (水平或者是垂直的)
    forward or backward or toBeginning or toEnd,or to
    (向前或向后,去开始位、结束位和特定位置)

    # scroll forward(default) vertically(default)
    (向前滚动(默认),在垂直方向(默认))
    d(scrollable=True).scroll(steps=10)
    # scroll forward horizentally
    (向前滚动,在水平方向)
    d(scrollable=True).scroll.horiz.forward(steps=100)
    # scroll backward vertically
    (向后滚动,在垂直方向)
    d(scrollable=True).scroll.vert.backward()
    # scroll to beginning horizentally
    (向开始位置滚动,在垂直方向)
    d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)
    # scroll to end vertically
    (向结束方向滚动,在垂直方向)
    d(scrollable=True).scroll.toEnd()
    # scroll forward vertically until specific ui object appears
    (向前滚动,直到特定的UI对象出现)
    d(scrollable=True).scroll.to(text="Security")

    Contribution

    Fork the repo, and clone to your computer.
    Checkout a new branch from develop branch
    Install requirements: pip install -r requirements.txt
    Make your changes, and update tests. Don't forget adding your name at the end of 'Contributors' section
    Pass all tests and your code must be covered: tox.
    Commit your changes and submit pull request to develop branch.
    

    贡献者

    Xiaocong He (@xiaocong)
    Yuanyuan Zou (@yuanyuan)
    Qian Jin (@QianJin2013)
    Xu Jingjie (@xiscoxu)
    Xia Mingyuan (@mingyuan-xia)
    

    问题和讨论

    如果您有任何错误报告或问题,烦请提交到 github issues。

    备注:
    Android的uiautomator适用于Android 4.1及以上版本,所以在使用它之前,请确保你的设备是Android4.1 +。
    有些方法仅工作在Android 4.2 / 4.3,所以你最好在使用之前详细阅读uiautomator的Java文档。
    该模块采用uiautomator-jsonrpc服务器作为后台程序与设备进行通信。
    该模块仅在python2.7 / 3.2 / 3.3 / pypy上测试。

    FAQ

    1.无法启动JSONRPC服务器: raise IOError(“RPC server not started!”)

    它可能是由网络,设备或环境引起的。因此,当您遇到此问题,请按照以下步骤,尝试手动启动JSONRPC服务器。

    1)从下载jar文件uiautomator jsonrpc服务器。

    2)Adb将下载的jar文件推送到 /data/local/tmp/

    3)通过命令启动jsonrpc服务器:

    adb shell uiautomator runtest bundle.jar uiautomator-stub.jar -c com.github.uiautomatorstub.Stub

    4)adb将本地端口转发到设备端口:

    adb forward tcp:9008 tcp:9008

    5)检查jsonrpc服务器是否正常

    如果你看到类似以下这样的信息:

    {“jsonrpc”:”2.0”,”id”:1,”result”:
    {“currentPackageName”:”android”,”displayHeight”:1280,”displayRotation”:0,”displaySizeDpX”:0,”displaySizeDpY”:0,
    “displayWidth”:720,”productName”:”falcon”,”sdkInt”:17,”naturalOrientation”:true}}
    则表示服务器已启动。

    如果你可以手动启动jsonrpc服务器,但你的脚本总是提示IOError(“RPC server not started!”),
    请提交问题 github issues。

    错误 httplib.BadStatusLine: ”

    JsonRPC服务器需要访问设备上的临时目录,但在一些底层设备上,它有可能抛出一些错误,在访问临时文件没有连接SD卡。因此,如果您遇到错误,请插入SD卡,然后重试。

  • 相关阅读:
    sql注入式攻击的原理及实例分析 (转载)
    java中静态初始化块、初始化块和构造方法的理解 (转载)
    Java Web应用开发中的一些概念(转载)
    Map.Entry的使用(转载)
    java对象的存储位置(转载)-- 结合上一篇一起学习
    深入Java对象及元素的存储区域(转载)
    Java 面试题问与答:编译时与运行时(转载)看了一遍还不是很懂 先收着
    Liferay应用程序模板如何获取自定义结构的字段值
    FreeMarker 快速入门
    Tomcat9.x配置规范
  • 原文地址:https://www.cnblogs.com/wanghzh/p/9372021.html
Copyright © 2011-2022 走看看