侧边栏壁纸
博主头像
Gstory's Blog博主等级

每天进步一点点!

  • 累计撰写 108 篇文章
  • 累计创建 23 个标签
  • 累计收到 11 条评论

目 录CONTENT

文章目录

uiautomator2之使用方法

gstory
2021-10-25 / 0 评论 / 0 点赞 / 16 阅读 / 25503 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-10-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.简介

uiautomator2是一个自动化测试开源工具,仅支持android平台的自动化测试,其封装了谷歌自带的uiautomator2测试框架,可以运行在支持Python的任一系统上,目前版本为2.10.2,仓库地址

2.工作原理

python-uiautomator2主要分为两个部分,python客户端,移动设备

  • python端: 运行脚本,并向移动设备发送HTTP请求
  • 移动设备:移动设备上运行了封装了uiautomator2的HTTP服务,解析收到的请求,并转化成uiautomator2的代码。

整个过程

  1. 在移动设备上安装atx-agent(守护进程), 随后atx-agent启动uiautomator2服务(默认7912端口)进行监听
  2. 在PC上编写测试脚本并执行(相当于发送HTTP请求到移动设备的server端)
  3. 移动设备通过WIFI或USB接收到PC上发来的HTTP请求,执行制定的操作

3.安装与启动

3.1 安装uiautomator2

使用pip安装

pip install -U uiautomator2

安装完成后,使用如下python代码查看环境是事配置成功

说明:后文中所有代码都需要导入uiautomator2库,为了简化我使用u2代替,d代表driver

import uiautomator2 as u2
# 连接并启动
d = u2.connect() 
print(d.info)

注意:需要安装 adb 工具,并配置到系统环境变量,才能操作手机

安装有问题可以到https://github.com/openatx/uiautomator2/wiki/Common-issues这里查看一下有没有相同的问题

3.2 安装weditor

weditor是一款基于浏览器的UI查看器,用来帮助我们查看UI元素定位。

因为uiautomator是独占资源,所以当atx运行的时候uiautomatorviewer是不能用的,为了减少atx频繁的启停,就需要用到此工具

使用pip安装

pip install -U weditor

查看安装是否成功

weditor -v

出现如下信息表示安装成功

0.6.8

运行weditor

python -m weditor
#或者直接在命令行运行
weditor

4. 元素定位

4.1 使用方法

d(定位方式=定位值)
#例:
element = d(text='Phone')
#这里返回的是一个列表,当没找到元素时,不会报错,只会返回一个长度为0的列表
#当找到多个元素时,会返回多个元素的列表,需要加下标再定位
element[0].click()
#获取元素个数
print(element.count)

4.2 支持的定位方式

ui2支持 android 中 UiSelector 类中的所有定位方式,详细可以在这个网址查看https://developer.android.com/reference/android/support/test/uiautomator/UiSelector

整体内容如下,所有的属性可以通过weditor查看到

名称描述
texttext是指定文本的元素
textContainstext中包含有指定文本的元素
textMatchestext符合指定正则的元素
textStartsWithtext以指定文本开头的元素
classNameclassName是指定类名的元素
classNameMatchesclassName类名符合指定正则的元素
descriptiondescription是指定文本的元素
descriptionContainsdescription中包含有指定文本的元素
descriptionMatchesdescription符合指定正则的元素
descriptionStartsWithdescription以指定文本开头的元素
checkable可检查的元素,参数为True,False
checked已选中的元素,通常用于复选框,参数为True,False
clickable可点击的元素,参数为True,False
longClickable可长按的元素,参数为True,False
scrollable可滚动的元素,参数为True,False
enabled已激活的元素,参数为True,False
focusable可聚焦的元素,参数为True,False
focused获得了焦点的元素,参数为True,False
selected当前选中的元素,参数为True,False
packageNamepackageName为指定包名的元素
packageNameMatchespackageName为符合正则的元素
resourceIdresourceId为指定内容的元素
resourceIdMatchesresourceId为符合指定正则的元素

4.3 子元素和兄弟定位

子元素定位

child()

#查找类名为android.widget.ListView下的Bluetooth元素
d(className="android.widget.ListView").child(text="Bluetooth")
# 下面这两种方式定位有点不准确,不建议使用
d(className="android.widget.ListView")\
.child_by_text("Bluetooth",allow_scroll_search=True)
d(className="android.widget.ListView").child_by_description("Bluetooth")

兄弟元素定位

sibling()

