
字节笔记本
2026年10月5日 · 约 16 分钟读完
一条命令把 Android 模拟器跑进 Docker
测 Android 应用,最大的痛点之一是环境。本地跑模拟器,吃内存、占 CPU,还经常因为各种玄学问题起不来。团队里每个人都要配一遍 SDK、装一遍镜像、调一遍 KVM。到了 CI(持续集成)更头疼:服务器上没有显示器,模拟器跑不起来,只能靠真机农场或者云服务,又贵又慢。
开源社区有个项目直接治这个病:docker-android。它把 Android 模拟器打包进 Docker 镜像,跑在容器里,通过网络远程操作。一个 docker run,模拟器就上线。本文就来拆这个「把模拟器服务化」的开源项目。

容器里跑起来的 Android 模拟器:桌面、设备信息、浏览器都正常工作,ADB 连上后可用 scrcpy 投屏操作。
一、它解决什么问题
先说清楚传统做法的三个痛点。
痛点 1:本地环境难配。 Android Studio 装一遍、SDK 下载几个 G、KVM 虚拟化要开 BIOS、HAXM/WHPX 还经常和别的虚拟化冲突。新人入职光配环境就一天。
痛点 2:CI 上跑不了模拟器。 大多数 CI runner 是无头(headless)的 Linux 服务器,没有图形界面,没有显示设备。模拟器需要 GPU 和显示支持,跑不起来。结果就是,CI 只能跑单元测试,UI 测试要么砍掉,要么用昂贵的云真机服务。
痛点 3:多版本矩阵测试是噩梦。 App 要覆盖 Android 10 到 14,每个版本一个模拟器,本地根本开不了那么多。云服务按机器和时长计费,账单吓人。
docker-android 的解法是,把模拟器变成一个 Docker 容器,像启动一个 Web 服务一样启动它。需要哪个版本,拉对应镜像跑就行;CI 流水线里 docker run 一行就能集成;远程通过 ADB 网络连接操作。
二、核心设计:极简加服务化
README 开宗明义:A minimal and customizable Docker image running the Android emulator as a service,一个极简、可定制的 Docker 镜像,把 Android 模拟器作为服务运行。
两个关键词:minimal(极简)和 as a service(服务化)。
极简:镜像里只装必要的东西
它基于 Alpine Linux(体积最小的 Docker 基础镜像之一),里面只装四样东西:Android Emulator 本体、ADB Server(远程连接用)、QEMU 加 libvirt(虚拟化支撑,跑 Android 系统)、JRE 11(emulator 需要 Java 运行时)。
不装 Android Studio、不装 IDE、不装开发工具,因为它是个运行时容器,不是开发环境。这种克制让镜像尽可能小。
体积对比(README 给出的数据):
| 变体 | 未压缩 | 压缩后 |
|---|---|---|
| API 33 + Emulator | 5.84 GB | 1.97 GB |
| API 32 + Emulator | 5.89 GB | 1.93 GB |
| API 28 + Emulator | 4.29 GB | 1.46 GB |
| 不含 SDK 和 Emulator | 414 MB | 138 MB |
最后一行很关键:可以构建一个不含 SDK 和 emulator 的裸镜像,只有 414MB,然后把 SDK 挂载进去(构建时加 INSTALL_ANDROID_SDK=0,运行时把 SDK 挂到容器的 /opt/android)。这对 CI 农场特别有用,共享一份 SDK 放在 NFS 之类的共享文件系统上,每个容器只挂载,不重复下载。
服务化:模拟器是个网络服务
容器启动后,模拟器和 ADB 监听在容器网络接口上,外部通过端口转发连接:
- ADB 端口(5555):
adb connect 127.0.0.1:5555远程连上去; - 模拟器端口(5554):用作模拟器控制台。
连上 ADB 后,所有 ADB 命令照常用:adb install、adb shell、adb logcat,就像模拟器在本地一样。还可以用 scrcpy 远程投屏控制:
adb connect 127.0.0.1:5555
scrcpy # 弹出窗口,实时显示并操控 Android 屏幕这就是服务化的精髓:模拟器不再是一个本地 GUI 程序,而是一个网络端点,任何能连到这个端点的机器都能操作它。默认情况下,模拟器使用 Pixel 预设,分辨率 1080x1920。

