zoukankan      html  css  js  c++  java
  • ATX uiautomator2 README 学习

    连接设备

    有多种方式可以连接

    • wifi 首先要保证你的手机(10.0.0.1)和电脑在同一个局域网内
    import uiautomator2 as u2

    d = u2.connect('10.0.0.1') # alias for u2.connect_wifi('10.0.0.1')
    print(d.info)
    • usb
      usb通过设备号连接,可以通过adb devices 查看
    import uiautomator2 as u2

    d = u2.connect('123456f') # alias for u2.connect_usb('123456f')
    print(d.info)
    • adb wifi
    import uiautomator2 as u2

    d = u2.connect_adb_wifi("10.0.0.1:5555")

    # Equals to
    # + Shell: adb connect 10.0.0.1:5555
    # + Python: u2.connect_usb("10.0.0.1:5555")

    全局设置

    里面包含一些u2全局设置的属性

    • Http debug
    >>> d.debug = True
    >>> d.info
    12:32:47.182 $ curl -X POST -d '{"jsonrpc": "2.0", "id": "b80d3a488580be1f3e9cb3e926175310", "method": "deviceInfo", "params": {}}' 'http://127.0.0.1:54179/jsonrpc/0'
    12:32:47.225 Response >>>
    {"jsonrpc":"2.0","id":"b80d3a488580be1f3e9cb3e926175310","result":{"currentPackageName":"com.android.mms","displayHeight":1920,"displayRotation":0,"displaySizeDpX":360,"displaySizeDpY":640,"displayWidth":1080,"productName"
    :"odin","screenOn":true,"sdkInt":25,"naturalOrientation":true}}
    <<< END
    • 默认等待超时时间
    d.implicitly_wait(10.0)
    d(text="Settings").click() # if Settings button not show in 10s, UiObjectNotFoundError will raised

    print("wait timeout", d.implicitly_wait()) # get default implicit wait

    将会作用在这些方法中 clicklong_clickdrag_toget_text, set_textclear_text,等.

    app管理

    • 安装
    d.app_install('http://some-domain.com/some.apk')
    • 唤起app
    # 默认的这种方法是先通过atx-agent解析apk包的mainActivity,然后调用am start -n $package/$activity启动
    d.app_start("com.example.hello_world")

    # 使用 monkey -p com.example.hello_world -c android.intent.category.LAUNCHER 1 启动
    # 这种方法有个附带的问题,它自动会将手机的旋转锁定给关掉
    d.app_start("com.example.hello_world", use_monkey=True) # start with package name

    # 通过指定main activity的方式启动应用,等价于调用am start -n com.example.hello_world/.MainActivity
    d.app_start("com.example.hello_world", ".MainActivity")
    • 停止app
    # equivalent to `am force-stop`, thus you could lose data
    d.app_stop("com.example.hello_world")
    # equivalent to `pm clear`
    d.app_clear('com.example.hello_world')
    • 停止所有运行的app
    # stop all
    d.app_stop_all()
    # stop all app except for com.examples.demo
    d.app_stop_all(excludes=['com.examples.demo'])
    • 获取app信息
    d.app_info("com.examples.demo")
    # expect output
    #{
    # "mainActivity": "com.github.uiautomator.MainActivity",
    # "label": "ATX",
    # "versionName": "1.1.7",
    # "versionCode": 1001007,
    # "size":1760809
    #}

    # save app icon
    img = d.app_icon("com.examples.demo")
    img.save("icon.png")
    • 获取所有正在运行的app
    d.app_list_running()
    # expect output
    # ["com.xxxx.xxxx", "com.github.uiautomator", "xxxx"]
    • 等待app运行
    pid = d.app_wait("com.example.android") # 等待应用运行, return pid(int)
    if not pid:
    print("com.example.android is not running")
    else:
    print("com.example.android pid is %d" % pid)

    d.app_wait("com.example.android", front=True) # 等待应用前台运行
    d.app_wait("com.example.android", timeout=20.0) # 最长等待时间20s(默认)
    • 传文件到设备
    # push to a folder
    d.push("foo.txt", "/sdcard/")
    # push and rename
    d.push("foo.txt", "/sdcard/bar.txt")
    # push fileobj
    with open("foo.txt", 'rb') as f:
    d.push(f, "/sdcard/")
    # push and change file access mode
    d.push("foo.sh", "/data/local/tmp/", mode=0o755)
    • 从设备拉取一个文件到电脑
    d.pull("/sdcard/tmp.txt", "tmp.txt")

    # FileNotFoundError will raise if the file is not found on the device
    d.pull("/sdcard/some-file-not-exists.txt", "tmp.txt")
    • 设备健康检查
    d.healthcheck()

    常见api使用

    介绍如何操作设备

    shell命令

    • 具有超时保护的shell 命令(默认60s)
    output, exit_code = d.shell("pwd", timeout=60) # timeout 60s (Default)
    # output: "/ ", exit_code: 0
    # Similar to command: adb shell pwd

    # Since `shell` function return type is `namedtuple("ShellResponse", ("output", "exit_code"))`
    # so we can do some tricks
    output = d.shell("pwd").output
    exit_code = d.shell("pwd").exit_code

    # The first argument can be list. for example
    output, exit_code = d.shell(["ls", "-l"])
    # output: "/....", exit_code: 0

    该 adb shell会阻塞,直到有结果活超时,不适合长时间使用

    • 长期运行的shell
    r = d.shell("logcat", stream=True)
    # r: requests.models.Response
    deadline = time.time() + 10 # run maxium 10s
    try:
    for line in r.iter_lines(): # r.iter_lines(chunk_size=512, decode_unicode=None, delimiter=None)
    if time.time() > deadline:
    break
    print("Read:", line.decode('utf-8'))
    finally:
    r.close() # this method must be called

    r.close() 时,命令将会被终止

    会话

    会话代表应用程序生命周期。 可用于启动应用程序,检测应用程序崩溃。

    • 唤起和关闭app

      sess = d.session("com.netease.cloudmusic") # start 网易云音乐
      sess.close() # 停止网易云音乐
      sess.restart() # 冷启动网易云音乐
    • 使用pythonwith

    with d.session("com.netease.cloudmusic") as sess:
    sess(text="Play").click()
    • 连接运行的app
    # launch app if not running, skip launch if already running
    #如果app没有运行将会唤起,运行则会连接
    sess = d.session("com.netease.cloudmusic", attach=True)

    # raise SessionBrokenError if not running
    # 异常在app不在运行的时候
    sess = d.session("com.netease.cloudmusic", attach=True, strict=True)

    • 检测app崩溃
    # When app is still running
    sess(text="Music").click() # operation goes normal

    # If app crash or quit
    #异常在app不在运行的时候
    sess(text="Music").click() # raise SessionBrokenError
    # other function calls under session will raise SessionBrokenError too

    # check if session is ok.
    # Warning: function name may change in the future
    sess.running() # True or False

    获取设备信息

    • 基本信息 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.device_info
    {'udid': '3578298f-b4:0b:44:e6:1f:90-OD103',
    'version': '7.1.1',
    'serial': '3578298f',
    'brand': 'SMARTISAN',
    'model': 'OD103',
    'hwaddr': 'b4:0b:44:e6:1f:90',
    'port': 7912,
    'sdk': 25,
    'agentVersion': 'dev',
    'display': {'width': 1080, 'height': 1920},
    'battery': {'acPowered': False,
    'usbPowered': False,
    'wirelessPowered': False,
    'status': 3,
    'health': 0,
    'present': True,
    'level': 99,
    'scale': 100,
    'voltage': 4316,
    'temperature': 272,
    'technology': 'Li-ion'},
    'memory': {'total': 3690280, 'around': '4 GB'},
    'cpu': {'cores': 8, 'hardware': 'Qualcomm Technologies, Inc MSM8953Pro'},
    'presenceChangedAt': '0001-01-01T00:00:00Z',
    'usingBeganAt': '0001-01-01T00:00:00Z'}
    • 屏幕大小

      print(d.window_size())
      # device upright output example: (1080, 1920)
      # device horizontal output example: (1920, 1080)
    • 运行的app

    print(d.app_current())
    # Output example 1: {'activity': '.Client', 'package': 'com.netease.example', 'pid': 23710}
    # Output example 2: {'activity': '.Client', 'package': 'com.netease.example'}
    # Output example 3: {'activity': None, 'package': None}
    • 等待activity
    d.wait_activity(".ApiDemos", timeout=10) # default timeout 10.0 seconds
    # Output: true of false
    • 获取设备号
    print(d.serial)
    # output example: 74aAEDR428Z9
    • 局域网ip
    print(d.wlan_ip)
    # output example: 10.0.0.1

    键盘事件

    • 打开/关闭屏幕
    d.screen_on() # turn on the screen
    d.screen_off() # turn off the screen
    • 获取屏幕状态
    d.info.get('screenOn') # require Android >= 4.4
    • 点击按键 d.press("home") # press the home key, with key name d.press("back") # press the back key, with key name d.press(0x07, 0x02) # press keycode 0x07('0') with META ALT(0x02)
    • 当前支持以下键名:
      • home
      • back
      • left
      • right
      • up
      • down
      • center
      • menu
      • search
      • enter
      • delete ( or del)
      • recent (recent apps)
      • volume_up
      • volume_down
      • volume_mute
      • camera
      • power

    更多key code定义参考Android KeyEvent

    • 解锁 python d.unlock() # This is equivalent to # 1. launch activity: com.github.uiautomator.ACTION_IDENTIFY # 2. press the "home" key

    与设备的手势交互

    • 单击

      d.click(x, y)
    • 双击

      d.double_click(x, y)
      d.double_click(x, y, 0.1) # default duration between two click is 0.1s
    • 长按

      d.long_click(x, y)
      d.long_click(x, y, 0.5) # long click 0.5s (default)
    • 滑动

      d.swipe(sx, sy, ex, ey)
      d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
    • 滑动扩展功能

      d.swipe_ext("right") # 屏幕右滑,41 "left", "right", "up", "bottom"
      d.swipe_ext("right", scale=0.9) # 默认0.9, 滑动距离为屏幕宽度的90%
      d.swipe_ext("right", box=(0, 0, 100, 100)) # (0,0) -> (100, 100) 这个区域做滑动

    * 拖拽

    ```python
    d.drag(sx, sy, ex, ey)
    d.drag(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
    • 滑动解锁

      # swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2)
      # time will speed 0.2s bwtween two points
      d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2))

      多用于九宫格解锁,提前获取到每个点的相对坐标(这里支持百分比), 更详细的使用参考这个帖子 使用u2实现九宫图案解锁

    • 按着拖动

      这个接口属于比较底层的原始接口,感觉并不完善,不过凑合能用。注:这个地方并不支持百分比

    d.touch.down(10, 10) # 模拟按下
    time.sleep(.01) # down move 之间的延迟,自己控制
    d.touch.move(15, 15) # 模拟移动
    d.touch.up() # 模拟抬起

    注: clickswipedrag 这几个操作支持百分比

    屏幕相关

    • 设置屏幕方向
      • natural or n
      • left or l
      • right or r
      • upsidedown or u (can not be set)
    # retrieve orientation. the output could be "natural" or "left" or "right" or "upsidedown"
    orientation = d.orientation

    # WARNING: not pass testing in my TT-M1
    # set orientation and freeze rotation.
    # notes: setting "upsidedown" requires Android>=4.3.
    d.set_orientation('l') # or "left"
    d.set_orientation("l") # or "left"
    d.set_orientation("r") # or "right"
    d.set_orientation("n") # or "natural"
    • 屏幕旋转
    # freeze rotation
    d.freeze_rotation()
    # un-freeze rotation
    d.freeze_rotation(False)
    • 截屏
    # take screenshot and save to a file on the computer, require Android>=4.2.
    d.screenshot("home.jpg")

    # get PIL.Image formatted images. Naturally, you need pillow installed first
    image = d.screenshot() # default format="pillow"
    image.save("home.jpg") # or home.png. Currently, only png and jpg are supported

    # get opencv formatted images. Naturally, you need numpy and cv2 installed first
    import cv2
    image = d.screenshot(format='opencv')
    cv2.imwrite('home.jpg', image)

    # get raw jpeg data
    imagebin = d.screenshot(format='raw')
    open("some.jpg", "wb").write(imagebin)
    • dump 视图结构
    # get the UI hierarchy dump content (unicoded).
    xml = d.dump_hierarchy()
    • 打开通知和设置
    d.open_notification()
    d.open_quick_settings()

    选择器

    选择器是一种灵活的机制用户标识一个特殊的ui对象

    # Select the object with text 'Clock' and its className is 'android.widget.TextView'
    d(text='Clock', className='android.widget.TextView')

    注: 可以使用d.info输出定位的控件信息,来确认是否正确

    选择器支持以下参数,更多参考UiSelector java文档

    • texttextContainstextMatchestextStartsWith
    • classNameclassNameMatches
    • descriptiondescriptionContainsdescriptionMatchesdescriptionStartsWith
    • checkablecheckedclickablelongClickable
    • scrollableenabled,focusablefocusedselected
    • packageNamepackageNameMatches
    • resourceIdresourceIdMatches
    • indexinstance

    子和兄弟控件(Children and siblings)

    • children
    # get the children or grandchildren
    d(className="android.widget.ListView").child(text="Bluetooth")
    • siblings (兄弟)
    # get siblings
    d(text="Google").sibling(className="android.widget.ImageView")
    • text description instance 定位子view
    # get the child matching the condition className="android.widget.LinearLayout"
    # and also its children or grandchildren with text "Bluetooth"
    d(className="android.widget.ListView", resourceId="android:id/list")
    .child_by_text("Bluetooth", className="android.widget.LinearLayout")

    # get children by allowing scroll search
    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_instance 和 child_by_instance差不多,只是条件换了,但是都是查找屏幕内的view

    例子dom信息如下:

    <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="WiFi" 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>
     

    这里有一个例子来说明如何使用

    注: 不同版本的settings可能实现不一样,例如6.0的就不是ListView

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

      • d(A).left(B), selects B on the left side of A.
      • d(A).right(B), selects B on the right side of A.
      • d(A).up(B), selects B above A.
      • d(A).down(B), selects B under A.
    ## select "switch" on the right side of "Wi‑Fi"
    d(text="Wi‑Fi").right(className="android.widget.Switch").click()
    • 多个实例
      有时候界面上会有多个view具有相同的属性,例如 text,这时候你可以使用instance来选择你需要的实例
    d(text="Add new", instance=0)  # which means the first instance with text "Add new"

    更多api信息 list-like API (similar to jQuery):

    # get the count of views with text "Add new" on current screen
    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 # ...

    注:在遍历list的时候要保证界面是不变化的,不然发生错误

    ui对象状态和信息

    • 检查ui是否存在
    d(text="Settings").exists # True if exists, else False
    d.exists(text="Settings") # alias of above property.

    # advanced usage
    d(text="Settings").exists(timeout=3) # wait Settings appear in 3s, same as .wait(3)

    • 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").get_text()  # get widget text
    d(text="Settings").set_text("My text...") # set the text
    d(text="Settings").clear_text() # clear the text
    • 获取控件中心点
    x, y = d(text="Settings").center()
    # x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, y

    点击选中的ui对象

    • 单击
    # click on the center of the specific ui object
    d(text="Settings").click()

    # wait element to appear for at most 10 seconds and then click
    d(text="Settings").click(timeout=10)

    # click with offset(x_offset, y_offset)
    # click_x = x_offset * width + x_left_top
    # click_y = y_offset * height + y_left_top
    d(text="Settings").click(offset=(0.5, 0.5)) # Default center
    d(text="Settings").click(offset=(0, 0)) # click left-top
    d(text="Settings").click(offset=(1, 1)) # click right-bottom

    # click when exists in 10s, default timeout 0s
    clicked = d(text='Skip').click_exists(timeout=10.0)

    # click until element gone, return bool
    is_gone = d(text="Skip").click_gone(maxretry=10, interval=1.0) # maxretry default 10, interval default 1.0

    • 长按
    # long click on the center of the specific UI object
    d(text="Settings").long_click()

    手势操作

    • 拖动
    # notes : drag can not be used for Android<4.3.
    # drag the UI object to a screen point (x, y), in 0.5 second
    d(text="Settings").drag_to(x, y, duration=0.5)
    # drag the UI object to (the center position of) another UI object, in 0.25 second
    d(text="Settings").drag_to(text="Clock", duration=0.25)
    • 滑动

      支持四个方向

      • left
      • right
      • top
      • bottom
      d(text="Settings").swipe("right")
      d(text="Settings").swipe("left", steps=10)
      d(text="Settings").swipe("up", steps=20) # 1 steps is about 5ms, so 20 steps is about 0.1s
      d(text="Settings").swipe("down", steps=20)
    • 双指滑动

    d(text="Settings").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2))
    • Two-point gesture on the specific UI object 不晓得怎么表达

    Supports two gestures:

    • In, from edge to center
    • Out, from center to edge
    # notes : pinch can not be set until Android 4.3.
    # from edge to center. here is "In" not "in"
    d(text="Settings").pinch_in(percent=100, steps=10)
    # from center to edge
    d(text="Settings").pinch_out()
    • 等待ui出现活消失

      # wait until the ui object appears
      d(text="Settings").wait(timeout=3.0) # return bool
      # wait until the ui object gone
      d(text="Settings").wait_gone(timeout=1.0)
    • fling指定的ui(scrollable)

      • horiz or vert(横竖)
      • forward or backward or toBeginning or toEnd (前后)
    # fling forward(default) vertically(default) 
    d(scrollable=True).fling()
    # fling forward horizontally
    d(scrollable=True).fling.horiz.forward()
    # fling backward vertically
    d(scrollable=True).fling.vert.backward()
    # fling to beginning horizontally
    d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
    # fling to end vertically
    d(scrollable=True).fling.toEnd()
    • 滑动指定的ui对象(scrollable)

    Possible properties:

    • 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 horizontally
    d(scrollable=True).scroll.horiz.forward(steps=100)
    # scroll backward vertically
    d(scrollable=True).scroll.vert.backward()
    # scroll to beginning horizontally
    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
    d(scrollable=True).scroll.to(text="Security")

    观察者

    当选择器找不到指定对象的时候,你可以注册观察者来执行一些操作

    • 注册
    d.watcher("AUTO_FC_WHEN_ANR").when(text="ANR").when(text="Wait") 
    .click(text="Force Close")
    # d.watcher(name) ## creates a new named watcher.
    # .when(condition) ## the UiSelector condition of the watcher.
    # .click(target) ## perform click action on the target UiSelector.

    #
    d.watcher("ALERT").when(text="OK").click()
    # Same as
    d.watcher("ALERT").when(text="OK").click(text="OK")

    # 多条件
    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.
    # .press(<keyname>, ..., <keyname>.() ## press keys one by one in sequence.

    • 检查是否触发
    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 watchers
    • 检查是否触发,任意一个
    d.watchers.triggered
    # true in case of any watcher triggered
    • 重置
    # reset all triggered watchers, after that, d.watchers.triggered will be false.
    d.watchers.reset()

    • 移除
    # remove all registered watchers
    d.watchers.remove()
    # remove the named watcher, same as d.watcher("watcher_name").remove()
    d.watchers.remove("watcher_name")
    • 强制运行所有
    # force to run all registered watchers
    d.watchers.run()

    另外文档还是有很多没有写,推荐直接去看源码init.py

    现在推荐使用这种观察者来处理弹窗等

    d.xpath.when(“服务条款%).when(“同意”).click() d.xpath.when(“立即体验”).click() d.xpath.when(“跳过”).click()
    d.xpath.run_watchers() # 运行一次弹窗检查 d.xpath.watch_background(2.0)

    全局设置

    # set delay 1.5s after each UI click and click, 点击延迟
    d.click_post_delay = 1.5 # default no delay

    # set default element wait timeout (seconds) 默认超时时间
    d.wait_timeout = 30.0 # default 20.0

    UiAutomator中的超时设置(隐藏方法)

    >> d.jsonrpc.getConfigurator() 
    {'actionAcknowledgmentTimeout': 500,
    'keyInjectionDelay': 0,
    'scrollAcknowledgmentTimeout': 200,
    'waitForIdleTimeout': 0,
    'waitForSelectorTimeout': 0}

    >> d.jsonrpc.setConfigurator({"waitForIdleTimeout": 100})
    {'actionAcknowledgmentTimeout': 500,
    'keyInjectionDelay': 0,
    'scrollAcknowledgmentTimeout': 200,
    'waitForIdleTimeout': 100,
    'waitForSelectorTimeout': 0}

    Input method

    这种方法通常用于不知道控件的情况下的输入。第一步需要切换输入法,然后发送adb广播命令,具体使用方法如下

    d.set_fastinput_ime(True) # 切换成FastInputIME输入法
    d.send_keys("你好123abcEFG") # adb广播输入
    d.clear_text() # 清除输入框所有内容(Require android-uiautomator.apk version >= 1.0.7)
    d.set_fastinput_ime(False) # 切换成正常的输入法
    d.send_action("search") # 模拟输入法的搜索

    send_action 说明

    该函数可以使用的参数有 go search send next done previous

    什么时候该使用这个函数呢?

    有些时候在EditText中输入完内容之后,调用press("search") or press("enter")发现并没有什么反应。
    这个时候就需要send_action函数了,这里用到了只有输入法才能用的IME_ACTION_CODE
    send_action先broadcast命令发送给输入法操作IME_ACTION_CODE,由输入法完成后续跟EditText的通信。(原理我不太清楚,有了解的,提issue告诉我)

    Toast

    Show Toast

    d.toast.show("Hello world")
    d.toast.show("Hello world", 1.0) # show for 1.0s, default 1.0s

    Get Toast

    # [Args]
    # 5.0: max wait timeout. Default 10.0
    # 10.0: cache time. return cache toast if already toast already show up in recent 10 seconds. Default 10.0 (Maybe change in the furture)
    # "default message": return if no toast finally get. Default None
    d.toast.get_message(5.0, 10.0, "default message")

    # common usage
    assert "Short message" in d.toast.get_message(5.0, default="")

    # clear cached toast
    d.toast.reset()
    # Now d.toast.get_message(0) is None

    XPath

    For example: 其中一个节点的内容

    <android.widget.TextView
    index="2"
    text="05:19"
    resource-id="com.netease.cloudmusic:id/qf"
    package="com.netease.cloudmusic"
    content-desc=""
    checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false"
    scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true"
    bounds="[957,1602][1020,1636]" />

    xpath定位和使用方法

    有些属性的名字有修改需要注意

    description -> content-desc
    resourceId -> resource-id

    常见用法

    # wait exists 10s
    d.xpath("//android.widget.TextView").wait(10.0)
    # find and click
    d.xpath("//*[@content-desc='分享']").click()
    # check exists
    if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
    print("exists")
    # get all text-view text, attrib and center point
    for elem in d.xpath("//android.widget.TextView").all():
    print("Text:", elem.text)
    # Dictionary eg:
    # {'index': '1', 'text': '999+', 'resource-id': 'com.netease.cloudmusic:id/qb', 'package': 'com.netease.cloudmusic', 'content-desc': '', 'checkable': 'false', 'checked': 'false', 'clickable': 'false', 'enabled': 'true', 'focusable': 'false', 'focused': 'false','scrollable': 'false', 'long-clickable': 'false', 'password': 'false', 'selected': 'false', 'visible-to-user': 'true', 'bounds': '[661,1444][718,1478]'}
    print("Attrib:", elem.attrib)
    # Coordinate eg: (100, 200)
    print("Position:", elem.center())

    其他XPath常见用法

    See also: https://github.com/openatx/uiautomator2/blob/master/uiautomator2/ext/xpath/README.md

    uiautomator2 是一个超级好的项目,希望大家一起参与多提pr

  • 相关阅读:
    IOS开发 网络发展史(NSURLProtocol)
    IOS开发 网络发展史(NSURLCach)
    IOS 开发 网络发展史(URLConnection)
    ios 网络开发(CFNetwork)
    MAC安装mysql
    conda 安装常用包
    从必应上拉取图片
    旋转数组中的二分查找
    东北大学 python模拟登录校园网(2019年6月启用新登录模式后)
    Ubuntu 安装Codeblocks
  • 原文地址:https://www.cnblogs.com/hwllovelq/p/11648529.html
Copyright © 2011-2022 走看看