#查找与google同一级别,类名为android.widget.ImageView的元素
d(text="Google").sibling(className="android.widget.ImageView")

链式调用

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

4.4 相对定位

相对定位支持在left, right, top, bottom,即在某个元素的前后左右

d(A).left(B),# 选择A左边的B
d(A).right(B),# 选择A右边的B
d(A).up(B), #选择A上边的B
d(A).down(B),# 选择A下边的B
#选择 WIFI 右边的开关按钮
d(text='Wi‑Fi').right(resourceId='android:id/widget_frame')

4.5 元素常用API

表格标注有@property装饰的类属性方法,均为下方示例方式

d(test="Settings").exists
方法描述返回值备注
exists()判断元素是否存在True,Flase@property
info()返回元素的所有信息字典@property
get_text()返回元素文本字符串
set_text(text)设置元素文本None
clear_text()清空元素文本None
center()返回元素的中心点位置(x,y)基于整个屏幕的点

exists其它使用方法:

d.exists(text='Wi‑Fi',timeout=5)

info()输出信息:

{
  "bounds": {
    "bottom": 407,
    "left": 216,
    "right": 323,
    "top": 342
  },
  "childCount": 0,
  "className": "android.widget.TextView",
  "contentDescription": null,
  "packageName": "com.android.settings",
  "resourceName": "android:id/title",
  "text": "Wi‑Fi",
  "visibleBounds": {
    "bottom": 407,
    "left": 216,
    "right": 323,
    "top": 342
  },
  "checkable": false,
  "checked": false,
  "clickable": false,
  "enabled": true,
  "focusable": false,
  "focused": false,
  "longClickable": false,
  "scrollable": false,
  "selected": false
}

可以通过上方信息分别获取元素的所有属性

4.6 XPATH定位

因为Java uiautoamtor中默认是不支持xpath,这是属于ui2的扩展功能,速度会相比其它定位方式慢一些

在xpath定位中,ui2中的description 定位需要替换为content-desc,resourceId 需要替换为resource-id

使用方法

# 只会返回一个元素,如果找不到元素,则会报XPathElementNotFoundError错误
# 如果找到多个元素,默认会返回第0个
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]')

# 如果返回的元素有多个,需要使用all()方法返回列表
# 使用all方法,当未找到元素时,不会报错,会返回一个空列表
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]').all()

5. 设备交互

5.1 单击

d(text='Settings').click()
#单击直到元素消失,超时时间10,点击间隔1
d(text='Settings').click_gone(maxretry=10, interval=1.0)

5.2 长按

d(text='Settings').longclick()

5.3 拖动

Android<4.3时不能使用拖动

# 在0.25S内将Setting拖动至Clock上,拖动元素的中心位置
# duration默认为0.5,实际拖动的时间会比设置的要高
d(text="Settings").drag_to(text="Clock", duration=0.25)

# 拖动settings到屏幕的某个点上
d(text="Settings").drag_to(877,733, duration=0.25)

#两个点之间的拖动,从点1拖动至点2
d.drag(x1,y1,x2,y2)

5.4 滑动

滑动有两个,一个是在driver上操作,一个是在元素上操作

元素上操作

从元素的中心向元素边缘滑动

# 在Setings上向上滑动。steps默认为10
# 1步约为5毫秒,因此20步约为0.1 s
d(text="Settings").swipe("up", steps=20) 

driver上操作

即对整个屏幕操作

# 实现下滑操作
x,y = d.window_size()
x1 = x / 2
y1 = y * 0.1
y2 = y * 0.9
d.swipe(x1,y1,x1,y2)

driver滑动的扩展方法,可以直接实现滑动,不需要再自己封装定位点

# 支持前后左右的滑动
# "left", "right", "up", "down"
# 下滑操作
d.swipe_ext("down")

5.5 双指操作

android>4.3

对元素操作

d(text='Settings').gesture(start1,start2,end1,end2,)
# 放大操作
d(text='Settings').gesture((525,960),(613,1121),(135,622),(882,1540))

封装好的放大缩小操作

# 缩小
d(text="Settings").pinch_in()
# 放大
d(text="Settings").pinch_out()

5.6 等待元素出现或者消失

# 等待元素出现
d(text="Settings").wait(timeout=3.0)
# 等待元素消失,返回True False,timout默认为全局设置的等待时间
d(text='Settings').wait_gone(timeout=20)

5.7 滚动界面

设置scrollable属性为True

