本文大致梳理一下 Appium + UiAutomator2 操控 Android 手机的底层通信流程,并解释 Instrumentation 是什么,以及一些常见容易误解的点。
在做 Android 自动化测试时,如果你的 capability 里配置了:
{
"platformName": "Android",
"appium:automationName": "UiAutomator2"
}
那么 Appium 实际上并不是直接操作手机界面的。
它背后会启动一套 Android 设备端的自动化服务,也就是:
appium-uiautomator2-server.apk
appium-uiautomator2-server-test.apk
然后通过 ADB、端口转发、HTTP 通信、Instrumentation、UiAutomator2 API 等机制,最终完成点击、输入、滑动、查找元素等操作。
一、整体架构先看一眼
当我们写 Appium 自动化脚本时,表面上是这样:
driver.find_element("id", "com.demo:id/login").click()
看起来像是 Python 直接操作了手机。
但实际上底层链路大概是这样的:
自动化脚本
|
| WebDriver / W3C Protocol
v
PC 上的 Appium Server
|
| HTTP 请求
v
本机端口,例如 127.0.0.1:8200
|
| adb forward
v
Android 设备内部端口,例如 6790
|
v
手机端 UiAutomator2 Server
|
| 调用 Android UiAutomator2 API
v
Android 系统 UI / 被测 App
再展开一点:
Python / Java / JS 测试代码
|
v
Appium Client
|
v
Appium Server,运行在电脑上
|
v
appium-uiautomator2-driver
|
v
ADB
|
v
Android 手机上的 UiAutomator2 Server
|
v
Instrumentation + UiAutomator2
|
v
目标 App / 系统弹窗 / 设置页面
所以 Appium 并不是一个单独的工具,而是一整套“客户端 + 服务端 + 设备端服务 + Android 测试框架”的组合。
二、Appium 里几个重要角色
1. Appium Client
比如 Python 里的:
from appium import webdriver
或者 Java 里的:
AndroidDriver driver = new AndroidDriver(url, capabilities);
它的作用是把你的自动化代码转换成 WebDriver 协议请求。
例如:
driver.find_element("id", "xxx").click()
会变成类似这样的 HTTP 请求:
POST /session/{sessionId}/element
POST /session/{sessionId}/element/{elementId}/click
也就是说,Appium Client 本身不直接操作手机,只是负责发请求。
2. Appium Server
Appium Server 通常运行在电脑上。
例如:
appium
默认监听:
http://127.0.0.1:4723
它接收 Appium Client 发来的命令,然后根据 capability 判断该用哪个 driver。
如果你配置的是:
{
"appium:automationName": "UiAutomator2"
}
那么 Appium Server 就会使用:
appium-uiautomator2-driver
3. appium-uiautomator2-driver
这是 Appium 在 PC 端的 Android 驱动。
它负责:
1. 解析 Android 相关 capability
2. 调用 adb 检查设备
3. 安装被测 App
4. 安装 UiAutomator2 Server APK
5. 启动手机端 UiAutomator2 Server
6. 建立 adb forward 端口转发
7. 把自动化命令代理到手机端服务
也就是说,它是电脑端 Appium 和 Android 设备之间的桥梁。
4. ADB
ADB 是 Android Debug Bridge。
Appium 操控 Android 手机必须依赖 ADB。
Appium 会通过 ADB 做很多事,例如:
adb devices
adb install
adb uninstall
adb shell am start
adb shell am force-stop
adb shell am instrument
adb forward
adb shell dumpsys
adb shell pm list packages
所以,Appium 不是完全绕开 ADB 操作手机的。
相反,它大量依赖 ADB 来安装、启动、转发端口、获取系统信息。
5. UiAutomator2 Server
这个是安装在 Android 手机上的设备端服务。
它通常由两个 APK 组成:
appium-uiautomator2-server.apk
appium-uiautomator2-server-test.apk
安装后,手机里通常能看到两个包:
io.appium.uiautomator2.server
io.appium.uiautomator2.server.test
它的作用是:
1. 在手机端启动一个 HTTP 服务
2. 接收 Appium Server 转发来的命令
3. 调用 Android UiAutomator2 API
4. 执行点击、输入、滑动、查找元素等操作
5. 把结果返回给 Appium Server
三、两个 APK 分别是什么作用?
Appium 使用 UiAutomator2 时,通常会往手机上安装两个 APK:
appium-uiautomator2-server.apk
appium-uiautomator2-server-test.apk
这两个很容易让人混淆。
1. appium-uiautomator2-server.apk
它是手机端服务的主体。
可以理解为:
真正干活的服务程序
它负责处理 Appium 命令,例如:
findElement
click
sendKeys
swipe
getPageSource
getScreenshot
pressKey
它内部会调用 Android 的 UiAutomator2 能力去操作设备 UI。
2. appium-uiautomator2-server-test.apk
它是 Instrumentation 测试 APK。
可以理解为:
启动器 / 测试壳 / Instrumentation 入口
它的作用不是直接处理所有自动化命令,而是通过 Android 的 Instrumentation 机制把 UiAutomator2 Server 启动起来。
常见启动命令类似:
adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner
也就是说,真正启动的时候启动的是:
io.appium.uiautomator2.server.test
然后它再让 UiAutomator2 Server 跑起来。
四、Instrumentation 是什么?
Instrumentation 是理解 Android 自动化底层非常关键的概念。
简单说:
Instrumentation 是 Android 官方提供的一套自动化测试运行机制。
它允许测试代码以一种特殊的方式运行,从而可以对 App 或设备 UI 进行测试、监控和控制。
1. 普通 App 为什么不能随便控制别的 App?
Android 有沙箱机制。
普通 App 之间是隔离的。
例如:
A App 不能随便点击 B App 的按钮
A App 不能随便读取 B App 的控件树
A App 不能随便控制 B App 的 Activity 生命周期
这是 Android 安全机制决定的。
但是自动化测试又必须做这些事:
点击按钮
输入文字
滑动页面
处理权限弹窗
获取当前界面结构
判断控件是否存在
所以 Android 提供了测试框架机制,也就是 Instrumentation。
2. Instrumentation 的基本结构
典型 Android Instrumentation 测试一般有两个包:
被测 APK
测试 APK
例如:
被测包:com.example.demo
测试包:com.example.demo.test
测试 APK 的 Manifest 里会声明:
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.example.demo" />
意思是:
这个测试 APK 使用 AndroidJUnitRunner 运行测试,
目标测试对象是 com.example.demo。
启动命令通常是:
adb shell am instrument -w com.example.demo.test/androidx.test.runner.AndroidJUnitRunner
这里的:
am instrument
就是启动 Instrumentation 测试。
3. AndroidJUnitRunner 是什么?
AndroidJUnitRunner 是 Android 测试运行器。
它的作用大致是:
1. 初始化测试环境
2. 加载测试代码
3. 执行 JUnit 测试
4. 输出测试结果
5. 提供 Instrumentation 上下文
可以理解为:
AndroidJUnitRunner 是 Instrumentation 测试的入口。
Appium 启动 UiAutomator2 Server 时,常见命令就是:
adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner
这表示:
通过 AndroidJUnitRunner 启动 io.appium.uiautomator2.server.test 这个测试包。
4. Instrumentation 和 UiAutomator2 的关系
Instrumentation 是测试运行机制。
UiAutomator2 是 Android 的 UI 自动化框架。
它们不是同一个东西。
可以这样理解:
Instrumentation:让测试代码运行起来的机制
AndroidJUnitRunner:测试代码的运行入口
UiAutomator2:真正查找控件、点击、滑动的自动化能力
Appium UiAutomator2 Server:封装 UiAutomator2 能力的 HTTP 服务
关系大概是:
adb shell am instrument
|
v
AndroidJUnitRunner
|
v
Instrumentation 测试环境
|
v
Appium UiAutomator2 Server
|
v
Android UiAutomator2 API
|
v
设备 UI
五、Appium + UiAutomator2 的完整启动流程
下面看一个完整流程。
当你执行:
driver = webdriver.Remote("http://127.0.0.1:4723", caps)
Appium 大致会做这些事。
1. Appium Client 创建 Session
客户端发送一个创建 session 的请求给 Appium Server:
POST /session
请求里包含 capability,例如:
{
"platformName": "Android",
"appium:automationName": "UiAutomator2",
"appium:deviceName": "emulator-5554",
"appium:appPackage": "com.demo",
"appium:appActivity": ".MainActivity"
}
Appium Server 收到后,开始初始化 Android 会话。
2. 检查设备连接
Appium 会调用:
adb devices
确认设备在线。
例如:
emulator-5554 device
如果有多个设备,还需要通过:
{
"appium:udid": "emulator-5554"
}
指定具体设备。
3. 获取设备信息
Appium 可能会执行类似:
adb shell getprop ro.build.version.sdk
adb shell getprop ro.product.model
adb shell wm size
adb shell wm density
用来判断 Android 版本、屏幕尺寸、设备信息等。
4. 安装或检查被测 App
如果你传了:
{
"appium:app": "/path/demo.apk"
}
Appium 会安装这个 APK。
如果你只传了:
{
"appium:appPackage": "com.demo",
"appium:appActivity": ".MainActivity"
}
Appium 可能不会安装新 APK,而是直接启动设备上已有的 App。
5. 安装 UiAutomator2 Server 两个 APK
Appium 会检查设备上是否已经安装:
io.appium.uiautomator2.server
io.appium.uiautomator2.server.test
如果没装,或者版本不匹配,会安装:
adb install appium-uiautomator2-server.apk
adb install appium-uiautomator2-server-test.apk
也可能带 -r 参数覆盖安装:
adb install -r appium-uiautomator2-server.apk
adb install -r appium-uiautomator2-server-test.apk
6. 建立 adb forward 端口转发
这是非常关键的一步。
手机端 UiAutomator2 Server 启动后,会在设备内部监听一个端口,例如:
6790
但是 PC 上的 Appium Server 不能直接访问 Android 设备内部的 127.0.0.1:6790。
所以 Appium 会通过 ADB 建立端口转发:
adb forward tcp:8200 tcp:6790
意思是:
电脑本地 8200 端口 -> Android 设备内部 6790 端口
之后 Appium Server 访问:
http://127.0.0.1:8200
实际上就是访问手机里的 UiAutomator2 Server。
这一步很容易被忽略,但它是 Appium Server 和设备端服务通信的关键。
7. 启动 UiAutomator2 Server
Appium 通过 Instrumentation 启动手机端服务:
adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner
这一步做了几件事:
1. 启动 io.appium.uiautomator2.server.test 测试包
2. AndroidJUnitRunner 初始化 Instrumentation 测试环境
3. 测试包启动 UiAutomator2 Server
4. UiAutomator2 Server 在设备端口上监听 HTTP 请求
启动成功后,设备端就有一个 HTTP 服务在等 Appium Server 发命令。
8. Appium Server 检查设备端服务是否就绪
Appium 可能会反复访问:
http://127.0.0.1:8200/status
如果返回正常,说明手机端 UiAutomator2 Server 已经起来了。
类似:
{
"value": {
"ready": true
}
}
如果连接失败,就可能看到:
ECONNREFUSED 127.0.0.1:8200
socket hang up
UiAutomator2 server cannot start
9. 启动被测 App
Appium 会通过 ADB 启动目标 App。
类似:
adb shell am start -W -n com.demo/.MainActivity
如果需要清空数据,可能会执行:
adb shell pm clear com.demo
如果 noReset 设置为 true,则可能不会清数据。
10. Session 创建完成
当以上步骤都成功后,Appium Client 端的:
driver = webdriver.Remote(...)
才算真正完成。
之后你写的点击、输入、查找元素等命令,才会通过这条链路发送到手机端执行。
六、一次 click 命令的底层过程
假设测试代码是:
driver.find_element("id", "com.demo:id/login").click()
大致流程如下。
1. 客户端发送查找元素请求
Appium Client 向 Appium Server 发送:
POST /session/{sessionId}/element
请求体可能类似:
{
"using": "id",
"value": "com.demo:id/login"
}
2. Appium Server 转发给 UiAutomator2 Server
Appium Server 不直接查找控件。
它会把请求转发到:
http://127.0.0.1:8200
由于前面建立了:
adb forward tcp:8200 tcp:6790
所以实际请求会进入手机端:
device:6790
3. UiAutomator2 Server 调用 Android UiAutomator2 API
手机端 UiAutomator2 Server 收到请求后,会调用 Android 的 UiAutomator2 能力。
例如根据 resource-id 查找节点:
com.demo:id/login
找到后返回一个 elementId 给 Appium Server。
4. 客户端发送点击请求
然后 click 请求类似:
POST /session/{sessionId}/element/{elementId}/click
Appium Server 再转发给手机端 UiAutomator2 Server。
5. 手机端执行点击
UiAutomator2 Server 调用 Android 自动化 API,在对应坐标或节点上执行点击。
最终手机界面产生真实点击效果。
七、getPageSource 的底层大概是什么?
当你执行:
driver.page_source
Appium 会请求手机端获取当前 UI 层级。
UiAutomator2 Server 会通过 UiAutomator2 获取当前窗口的 Accessibility/UI 层级信息,然后转成 XML 返回。
返回结果类似:
<hierarchy>
<node
index="0"
text="登录"
resource-id="com.demo:id/login"
class="android.widget.Button"
clickable="true" />
</hierarchy>
需要注意:
page_source 不是直接读取 App 的布局 XML。
它读取的是当前界面在系统 UI 自动化层暴露出来的节点信息。
所以有些自定义控件、WebView、Canvas 绘制内容,可能看不到完整结构。
八、UiAutomator2 到底能操作什么?
UiAutomator2 的特点是可以操作整个设备 UI。
它可以操作:
被测 App
系统权限弹窗
系统设置页面
输入法界面
通知栏部分内容
其他 App 的界面
这也是 Appium 选择 UiAutomator2 的重要原因。
相比 Espresso:
Espresso 更适合单 App 内部白盒/灰盒测试
UiAutomator2 更适合黑盒、跨 App、系统级 UI 操作
九、几个常见 capability 和底层行为
1. automationName
{
"appium:automationName": "UiAutomator2"
}
表示使用 UiAutomator2 驱动。
如果不写,某些 Appium 版本可能默认选择 UiAutomator2,但建议显式写上。
2. systemPort
{
"appium:systemPort": 8200
}
这是 PC 端用于转发到设备端 UiAutomator2 Server 的端口。
多设备并发时必须注意,不能冲突。
例如:
设备1: systemPort = 8200
设备2: systemPort = 8201
设备3: systemPort = 8202
否则多个 session 会抢同一个端口。
3. skipServerInstallation
{
"appium:skipServerInstallation": true
}
表示跳过安装 UiAutomator2 Server APK。
容易误解的是:
它不是不需要 UiAutomator2 Server。
而是表示:
假设设备上已经安装了可用版本,所以跳过安装步骤。
如果设备上没有安装,或者版本不匹配,可能启动失败。
4. noReset
{
"appium:noReset": true
}
表示不重置应用状态。
通常影响:
是否清除 App 数据
是否重新安装 App
是否保留登录状态
但它不代表 Appium 一定不做任何初始化操作。
UiAutomator2 Server 该启动还是要启动。
5. appPackage / appActivity
{
"appium:appPackage": "com.demo",
"appium:appActivity": ".MainActivity"
}
表示启动设备上已有 App 的指定 Activity。
如果没有传 app,Appium 一般不会安装新 APK,而是直接启动已有包。
十、容易误解的几个细节
下面这些是很多初学者容易混淆的地方。
误解 1:Appium 直接通过 ADB click 操作手机
不完全对。
ADB 可以执行点击:
adb shell input tap 100 200
但 Appium 的正常元素操作不是简单拼 ADB 命令实现的。
Appium 的元素查找、点击、输入主要是通过:
UiAutomator2 Server -> Android UiAutomator2 API
完成的。
ADB 更多负责:
安装 APK
启动服务
端口转发
启动 App
获取系统信息
执行部分辅助命令
误解 2:Appium Server 装在手机上
不对。
Appium Server 通常运行在电脑上。
手机上安装的是:
UiAutomator2 Server APK
也就是:
io.appium.uiautomator2.server
io.appium.uiautomator2.server.test
可以理解为:
电脑上是总控服务
手机上是执行代理服务
误解 3:appium-uiautomator2-server-test.apk 是没用的测试包
不对。
它非常重要。
没有它,UiAutomator2 Server 很可能启动不起来。
因为 Appium 需要通过:
adb shell am instrument
以 Instrumentation 的方式启动服务。
server-test.apk 就是这个 Instrumentation 启动入口。
误解 4:Instrumentation 就是 UiAutomator2
不对。
它们是不同层面的东西。
Instrumentation:Android 测试运行机制
UiAutomator2:Android UI 自动化框架
AndroidJUnitRunner:Instrumentation 的测试运行器
Appium UiAutomator2 Server:封装 UiAutomator2 的设备端 HTTP 服务
可以这样记:
Instrumentation 负责让测试环境跑起来;
UiAutomator2 负责真正操作 UI。
误解 5:page_source 就是 App 的布局文件
不对。
Appium 获取的 page source 是当前屏幕上通过 UI 自动化层暴露出来的控件树。
它不等于:
res/layout/activity_main.xml
也不等于 App 内部真实 View 树的完整信息。
尤其是这些场景:
WebView
Flutter
Unity
Canvas 自绘控件
高度自定义控件
可能拿不到理想的节点结构。
误解 6:只要控件在屏幕上,就一定能 findElement
不一定。
原因可能包括:
控件没有暴露 resource-id
控件 text 为空
控件是自绘的
控件被其他窗口遮挡
当前 page source 还没刷新
WebView 上下文没切换
元素在 RecyclerView 中还没滑出来
系统动画还没结束
所以实际测试中要配合:
显式等待
合理定位方式
滑动查找
上下文切换
关闭动画
误解 7:UiAutomator2 Server 端口就是 8200
不严谨。
通常:
8200 是 PC 本地端口
6790 是设备内部端口
Appium 通过:
adb forward tcp:8200 tcp:6790
把电脑端口转发到设备端口。
所以你的 Appium Server 访问的是:
127.0.0.1:8200
但真正服务运行在手机内部的端口上。
误解 8:多设备并发只要连多个手机就行
不够。
多设备并发时要注意端口不能冲突。
至少要关注:
udid
systemPort
chromedriverPort,涉及 WebView 时
mjpegServerPort,涉及截图流时
例如:
{
"appium:udid": "device1",
"appium:systemPort": 8200
}
另一个设备:
{
"appium:udid": "device2",
"appium:systemPort": 8201
}
否则可能出现请求串设备、端口占用、session 启动失败等问题。
十一、常见问题和排查方向
1. UiAutomator2 server cannot start
可能原因:
server APK 安装失败
server-test APK 安装失败
Instrumentation 启动失败
端口转发失败
设备卡顿
Android 系统限制后台启动
旧版本残留
可以尝试:
adb uninstall io.appium.uiautomator2.server
adb uninstall io.appium.uiautomator2.server.test
然后重新运行 Appium。
2. ECONNREFUSED 127.0.0.1:8200
表示 Appium Server 访问本地 8200 端口失败。
可能原因:
adb forward 没建成功
UiAutomator2 Server 没启动
systemPort 被占用
Instrumentation 崩了
设备断开
可以检查:
adb forward --list
3. socket hang up
可能原因:
设备端服务崩溃
UiAutomator2 Server 响应中断
Appium 和 server APK 版本不匹配
设备性能差导致超时
可以看 Appium 日志和 logcat:
adb logcat
4. A new session could not be created
这个错误比较泛,需要看详细日志。
常见方向:
设备未连接
capability 配错
appPackage/appActivity 错
APK 安装失败
权限问题
UiAutomator2 Server 启动失败
端口冲突
十二、手动观察一下设备上安装的 Appium 组件
可以执行:
adb shell pm list packages | grep appium
可能看到:
package:io.appium.uiautomator2.server
package:io.appium.uiautomator2.server.test
卸载:
adb uninstall io.appium.uiautomator2.server
adb uninstall io.appium.uiautomator2.server.test
查看端口转发:
adb forward --list
手动启动 Instrumentation,理论上类似:
adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner
不过实际使用中通常不需要你手动启动,Appium 会自动处理。
十三、用一句话总结整套底层过程
Appium + UiAutomator2 操控 Android 手机,大致是:
测试脚本通过 WebDriver 协议请求 PC 上的 Appium Server;
Appium Server 使用 uiautomator2-driver 调用 ADB 初始化设备;
ADB 安装并通过 Instrumentation 启动手机端 UiAutomator2 Server;
Appium Server 通过 adb forward 与手机端服务通信;
手机端服务调用 Android UiAutomator2 API 操作目标 App 或系统 UI;
执行结果再沿原链路返回给测试脚本。
十四、最后再总结几个关键点
1. Appium Server 在电脑上,不在手机上。
2. 手机上运行的是 UiAutomator2 Server。
3. UiAutomator2 Server 由两个 APK 组成:
- server.apk:服务主体
- server-test.apk:Instrumentation 启动入口
4. Instrumentation 是 Android 官方测试运行机制。
5. UiAutomator2 是真正负责 UI 查找和操作的自动化框架。
6. adb forward 是电脑端 Appium Server 和手机端服务通信的关键。
7. page_source 不是 App 源布局,而是 UI 自动化层看到的节点树。
8. 多设备并发一定要注意 systemPort 等端口隔离。
如果用一个类比来理解:
测试脚本:发号施令的人
Appium Server:电脑上的指挥中心
ADB:电脑和手机之间的通道
UiAutomator2 Server:手机上的执行代理
Instrumentation:让执行代理以测试身份运行的机制
UiAutomator2:执行代理手里的自动化工具
目标 App:被操作对象
这样理解之后,再看 Appium 日志里的安装、端口、instrument、proxy、status 等信息,就会清晰很多。