docker-android 工作流:宿主机把 /dev/kvm 传给容器,容器内模拟器加 ADB 监听 5555 端口,外部通过 adb connect 连接后跑测试或投屏。
三、三种镜像变体
docker-compose.yml 里定义了三个服务,对应三种使用场景。
android-emulator(基础版):纯 CPU 模拟器,Google APIs 镜像,适合大多数测试场景,不需要 GPU。android-emulator-cuda(GPU 加速版):用Dockerfile.gpu构建,挂载 NVIDIA GPU。GPU 加速让模拟器渲染流畅很多,尤其适合游戏、图形密集型 App 的测试。android-emulator-cuda-store(GPU 加 Play Store 版):在 GPU 版基础上改用google_apis_playstore镜像,带 Google Play 商店,适合需要测内购、Play 服务、应用更新的场景。
用 Play Store 版需要配置 adbkey:用 adb keygen adbkey 生成私钥和公钥两个文件,覆盖到 keys 目录,保证模拟器和客户端的认证一致。
四、怎么用:三步上手
第 1 步:确保宿主机支持 KVM
模拟器需要硬件虚拟化。Linux 上检查:
ls -la /dev/kvm # 存在即支持重要前提:宿主机必须是 Linux 且 CPU 支持 KVM。macOS 和 Windows 的 Docker Desktop 跑不了这个,因为 KVM 是 Linux 内核特性,Docker Desktop 的虚拟化层不透传。这个项目主要面向 Linux 服务器和 CI 环境。
第 2 步:拉镜像或自建
用预构建镜像(最快):
docker pull halimqarroum/docker-android:api-33或自建(可定制):
docker build -t android-emulator .定制构建参数(对应 Android 9、带 Play 商店、x86 架构):
docker build \
--build-arg API_LEVEL=28 \
--build-arg IMG_TYPE=google_apis_playstore \
--build-arg ARCHITECTURE=x86 \
--tag android-emulator .第 3 步:运行加连接
# 运行(挂载 KVM,暴露 ADB 端口)
docker run -it --rm --device /dev/kvm -p 5555:5555 android-emulator
# 等几十秒让 Android 启动完,然后连接
adb connect 127.0.0.1:5555
# 像本地模拟器一样用
adb install my-app.apk
adb shell monkey -p com.example 1
scrcpy需要持久化数据(默认每次重启擦除):
docker run -it --rm --device /dev/kvm -p 5555:5555 \
-v ~/android_avd:/data android-emulator挂载 /data 后,安装的 App 和用户数据都会保留。
五、关键配置参数
docker-compose.yml 暴露的可调环境变量:
| 变量 | 默认值 | 作用 |
|---|---|---|
| MEMORY | 16384 | 模拟器内存(MB) |
| CORES | 16 | 模拟器 CPU 核数 |
| DISABLE_ANIMATION | false | 关动画,CI 测试更快更稳 |
| DISABLE_HIDDEN_POLICY | true | 关隐藏策略 |
| SKIP_AUTH | false | 跳过 ADB 认证 |
| EXTRA_FLAGS | -no-metrics -no-audio -partition-size=8192 | 额外模拟器参数 |
CI 场景的关键配置有三点:DISABLE_ANIMATION 设为 true,动画会让 UI 测试变慢且不稳定;MEMORY 要给够,不够会卡死;EXTRA_FLAGS 里保留 -no-audio,无头环境不需要音频。
六、CI 集成:这才是它的主战场
docker-android 最大的价值在 CI。GitHub Actions 的典型集成方式(示意):
jobs:
instrumented-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 启动 Android 模拟器容器
run: |
docker run -d --name android \
--device /dev/kvm \
-p 5555:5555 \
-e DISABLE_ANIMATION=true \
halimqarroum/docker-android:api-33
- name: 等待模拟器启动
run: sleep 60
- name: 连接并测试
run: |
adb connect 127.0.0.1:5555
./gradlew connectedAndroidTest和常见方案的对比:
| 维度 | docker-android | android-emulator-runner | 云真机服务 |
|---|---|---|---|
| 成本 | 自托管 runner 几乎免费 | GitHub runner 免费 | 按时长收费 |
| 灵活性 | 高,任意定制镜像 | 中,固定配置 | 低 |
| 多版本矩阵 | 拉不同 tag 即可 | 每次重建 | 要买不同设备 |
| 速度 | 容器秒级启动 | 每次冷启动慢 | 看排队 |
| 远程访问 | 支持 ADB 与 scrcpy | 仅限 CI 内部 | 支持但收费 |
对需要测试多版本矩阵的团队,docker-android 加自托管 runner 是性价比最高的组合。
七、一个值得注意的设计:每次重启擦除
README 提了一句:Emulator images are wiped each time the emulator re-starts,模拟器每次重启时镜像被擦除。
这是个刻意的设计决策,保证每次测试都从干净状态开始,避免上一次测试的残留污染下一次结果。对 CI 来说这是正确的取舍,确定性比省时间重要。
如果需要保留状态(比如调试时不想每次重装 App),挂载 -v ~/android_avd:/data 就行。它给了你选择权,但默认落在最安全的那一档。
八、和同类项目对比
README 的 See also 提到两个相关项目:
| 项目 | 特点 | 区别 |
|---|---|---|
| 本项目 docker-android | Alpine 加 KVM,极简,ADB 加 scrcpy | 最轻,适合 CI |
| budtmo/docker-android | 带 WebRTC 接口,浏览器直接看屏幕 | 更易用,但镜像更大 |
| alvr/alpine-android | 不同的 Alpine 基础镜像 | 类似思路,社区维护 |
选型建议:纯 CI 命令行测试选本项目,最轻最快;需要浏览器远程看屏幕选 budtmo/docker-android,WebRTC 免装客户端;需要深度定制也选本项目,构建参数丰富。
九、谁该用,注意什么
强烈推荐,如果你:做 Android 开发且要在 CI 里跑插桩测试(instrumented tests);要测多 Android 版本矩阵;想搭自己的模拟器农场,不想用云服务;做移动端自动化(Appium、Espresso、UI Automator);或者想远程控制 Android 做自动化任务。
注意几点:
- 必须 Linux 加 KVM。 macOS 和 Windows 的 Docker Desktop 跑不了;CI runner 要是 Linux 且支持嵌套虚拟化(裸金属实例、nested VM、自建物理机等)。
- 资源要给够。 README 明确要求 API 33 至少 4GB 内存、8GB 磁盘,给不够会卡死或启动失败。
- Play Store 版要配 adbkey。 不配的话连不上带 Play 服务的镜像。
- 镜像体积不小。 带 SDK 和 emulator 压缩后约 2GB,CI 里建议从 Docker Hub 拉预构建镜像,别每次现建。
- 启动有延迟。 容器起来后,Android 系统就绪还要等几十秒,CI 脚本里要
sleep或轮询adb get-state。
十、小结
一句话:docker-android 把 Android 模拟器变成了一个 Docker 容器,一条命令启动,网络远程连接,版本随意切换,专为 CI 测试和远程控制而生。
它的价值不在技术多炫,而在把一件本来就麻烦的事(搭 Android 测试环境)变得极其简单。docker run 一行,模拟器上线;换个 tag,就是另一个 Android 版本;CI 流水线里无脑集成。
对 Android 开发团队来说,这种环境容器化是提升工程效率的基础设施级改进。当你不再花时间配环境,才能把时间花在真正重要的事上:写好 App。如果你的团队正在维护原生或跨端 Android 项目,把这套方案接进 CI,UI 测试就能从偶尔为之变成日常动作。
本文基于开源项目 docker-android 的仓库文档与配置文件整理,仓库地址 https://github.com/HQarroum/docker-android ,采用 Apache 2.0 协议,运行需要 Linux 加 KVM 环境。