滚动类型:horiz 为水平 vert 为垂直

滚动方向:forward 向前

  • backward 向后

  • toBeginning 滚动至开始

  • toEnd 滚动至最后

  • to 滚动直接某个元素出现

所有方法均返回Bool值

# 垂直滚动到页面顶部/横向滚动到最左侧
d(scrollable=True).scroll.toBeginning()
d(scrollable=True).scroll.horiz.toBeginning()
# 垂直滚动到页面最底部/横向滚动到最右侧
d(scrollable=True).scroll.toEnd()
d(scrollable=True).scroll.horiz.toEnd()
# 垂直向后滚动到指定位置/横向向右滚动到指定位置
d(scrollable=True).scroll.to(description="指定位置")
d(scrollable=True).scroll.horiz.to(description="指定位置")
# 垂直向前滚动(横向同理)
d(scrollable=True).scroll.forward()
# 垂直向前滚动到指定位置(横向同理)
d(scrollable=True).scroll.forward.to(description="指定位置")
# 滚动直到System元素出现
d(scrollable=True).scroll.to(text="System")
Take screenshot of widget

im = d(text="Settings").screenshot()
im.save("settings.jpg")

5.8 输入

5.8.1 输入自定义文本

# 使用adb广播的方式输入
d.send_keys('hello')
# 清空输入框
d.clear_text()

5.8.2 输入按键

两种方法

# 发送回车
d.press('enter')
# 第二种
d.keyevent('enter')

目前press支持的按键如下

  """
        press key via name or key code. Supported key name includes:
            home, back, left, right, up, down, center, menu, search, enter,
            delete(or del), recent(recent apps), volume_up, volume_down,
            volume_mute, camera, power.
        """

keyevent是通过“adb shell input keyevent”方式输入,支持按键更加丰富

更多详细的按键信息https://developer.android.com/reference/android/view/KeyEvent.html

5.8.3 输入法切换

# 切换成ui2的输入法,这里会隐藏掉系统原本的输入法,默认是使用系统输入法
# 当传入False时会使用系统默认输入法,默认为Fasle
d.set_fastinput_ime(True)
# 查看当前输入法
d.current_ime()
#返回值
('com.github.uiautomator/.FastInputIME', True)

5.8.4 模拟输入法功能

可以模拟的功能有 go ,search ,send ,next, done ,previous

如果使用press输入按键无效,可以尝试使用此方法输入

# 搜索功能
d.send_action("search")

5.9 toast操作

# 获取toast,当没有找到toast消息时,返回default内容
d.toast.get_message(timout=5,default='no toast')
# 清空toast缓存
d.toast.reset()

5.9 监控界面

使用wather进行界面的监控,可以用来实现跳过测试过程中的弹框

当启动wather时,会新建一个线程进行监控

可以添加多个watcher

用法

# 注册监控,当界面内出现有allow字样时,点击allow
d.watcher.when('allow').click()

# 移除 allow 的监控
d.watcher.remove("allow")

# 移除所有的监控
d.watcher.remove()

# 开始后台监控
d.watcher.start()
d.watcher.start(2.0) # 默认监控间隔2.0s

# 强制运行所有监控
d.watcher.run()

# 停止监控
d.watcher.stop()

# 停止并移除所有的监控,常用于初始化
d.watcher.reset()

5.10 多点滑动

这里可以用来实现图案解锁

使用touch类

# 模拟按下不放手
touch.down(x,y)
# 停住3S
touch.sleep(x,y)
# 模拟移动
touch.move(x,y)
# 模拟放开
touch.up(x,y)
#实现长按,同一个点按下休眠5S后抬起
d.touch.down(252,1151).sleep(5).up(252,1151)
# 实现四点的图案解锁,目前只支持坐标点
d.touch.down(252,1151).move(559,1431).move(804,1674).move(558,1666).up(558,1666)

6. 图像操作

6.1 截图

d.screenshot('test.png')

6.2 录制视频

这个感觉是比较有用的一个功能,可以在测试用例开始时录制,结束时停止录制,然后如果测试fail。则上传到测试报告,完美复原操作现场,具体原理后面再去研究

首先需要下载依赖,官方推荐使用镜像下载

pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple

执行录制

# 启动录制,默认帧率为20
d.screenrecord('test.mp4')
# 其它操作
time.sleep(10)
#停止录制,只有停止录制了才能看到视频 
d.screenrecord.stop()

6.3 图片识别点击

下载与录制视频同一套依赖

