|
1 | | -### uiparser ( MonkeyRunner is DEAD ) |
| 1 | +# MonkeyRunner is DEAD |
2 | 2 |
|
3 | | -Setup |
4 | | -> export ANDROID_HOME="/Users/chenqi/Library/Android/sdk" |
| 3 | +## UI Automator |
5 | 4 |
|
6 | | -Run |
7 | | -> $ANDROID_HOME/tools/bin/monkeyrunner uiparser.py |
| 5 | +https://developer.android.com/training/testing/ui-automator |
8 | 6 |
|
9 | | -### MonkeyRunner |
10 | | -https://developer.android.google.cn/studio/test/monkeyrunner |
| 7 | +Android 平台所有自动化测试框架的底层实现都依赖官方提供的 UI Automator 测试框架,适用于跨系统和已安装应用程序的跨应用程序功能UI测试。主要功能包括三部分: |
11 | 8 |
|
12 | | -#### MonkeyDevce.shell() |
13 | | ->object shell (string cmd) |
14 | | -Executes an `adb` shell command and returns the result, if any. |
| 9 | ++ UI Automator Viewer 检查布局层次结构的查看器。 |
| 10 | ++ UiDevice 设备状态信息并在目标设备上执行操作的API。 |
| 11 | ++ UI Automator API 支持跨应用程序UI测试的API。 |
| 12 | + |
| 13 | +## UI Automator Viewer |
| 14 | + |
| 15 | +PC 端 GUI 工具,扫描和分析 Android 设备上当前显示的 UI 组件。展示 UI 布局层次结构,查看设备上当前对用户可见的 UI 组件的属性。从名称可以看出,它是 UI Automator 的只读功能部分,即只能查看 UI 组件的树形结构和属性,不能操作控制 UI 组件。 |
| 16 | + |
| 17 | +`uiautomatorviewer` 位于 `<android-sdk>/tools/bin` 目录。启动入口是一个bash文件,实际调用 `<android-sdk>/tools/lib` 目录的 `uiautomatorviewer-26.0.0-dev.jar` 。 GUI 基于 Eclipse + SWT 实现,使用 Gradle 构建。系列工具源码在 `https://android.googlesource.com/platform/tools/swt/` ,依赖 `https://android.googlesource.com/platform/tools/base/` 。活跃分支: `mirror-goog-studio-master-dev` 。该仓库还包含 `chimpchat, ddms, hierarchyviewer2, monkeyrunner, swtmenubar, traceview` 这些工具。 |
| 18 | + |
| 19 | +其内部实现基于 `adb shell uiautomator dump` 。从源码仓库提交记录看,主要功能开发的活跃时间是 2014-2015,2016之后已经很少更新维护。那个年代的 Android 开发主要使用 Eclipse , 所以基于 SWT 实现多平台 PC GUI ,在当时合理。 |
| 20 | + |
| 21 | +该工具实际使用运行不稳定,极易报错,常见:`Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncException: Remote object doesn't exist!` |
| 22 | + |
| 23 | +错误原因通常是: |
| 24 | + |
| 25 | ++ adb 连接通道不稳定。 |
| 26 | ++ 机型兼容性问题,权限问题。 |
| 27 | ++ 当前手机应用程序界面处于动态,例如播放视频,动画。并且10秒超时时间仍未进入静态。 |
| 28 | + |
| 29 | +分析源码可知,错误都源于 `Android Framework uiautomator` 。 |
| 30 | + |
| 31 | +## MonkeyRunner |
15 | 32 |
|
16 | | -等同于调用`adb shell`命令。 |
17 | | -https://developer.android.google.cn/studio/command-line/adb#shellcommands |
| 33 | +https://developer.android.com/studio/test/monkeyrunner |
| 34 | + |
| 35 | +官方提供的另外一个工具,封装 uiautomator API,供 Python 脚本调用,也可注入 java 扩展插件。相比 `uiautomatorviewer` 和 `uiautomator` 命令行工具,可编程扩展性更佳。 |
| 36 | + |
| 37 | +MonkeyRunner 使用了比较冷门的 Jython 实现。我参照官方文档实现了一个Demo。详见 [uiparser.py](https://github.com/9468305/python-script/blob/master/monkeyrunner/uiparser.py) 。 |
| 38 | + |
| 39 | +### 1. 启动运行入口 |
| 40 | + |
| 41 | +> monkeyrunner -plugin <plugin_jar> <program_filename> <program_options> |
| 42 | +
|
| 43 | +monkeyrunner 是一个bash文件,位于 `<android-sdk>/tools/bin` ,启动调用 `<android-sdk>/tools/lib/monkeyrunner-26.0.0-dev.jar` 。 |
| 44 | + |
| 45 | +```bash |
| 46 | +export ANDROID_HOME="~/Library/Android/sdk" |
| 47 | +$ANDROID_HOME/tools/bin/monkeyrunner uiparser.py |
| 48 | +``` |
| 49 | + |
| 50 | +### 2. 主要方法 |
18 | 51 |
|
19 | 52 | #### MonkeyDevice.getProperty() |
20 | | -> object getProperty (string key) |
21 | | -Given the name of a system environment variable, returns its value for this device. |
22 | 53 |
|
23 | | -获取设备系统环境变量。 |
24 | | -等同于调用`adb shell getprop <keyword>`。 |
25 | | -不同厂商的设备,key可能不同。 |
26 | | -使用`adb shell getprop`,显示所有系统环境变量的key字符串 |
| 54 | +等同于调用 `adb shell getprop <keyword>` 。获取设备系统环境变量。 |
| 55 | +不同厂商的设备,key可能不同。针对具体测试机型,可使用 `adb shell getprop` ,显示所有系统环境变量的key字符串。 |
27 | 56 |
|
28 | | -### adb |
| 57 | +#### MonkeyDevce.shell() |
| 58 | + |
| 59 | +等同于调用`adb shell`命令。 |
| 60 | + |
| 61 | +### 3. 缺陷 |
| 62 | + |
| 63 | +MonkeyRunner 基于 Jython version 2.5.3 。看上去结合了Java和Python的优势,实际对于Java和Python编程都不友好。 |
| 64 | + |
| 65 | ++ Jython 2.5.3 过时,主流的Python 3.x和2.7的很多语法和库无法使用。 |
| 66 | ++ 使用vscode等编辑器编码时,缺少智能提示和自动补全。编辑器和pylint无法识别导入的库, 例如 `from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage` 。 |
| 67 | ++ Jython 似乎不能像常规的python程序一样引用外部库。实测只能使用 MonkeyRunner 内置的 `os, sys, subprocess` 等库。 |
| 68 | ++ Java extend plugin 能做的事情较少。 |
| 69 | + |
| 70 | +MonkeyRunner 实际仍然是使用 `adb shell` 和其中的 `uiautomator` 命令获取UI组件状态和属性。所以它跟 `UI Automator Viewer` 一样受限于 `uiautomator` 本身的缺陷,导致运行不稳定。 |
| 71 | + |
| 72 | +## adb shell uiautomator |
| 73 | + |
| 74 | +**adb** |
29 | 75 | https://developer.android.google.cn/studio/command-line/adb |
30 | 76 |
|
31 | | -#### adb shell am |
| 77 | +**adb shell am** |
32 | 78 | https://developer.android.google.cn/studio/command-line/adb#am |
33 | 79 | 使用 Activity Manager (am) 工具发出命令以执行各种系统操作,如启动 Activity、强行停止进程、广播 intent、修改设备屏幕属性及其他操作。 |
34 | 80 |
|
35 | | -#### adb shell pm |
| 81 | +**adb shell pm** |
36 | 82 | https://developer.android.google.cn/studio/command-line/adb#pm |
37 | 83 | 使用软件包管理器 Package Manager (pm) 工具发出命令,安装,卸载,查询安装包。 |
38 | 84 |
|
39 | | -#### adb shell uiautomator |
40 | | -获取当前界面的层级结构XML信息。 |
41 | | -``` |
42 | | -adb shell uiautomator dump /sdcard/uiparser/ui.xml |
43 | | -adb pull /sdcard/uiparser/ui.xml ./ui.xml |
44 | | -``` |
45 | | -Usage: |
46 | | -``` |
| 85 | +**adb shell uiatomator** |
| 86 | +官网相关页面已被删除,仅能从搜索引擎历史快照中找到。猜测可能近期会有变更,或者官方建议不再使用。 |
| 87 | +通过执行命令可以查看使用方法和参数。 |
| 88 | + |
| 89 | +```bash |
47 | 90 | Usage: uiautomator <subcommand> [options] |
48 | 91 |
|
49 | 92 | Available subcommands: |
@@ -80,10 +123,19 @@ dump: creates an XML dump of current UI hierarchy |
80 | 123 | events: prints out accessibility events until terminated |
81 | 124 | ``` |
82 | 125 |
|
83 | | -### Bug |
84 | | -[DumpCommand.java](https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java) |
| 126 | +### uiautomator 缺陷 |
| 127 | +
|
| 128 | +运行耗时长,失败率高,频繁报错。 |
| 129 | +`ERROR: could not get idle state.` 通常表示当前UI处于动态渲染刷新期间,例如正在播放视频,动画。在10秒超时时间内仍未进入静态。因为此时 UI 树的节点对象快速变化中,不能稳定获取。 |
| 130 | +
|
| 131 | +### uiautomator 源码 |
| 132 | +
|
| 133 | +关键代码 `uiAutomation.waitForIdle(1000, 1000 * 10);` 即单次超时等待1秒,最长超时等待10秒。超时抛出异常。 |
85 | 134 |
|
86 | | -`uiAutomation.waitForIdle(1000, 1000 * 10);` |
| 135 | +`DumpCommand.java` |
| 136 | +> https://android.googlesource.com/platform/frameworks/testing/+/master/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java |
| 137 | +
|
| 138 | +TODO: choose right branch |
87 | 139 |
|
88 | 140 | ```Java |
89 | 141 | // It appears that the bridge needs time to be ready. Making calls to the |
@@ -113,7 +165,8 @@ System.out.println( |
113 | 165 | String.format("UI hierchary dumped to: %s", dumpFile.getAbsolutePath())); |
114 | 166 | ``` |
115 | 167 |
|
116 | | -[UiAutomation.java](https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/UiAutomation.java) |
| 168 | +`UiAutomation.java` |
| 169 | +> https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/UiAutomation.java |
117 | 170 |
|
118 | 171 | ```Java |
119 | 172 | /** |
@@ -163,4 +216,8 @@ public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis) |
163 | 216 | } |
164 | 217 | } |
165 | 218 | } |
166 | | -``` |
| 219 | +``` |
| 220 | +
|
| 221 | +## hierarchyviewer2 |
| 222 | +
|
| 223 | +TODO |
0 commit comments