UIAutomator2

1,051 阅读4分钟

背景:

偶现ANR&Crash问题难以复现,可以通过mokey,UIAutomator2做复现,解放双手提高生产力,其中后者操作控制可编程。相比于安卓多种单元测试框架,无代码侵入,可直接应对二进制文件自动化测试。使用一下,整个小案列,顺便看下源码实现。

Install uiautomator2

git clone https://github.com/openatx/uiautomator2
pip install -e uiautomator2
测试是否安装成功 `uiautomator2 --help`

Install weditor (UI Inspector)

pip install -U weditor

pip install weditor 出错|跳过

pip install -U setuptools
git clone https://github.com/openatx/weditor
pip3 install -e weditor

Install daemons to a device

python -m uiautomator2 init

启动weditor

python -m weditor
将此命令做成bat,点击执行

使用weditor

界面&编辑器

uiautomator的api使用

略,python脚本编写,详见官方文档

自动化测试案例举个例

app启动过程偶现ANR问题,自动化复现,获取日志
# coding: utf-8
#
import uiautomator2 as u2
import time
import logging
d = u2.connect()
d.click_post_delay = 1.5#
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(funcName)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info(d)
d.watcher.stop()
d.app_stop_all()
d.watcher("ANR").when(xpath="ANR").when("Force Close").click()
appId = "xxx"
def run_app():
    if d.session(appId, attach=True):
        logger.info("已打开程序")
        time.sleep(2)
        if d(text="xxx").exists(timeout=5):
            logger.info("确认启动完成")
            time.sleep(2)
        else:
            logger.info("不确定启动完成")
            time.sleep(5)
        d.app_stop(appId)
    else:
        print('打开程序失败')

i=0
while True:
    print('------',i,'------')
    run_app()
    if i>5000:
        break
    i+=1
d.shell("adb bugreport")
做成exe分发出去
    pyinstaller -F -w -i .\icon_Cow.ico .\main.py
启动时间统计
自动化打卡
...

UiAutomator

1. uiautomator是命令行jar包
2. 通过accessibility获取到窗口视图节点信息树,转换成xml序列化数据文件到指定目录
3. 视图树信息获取流程,有分层,跨进程,涉及到的类如下
- com.android.commands.uiautomator.Launcher
- com.android.commands.uiautomator.DumpCommand
- com.android.uiautomator.core.UiAutomationShellWrapper
- android.app.UiAutomation
- android.app.UiAutomationConnection
- android.app.UiAutomation$IAccessibilityServiceClientImpl
- android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper
- com.android.server.accessibility.UiAutomationManager
- com.android.server.accessibility.AccessibilityServiceConnection
- android.accessibilityservice.AccessibilityService
- android.view.accessibility.AccessibilityInteractionClient
- com.android.server.accessibility.AbstractAccessibilityServiceConnection
- android.view.accessibility.AccessibilityWindowInfo
- android.view.accessibility.AccessibilityNodeInfo
4. 剩下的主要api
- UiAutomatorBridge取代了之前版本UIXXX作为上下文
- InteractionController 拦截控制器,可以处理各种点击触摸按键滑动,支持等待,对应UIXXX相应功能。
- QueryController窗口视图树节点查询能力

UiAutomatorViewer源码原理分析

1. 命令行jar包&eclipse桌面应用,提供弹窗选设备,展示视图树(rectangles+snapshot)
2. ddmlib连接设备,通过shell命令获取ui—hierarchy.xml文件及snapshot,xml—SAX解析填充方式矩形框到swt的treeview+一张snapshot。
    待补充

android-uiautomator-server源码分析

命令方式远程启动安卓测试进程,测试进程顺带拉起app监控进程。
1. 命令方式启动安卓进程和组件
adb forward tcp:9008 tcp:9008
adb shell am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub \
    com.github.uiautomator.test/androidx.test.runner.AndroidJUnitRunner
adb shell am broadcast -a ....此命令可以发送广播,被应用接收到,转成端测业务处理逻辑
adb shell am start -n ...
---app进程app-uiautomator.apk
2. 对外提供的服务:向PC上报(http请求方式)电量wif变更(广播监听方式触发),录屏、拍照、旋转屏幕监听、修改手机GPS&WIFI定位,远程触摸事件控制。还有最重要的远程暴露UiAutomator能力接口,rpc方式见8。
3. 访问PC服务:起停检查测试uiautomator服务,提供端侧提示语。
4. app端侧使用:端侧点击按钮,悬浮窗。
5. 有4个main函数,调用举例:
    APKPATH=$(adb shell pm path com.github.uiautomator | cut -d: -f2)
    adb shell CLASSPATH=$APKPATH exec app_process /system/bin com.github.uiautomator.MinicapAgent --help