这个功能是首先手动截取需要点击目标的图片,然后ui2在界面中去匹配这个图片,目前我尝试了精确试不是很高,误点率非常高,不建议使用

# 点击 
d.image.click('test.png')
# 匹配图片,返回相似度和坐标
# {'similarity': 0.9314796328544617, 'point': [99, 630]}
d.image.match('test.png')

7. 应用管理

7.1 获取当前界面的APP信息

d.app_current()
#返回当前界面的包名,activity及pid
{
    "package": "com.xueqiu.android",
    "activity": ".common.MainActivity",
    "pid": 23007
}

7.2 安装应用

可以从本地路径及url下载安装APP,此方法无返回值,当安装失败时,会抛出RuntimeError异常

# 本地路径安装 
d.app_install('test.apk')
# url安装 
d.app_install('http://s.toutiao.com/UsMYE/')

7.3 运行应用

默认当应用在运行状态执行start时不会关闭应用,而是继续保持当前界面

如果需要消除前面的启动状态,则需要加stop=True参数

# 通过包名启动
d.app_start("com.xueqiu.android",stop=True)

#源码说明
    def app_start(self, package_name: str, 
                  activity: Optional[str]=None, 
                  wait: bool = False, 
                  stop: bool=False, 
                  use_monkey: bool=False):
        """ Launch application
        Args:
            package_name (str): package name
            activity (str): app activity
            stop (bool): Stop app before starting the activity. (require activity)
            use_monkey (bool): use monkey command to start app when activity is not given
            wait (bool): wait until app started. default False
        """

7.4 停止应用

stop和clear的区别是结束应用使用的命令不同

stop使用的是“am force-stop”

clear使用的是“pm clear”

# 通过包名结束单个应用
d.app_stop("com.xueqiu.android")
d.app_clear('com.xueqiu.android')

# 结束所有应用,除了excludes参数列表中的应用包名
# 如果不传参,则会只保留两个依赖服务应用
# 会返回一个结束应用的包名列表
d.app_stop_all(excludes=['com.xueqiu.android'])

7.5 获取应用信息

d.app_info('com.xueqiu.android')

#输出
{
    "packageName": "com.xueqiu.android",
    "mainActivity": "com.xueqiu.android.common.splash.SplashActivity",
    "label": "雪球",
    "versionName": "12.6.1",
    "versionCode": 257,
    "size": 72597243
}

7.6 获取应用图标

img = d.app_icon('com.xueqiu.android')
img.save('icon.png')

7.7 等待应用启动

# 等待此应用变为当前应用,返回pid,超时未启动成功则返回0
# front为true表示等待app成为当前app,
# 默认为false,表示只要后台有这个应用的进程就会返回PID
d.app_wait('com.xueqiu.android',60,front=True)

7.8 卸载应用

# 卸载成功返回true,没有此包或者卸载失败返回False
d.app_uninstall('com.xueqiu.android')

# 卸载所有自己安装的第三方应用,返回卸载app的包名列表
# excludes表示不卸载的列表
# verbose为true则会打印卸载信息
d.app_uninstall_all(excludes=[],verbose=True)

卸载全部应用返回的包名列表并一定是卸载成功了,最好使用verbose=true打印一下信息,这样可以查看到是否卸载成功

uninstalling com.xueqiu.android  OK
uninstalling com.android.cts.verifier  FAIL

或者可以修改一下源码,使其只输出成功的包名,注释的为增加的代码,未注释的是源码

    def app_uninstall_all(self, excludes=[], verbose=False):
        """ Uninstall all apps """
        our_apps = ['com.github.uiautomator', 'com.github.uiautomator.test']
        output, _ = self.shell(['pm', 'list', 'packages', '-3'])
        pkgs = re.findall(r'package:([^\s]+)', output)
        pkgs = set(pkgs).difference(our_apps + excludes)
        pkgs = list(pkgs)
        # 增加一个卸载成功的列表
        #sucess_list = []
        for pkg_name in pkgs:
            if verbose:
                print("uninstalling", pkg_name, " ", end="", flush=True)
            ok = self.app_uninstall(pkg_name)
            if verbose:
                print("OK" if ok else "FAIL")
                # 增加如下语句,当成功则将包名加入list
                #if ok:
                 #   sucess_list.append(pkg_name)
     # 返回成功的列表
    #	return sucess_list
        return pkgs

8. 其它实用方法

8.1 连接设备

