Android Uiautomator2 python Wrapper 这是一个可以完成Android的UI自动化的python库。 该项目还在火热的开发中
公司专注于为企业提供网站设计制作、成都做网站、微信公众号开发、商城建设,小程序设计,软件定制网站开发等一站式互联网企业服务。凭借多年丰富的经验,我们会仔细了解各客户的需求而做出多方面的分析、设计、整合,为客户设计出具风格及创意性的商业解决方案,成都创新互联公司更提供一系列网站制作和网站推广的服务。
google提供的uiautomator库功能做起安卓自动化来非常强大,唯独有两个缺点:1. 只能在手机上运行 2. 只能使用java语言。 所以为了能更简单快捷的使用uiautomator,这个项目通过在手机上运行了一个http服务的方法,将uiautomator中的函数开放了出来。然后再将这些http接口,封装成了python库。这里要非常感谢 Xiaocong He ( @xiaocong ),他将这个想法实现了出来,uiautomator2这个项目则是对原有xiaocong的项目 uiautomator 进行了bug的修改,功能进行了加强。具体有以下
虽然我说的很简单,但是实现起来用到了很多的技术和技巧,功能非常强,唯独文档有点少。哈哈
Install python library
# Since uiautomator2 is still developing, you have to add --pre to install development version pip install --pre uiautomator2 # Or you can install from source git clone https://github.com/openatx/uiautomator2 pip install -e uiautomator2
Optional, used in screenshot()
pip install pillow
Push and install (apk, atx-agent, minicap, minitouch) to device
电脑连接上一个手机或多个手机, 确保adb已经添加到环境变量中,执行下面的命令会自动安装 uiautomator-apk 以及 atx-agent
python -m uiautomator2 init
安装提示 success 即可
下文中我们用 device_ip 这个变量来定义手机的IP,通常来说安装完 atx-agent 的时候会自动提示你手机的IP是多少。
如果手机的WIFI跟电脑不是一个网段的,需要先通过数据线将手机连接到电脑上,使用命令 adb forward tcp:7912 tcp:7912 将手机上的服务端口7912转发到PC上。这个时候连接地址使用 127.0.0.1 即可。
init: 初始化设备的atx-agent等
Installation部分已经介绍过,这里就不写了
install: 通过URL安装应用
$ python -m uiautomator2 install $device_ip https://example.org/some.apk MainThread: 15:37:55,731 downloading 80.4 kB / 770.6 kB MainThread: 15:37:56,763 installing 770.6 kB / 770.6 kB MainThread: 15:37:58,780 success installed 770.6 kB / 770.6 kB
clear-cache: 清空缓存
$ python -m uiautomator2 clear-cache
app-stop-all : 停止所有应用
$ python -m uiautomator2 app-stop-all $device_ip
Open python, input with the following code
There are two ways to connect to the device.
import uiautomator2 as u2 d = u2.connect('10.0.0.1') # same as call with u2.connect_wifi('10.0.0.1') print(d.info)
import uiautomator2 as u2 d = u2.connect('123456f') # same as call with u2.connect_usb('123456f') print(d.info)
If just call u2.connect() with no arguments, env-var ANDROID_DEVICE_IP will first check. if env-var is empty, connect_usb will be called. you need to make sure there is only one device connected with your computer.
先中文写着了,国外大佬们先用Google Translate顶着
d.healthcheck()
需要设备曾经使用 python -muiautomator2 init 初始化过
d = u2.connect_usb("{Your-Device-Serial}")
10s内如果出现Skip则点击
clicked = d(text='Skip').click_exists(timeout=10.0)
用于开发者或有经验的使用者定位问题
>>> 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
Notes:In below examples, we use d represent the uiautomator2 connect object
This part show the normal actions of the device through some simple examples
d.info
Below is a possible result:
{ 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 }
Tun on/off screen
d.screen_on() # turn on screen d.screen_off() # turn off screen
Get screen on/off status
d.info.get('screenOn') # require android >= 4.4
Press hard/soft key
d.press("home") # press home key d.press("back") # the normal way to press back key d.press(0x07, 0x02) # press keycode 0x07('0') with META ALT(0x02)
Next keys are currently supported:
You can find all key code definitions at Android KeyEvnet
Unlock screen
d.unlock() # 1. launch activity: com.github.uiautomator.ACTION_IDENTIFY # 2. press "home"
Click the screen
d.click(x, y)
Long click the screen
d.long_click(x, y) d.long_click(x, y, 0.5) # long click 0.5s (default)
Swipe
d.swipe(sx, sy, ex, ey) d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
Drag
d.drag(sx, sy, ex, ey) d.drag(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
Note: click, swipe, drag support percent position. Example:
d.long_click(0.5, 0.5) means long click center of screen
Retrieve/Set Orientation
The possible orientation is:
# retrieve orientation, it may 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: "upsidedown" can not be set until 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/Un-Freeze rotation
# freeze rotation d.freeze_rotation() # un-freeze rotation d.freeze_rotation(False)
Take screenshot
# take screenshot and save to local file "home.jpg", can not work until Android 4.2. d.screenshot("home.jpg") # get PIL.Image format, need install pillow first image = d.screenshot() image.save("home.jpg") # or home.png # get opencv format, need install numpy and cv2 import cv2 image = d.screenshot(format='opencv') cv2.imwrite('home.jpg', image)
Dump Window Hierarchy
# or get the dumped content(unicode) from return. xml = d.dump_hierarchy()
Open notification or quick settings
d.open_notification() d.open_quick_settings()
push file into device
# push into 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 mode d.push("foo.sh", "/data/local/tmp/", mode=0o755)
pull file from device
d.pull("/sdcard/tmp.txt", "tmp.txt") # FileNotFoundError will raise if file not found in device d.pull("/sdcard/some-file-not-exists.txt", "tmp.txt")
Include app install, launch and stop
Only support install from url for now.
d.app_install('http://some-domain.com/some.apk')
d.app_start("com.example.hello_world") # start with package name
# perform am force-stop d.app_stop("com.example.hello_world") # perform pm clear d.app_clear('com.example.hello_world')
# stop all d.app_stop_all() # stop all app except com.examples.demo d.app_stop_all(excludes=['com.examples.demo'])
Selector is to identify specific ui object in current window.
# To seleted the object ,text is 'Clock' and its className is 'android.widget.TextView' d(text='Clock', className='android.widget.TextView')
Selector supports below parameters. Refer to UiSelector java doc for detailed information.
child
# get the child or grandchild d(className="android.widget.ListView").child(text="Bluetooth")
sibling
# get sibling or child of sibling d(text="Google").sibling(className="android.widget.ImageView")
child by text or description or instance
# get the child match className="android.widget.LinearLayout" # and also it or its child or grandchild contains text "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 is to find child which or which's grandchild contains the specified description, others are the same as child_by_text .
child_by_instance is to find child which has a child UI element anywhere within its sub hierarchy that is at the instance specified. It is performed on visible views without scrolling.
See below links for detailed information:
Above methods support chained invoking, e.g. for below hierarchy
...
We want to click the switch at the right side of text 'Wi‑Fi' to turn on/of Wi‑Fi. As there are several switches with almost the same properties, so we can not use like d(className="android.widget.Switch") to select the ui object. Instead, we can use code below to select it.
d(className="android.widget.ListView", resourceId="android:id/list") \ .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \ .child(className="android.widget.Switch") \ .click()
relative position
Also we can use the relative position methods to get the view: left , right , top , bottom.
So for above case, we can write code alternatively:
## select "switch" on the right side of "Wi‑Fi" d(text="Wi‑Fi").right(className="android.widget.Switch").click()
Multiple instances
Sometimes the screen may contain multiple views with the same e.g. text, then you will have to use "instance" properties in selector like below:
d(text="Add new", instance=0) # which means the first instance with text "Add new"
However, uiautomator provides list like methods to use it.
# 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 # ...
Notes: when you are using selector like a list, you must make sure the screen keep unchanged, else you may get ui not found error.
Check if the specific ui object exists
d(text="Settings").exists # True if exists, else False d.exists(text="Settings") # alias of above property.
Retrieve the info of the specific ui object
d(text="Settings").info
Below is a possible result:
{ 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 }
Set/Clear text of editable field
d(text="Settings").clear_text() # clear the text d(text="Settings").set_text("My text...") # set the text
Perform click on the specific ui object
# click on the center of the specific ui object d(text="Settings").click() # wait element show for 10 seconds(Default) d(text="Settings").click(timeout=10) # alias of click # short name for quick type with keyboard d(text="Settings").tap() # wait element show for 0 seconds d(text="Settings").tap_nowait()
Perform long click on the specific ui object
# long click on the center of the specific ui object d(text="Settings").long_click()
Drag the ui object to another point or ui object
# notes : drag can not be set until Android 4.3. # drag the ui object to point (x, y) d(text="Settings").drag_to(x, y, duration=0.5) # drag the ui object to another ui object(center) d(text="Settings").drag_to(text="Clock", duration=0.25)
Two point gesture from one point to another
d(text="Settings").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2))
Two point gesture on the specific ui object
Supports two gestures:
# 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()
Wait until the specific ui appears or gone
# 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)
Default timeout is 20s. see global settings for more details
Perform fling on the specific ui object(scrollable)
Possible properties:
# 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()
Perform scroll on the specific ui object(scrollable)
Possible properties:
# 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 d(scrollable=True).scroll.to(text="Security")
You can register watcher to perform some actions when a selector can not find a match.
Register Watcher
When a selector can not find a match, uiautomator will run all registered watchers.
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("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(, ..., .() ## press keys one by one in sequence.
Check if the named watcher triggered
A watcher is triggered, which means the watcher was run and all its conditions matched.
d.watcher("watcher_name").triggered # true in case of the specified watcher triggered, else false
Remove named watcher
# remove the watcher d.watcher("watcher_name").remove()
List all watchers
d.watchers # a list of all registered wachers' names
Check if there is any watcher triggered
d.watchers.triggered # true in case of any watcher triggered
Reset all triggered watchers
# reset all triggered watchers, after that, d.watchers.triggered will be false. d.watchers.reset()
Remvoe watchers
# 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 watchers
# force to run all registered watchers d.watchers.run()
另外文档还是有很多没有写,推荐直接去看源码 init .py
# 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
这种方法通常用于不知道控件的情况下的输入。第一步需要切换输入法,然后发送adb广播命令,具体使用方法如下
d.set_fastinput_ime(True) # 切换成FastInputIME输入法 d.send_keys("你好123abcEFG") # adb广播输入 d.set_fastinput_ime(False) # 切换成正常的输入法
$ adb forward tcp:9008 tcp:9008 $ curl 127.0.0.1:9008/ping # expect: pong $ curl -d '{"jsonrpc":"2.0","method":"deviceInfo","id":1}' 127.0.0.1:9008/jsonrpc/0 # expect JSON output
提示 502 错误
尝试手机连接PC,然后运行下面的命令
adb shell am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub \ com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner
如果运行正常,启动测试之前增加一行代码 d.healthcheck()
如果报错,可能是缺少某个apk没有安装,使用下面的命令重新初始化 python -m uiautomator2 init --reinstall
手机 python -muiautomator2 init 之后,浏览器输入 <手机IP:7912>,会发现一个远程控制功能,延迟非常低噢。^_^
项目重构自 https://github.com/openatx/atx-uiautomator
Auto generated by pbr: CHANGELOG
Others contributors
Under MIT
新闻标题:可以完成AndroidUI自动化的Python库
文章出自:http://www.shufengxianlan.com/qtweb/news22/17172.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联