6、动态权限通过反射处理
7. reflect、commons.cli、NanoHTTPD、MediaCodec,jsonrpc4j(8)等应用
----测试进程app-uiautomator-test.apk
8. 测试目录下有Stub启动rpc服务(json协议)(AutomatorHttpServer:NanoHTTPD)AutomatorService,对远程暴露UiAutomator的api或能力(用的还是UIXXXAPI),是以rpc方式提供的。
    待补充

UIAutomator2源码原理分析

1. py实现的映射库,PC端通过远程rpc(json协议),映射UIAutomator能力的api。
2. usb或wifi方式连接设备为起点,Device(abc多继承,3~7)封装了所有能力。
3. _Device建立jsonrpc远程服务连接,通过http请求封装了主要的api
4. _AppMixIn封装app开头的几个常用命令
5. _PluginMixIn封装xpath,录屏,拍照,控件,等待
6. _InputMethodMixIn输入事件
7. _DeprecatedMixIn略
8. 2的流程连接设备,通过rpc启动手机端服务,如下:
    connect(addr=None) -> Device:
    def connect_usb(serial: Optional[str] = None, init: bool = False) -> Device:
    def connect_adb_wifi(addr) -> Device:
    class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn):
    class _Device(_BaseClient):
    def __init__(self, clnt: "_BaseClient"):
    return self.server._jsonrpc_retry_call(self.method, params,
    self.reset_uiautomator(str(e))  # uiautomator可能出问题了,强制重启一下
    卸载安装app-uiautomator.apk和app-uiautomator-test.apk
    output = self._test_run_instrument()
    "am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner",
    def _test_run_instrument(self):
9. 也提供cmd命令调用,帮助,通过adb_util库提供也有些调用,安装apk,获取手机配置信息,但大部分uiautomator能力是通过通过http或httprpc的。
    待补充

weditor源码原理分析

命令行应用,通过浏览器访问自建立web应用,通过UIAutomator2或wda获取窗口视图树信息,展示到web控件树种,提供交互操作。
用浏览器访问本地页面,创建本地web应用,使用同一个端口号。
webbrowser.open(f'http://localhost:{port}', new=2)
application = make_app({
    'static_path': os.path.join(__dir__, 'static'),
    'template_path': os.path.join(__dir__, 'templates'),
    'debug': debug,
})
application.listen(port)
通过tornado的web服务器api创建web应用(有点像netty的api设计)
application = tornado.web.Application(
    [
        (r"/", MainHandler),
        (r"/api/v1/version", VersionHandler),
        (r"/api/v1/connect", DeviceConnectHandler),
        (r"/api/v1/crop", CropHandler),
        (r"/api/v1/devices/([^/]+)/screenshot", DeviceScreenshotHandler),
        (r"/api/v1/devices/([^/]+)/hierarchy", DeviceHierarchyHandler),
        # (r"/api/v1/devices/([^/]+)/exec", DeviceCodeDebugHandler),
        (r"/api/v1/devices/([^/]+)/widget", DeviceWidgetListHandler),
        (r"/api/v1/widgets", DeviceWidgetListHandler),  # add widget
        (r"/api/v1/widgets/([^/]+)", DeviceWidgetListHandler),
        # v2
        (r"/api/v2/devices/([^/]+)/hierarchy", DeviceHierarchyHandlerV2),
        # widgets
        (r"/widgets/([^/]+)", WidgetPreviewHandler),
        (r"/widgets/(.+/.+)", tornado.web.StaticFileHandler, {
            "path": "./widgets"
        }),
        # cache static assets
        (r"/(unpkg.com/.*)", StaticProxyHandler),
        (r"/(cdn.jsdelivr.net/.*)", StaticProxyHandler),
        # (r"/ws/v1/build", BuildWSHandler),
        (r"/ws/v1/python", PythonShellHandler),
        (r"/quit", QuitHandler),
    ],
    **settings)
可视化可交互页面部分内容分析
MainHandler加载了一个本地模板页面
DeviceConnectHandler,处理设备连接,device支持安卓ios,支持snapshot,窗口视图树信息获取。_AndroidDevice实际上用到是UIAutomator2,视图树信息获取将xml文件转成json数据。_AppleDevice用到的是wda,视图树信息直接是json。
DeviceHierarchyHandlerXXX就是提供视图树信息的调用。
DeviceWidgetListHandler会同步修改widget_data、meta.json、hierarchy.xml(get获取,put删除,post提交)应该是视图树节点展示修改删除。
DeviceScreenshotHandler提供手机snapshot
XXXHandler略。
    待补充

参考文档

blog.csdn.net/u011106915/… github.com/openatx/uia… blog.csdn.net/hzk59451232… blog.csdn.net/hzk59451232… blog.csdn.net/hzk59451232… testerhome.com/topics/1135… testerhome.com/topics/1103… blog.csdn.net/cadi2011/ar…