#当PC只连接了一个设备时,可以使用此种方式
d = u2.connect()
#返回的是Device类,此类继承方式如下

class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn):
    """ Device object """


# for compatible with old code
Session = Device

connect()可以使用如下其它方式进行连接

#当PC与设备在同一网段时,可以使用IP地址和端口号通过WIFI连接,无需连接USB线
connect("10.0.0.1:7912")
connect("10.0.0.1") # use default 7912 port
connect("http://10.0.0.1")
connect("http://10.0.0.1:7912")
#多个设备时,使用设备号指定哪一个设备
connect("cff1123ea")  # adb device serial number

8.2 获取设备及driver信息

8.2.1 获取driver信息

d.info
#输出
{
    "currentPackageName": "com.android.systemui",
    "displayHeight": 2097,
    "displayRotation": 0,
    "displaySizeDpX": 360,
    "displaySizeDpY": 780,
    "displayWidth": 1080,
    "productName": "freedom_turbo_XL",
    "screenOn": true,
    "sdkInt": 29,
    "naturalOrientation": true
}

8.2.2 获取设备信息

会输出测试设备的所有信息,包括电池,CPU,内存等

d.device_info
#输出
{
    "udid": "61c90e6a-ba:1b:ba:46:91:0e-freedom_turbo_XL",
    "version": "10",
    "serial": "61c90e6a",
    "brand": "Schok",
    "model": "freedom turbo XL",
    "hwaddr": "ba:1b:ba:46:91:0e",
    "port": 7912,
    "sdk": 29,
    "agentVersion": "0.9.4",
    "display": {
        "width": 1080,
        "height": 2340
    },
    "battery": {
        "acPowered": false,
        "usbPowered": true,
        "wirelessPowered": false,
        "status": 2,
        "health": 2,
        "present": true,
        "level": 98,
        "scale": 100,
        "voltage": 4400,
        "temperature": 292,
        "technology": "Li-ion"
    },
    "memory": {
        "total": 5795832,
        "around": "6 GB"
    },
    "cpu": {
        "cores": 8,
        "hardware": "Qualcomm Technologies, Inc SDM665"
    },
    "arch": "",
    "owner": null,
    "presenceChangedAt": "0001-01-01T00:00:00Z",
    "usingBeganAt": "0001-01-01T00:00:00Z",
    "product": null,
    "provider": null
}

8.2.3 获取屏幕分辨率

# 返回(宽,高)元组
d.window_size()
# 例 分辨率为1080*1920
# 手机竖屏状态返回 (1080,1920)
# 横屏状态返回 (1920,1080)

8.2.4 获取 IP 地址

# 返回ip地址字符串,如果没有则返回None
d.wlan_ip

8.3 driver全局设置

8.3.1 使用settings设置

查看settings默认设置

d.settings
#输出

{
    #点击后的延迟,(0,3)表示元素点击前等待0秒,点击后等待3S再执行后续操作
    'operation_delay': (0, 3),
    # opretion_delay生效的方法,默认为click和swipe
    # 可以增加press,send_keys,long_click等方式
    'operation_delay_methods': ['click', 'swipe'],
    # 默认等待时间,相当于appium的隐式等待
    'wait_timeout': 20.0,
    # xpath日志
    'xpath_debug': False
}

修改默认设置,只需要修改settings字典即可

#修改延迟为操作前延迟2S 操作后延迟4.5S
d.settings['operation_delay'] = (2,4.5)
#修改延迟生效方法
d.settings['operation_delay_methods'] = {'click','press','send_keys'}
# 修改默认等待
d.settings['wait_timeout'] = 10

8.3.2 使用方法或者属性设置

  • http默认请求超时时间
# 默认值60s, 
d.HTTP_TIMEOUT = 60 
  • 当设备掉线时,等待设备在线时长
# 仅当TMQ=true时有效,支持通过环境变量 WAIT_FOR_DEVICE_TIMEOUT 设置
d.WAIT_FOR_DEVICE_TIMEOUT = 70 
  • 元素查找默认等待时间
# 打不到元素时,等待10后再报异常
d.implicitly_wait(10.0)
  • 打开HTTP debug信息
d.debug = True
d.info
#输出
15:52:04.736 $ curl -X POST -d '{"jsonrpc": "2.0", "id": "0eed6e063989e5844feba578399e6ff8", "method": "deviceInfo", "params": {}}' 'http://localhost:51046/jsonrpc/0'
15:52:04.816 Response (79 ms) >>>
{"jsonrpc":"2.0","id":"0eed6e063989e5844feba578399e6ff8","result":{"currentPackageName":"com.android.systemui","displayHeight":2097,"displayRotation":0,"displaySizeDpX":360,"displaySizeDpY":780,"displayWidth":1080,"productName":"freedom_turbo_XL","screenOn":true,"sdkInt":29,"naturalOrientation":true}}
<<< END
  • 休眠
# 相当于 time.sleep(10)
d.sleep(10)

8.4 亮灭屏

# 亮屏
d.screen_on()
# 灭屏
d.screen_off()

8.5 屏幕方向

# 设置屏幕方向
d.set_orientation(value)
# 获取当前屏幕方向
d.orientation

value 值参考,只要是元组中的任一一个值就可以

# 正常竖屏
(0, "natural", "n", 0), 
# 往左横屏,相当于手机屏幕顺时针旋转90度
# 现实中如果要达到此效果,需要将手机逆时针旋转90度
 (1, "left", "l", 90),
# 倒置,这个需要看手机系统是否支持,倒过来显示 
 (2, "upsidedown", "u", 180), 
# 往右横屏,调整与往左相反,屏幕顺时针旋转270度
 (3, "right", "r", 270))

8.6 打开通知栏与快速设置

打开通知栏

d.open_notification()

打开快速设置

d.open_quick_settings()

8.7 文件导入导出

8.7.1 导入文件

# 如果是目录,这里"/sdcrad/"最后一个斜杠一定要加,否则会报错
d.push("test.txt","/sdcrad/")
d.push("test.txt","/sdcrad/test.txt")

8.7.2 导出文件

d.pull('/sdcard/test.txt','text.txt')

8.8 执行shell命令

使用shell方法执行

8.8.1 执行非阻塞命令

output返回的是一个整体的字符串,如果需要抽取值,需要对output进行解析提取处理

# 返回输出和退出码,正常为0,异常为1
output,exit_code = d.shell(["ls","-l"],timeout=60)

8.8.2 执行阻塞命令(持续执行的命令)

# 返回一个命令的数据流 output为requests.models.Response
output = d.shell('logcat',stream=True)
try:
    # 按行读取,iter_lines为迭代响应数据,一次一行
    for line in output.iter_lines():
        print(line.decode('utf8'))
finally:
    output.close()

源码描述

    def shell(self, cmdargs: Union[str, List[str]], stream=False, timeout=60):
        """
        Run adb shell command with arguments and return its output. Require atx-agent >=0.3.3

        Args:
            cmdargs: str or list, example: "ls -l" or ["ls", "-l"]
            timeout: seconds of command run, works on when stream is False
            stream: bool used for long running process.

        Returns:
            (output, exit_code) when stream is False
            requests.Response when stream is True, you have to close it after using

        Raises:
            RuntimeError

        For atx-agent is not support return exit code now.
        When command got something wrong, exit_code is always 1, otherwise exit_code is always 0
        """

8.9 session(目前已经被弃用)

8.10 停止UI2服务

因为有atx-agent的存在,Uiautomator会被一直守护着,如果退出了就会被重新启动起来。但是Uiautomator又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的uiautomatorviewer 就都不能用了,除非关掉该框架本身的uiautomator

使用代码停止

d.service("uiautomator").stop()

手动停止

直接打开ATX APP(init成功后,就会安装上),点击关闭UIAutomator

False, timeout=60):
“”"
Run adb shell command with arguments and return its output. Require atx-agent >=0.3.3

    Args:
        cmdargs: str or list, example: "ls -l" or ["ls", "-l"]
        timeout: seconds of command run, works on when stream is False
        stream: bool used for long running process.

    Returns:
        (output, exit_code) when stream is False
        requests.Response when stream is True, you have to close it after using

    Raises:
        RuntimeError

    For atx-agent is not support return exit code now.
    When command got something wrong, exit_code is always 1, otherwise exit_code is always 0
    """

## 8.9 session(目前已经被弃用)

## 8.10 停止UI2服务

因为有atx-agent的存在,Uiautomator会被一直守护着,如果退出了就会被重新启动起来。但是Uiautomator又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的uiautomatorviewer 就都不能用了,除非关掉该框架本身的uiautomator

**使用代码停止**

```python
d.service("uiautomator").stop()

手动停止

直接打开ATX APP(init成功后,就会安装上),点击关闭UIAutomator

0

评论区