sh
docker push yeshen/docker-android:emulator_16.0
基础原理
-
系统镜像来源
docker-android 项目 = 用 Docker 容器运行 Google 官方 Android 模拟器。
容器里跑的是与 Android Studio 完全同源的组件:
- emulator 二进制(QEMU/KVM 虚拟机)
- system-images 系统镜像(Android 操作系统镜像)
两者都来自 Google 官方仓库 dl.google.com/android/repository,
通过 sdkmanager/avdmanager 下载和管理------和 Android Studio 的
SDK Manager / Device Manager 是同一个仓库、同一套镜像、同一个 emulator。
-
容器内启动链路(CLI 驱动,Python)
容器启动 -> supervisord -> docker-android start device
-> avdmanager create avd(创建虚拟设备,首次)
-> emulator @avd -gpu swiftshader ...(启动 QEMU,需宿主机 /dev/kvm)
-> adb 轮询 sys.boot_completed / launcher 聚焦(就绪检查)
-> device_status 写 READY -> keep_alive 保活(并检测崩溃自动重启)
-
对外连接方式
- adb:容器暴露 5555 端口,宿主机
adb connect localhost:5555 - noVNC / webVNC:容器暴露 6080 端口,浏览器访问 http://localhost:6080
看画面、点按(x11vnc + noVNC 转发) - VNC:5900 端口
- Appium:4723(可选,镜像已裁剪掉 Node/Appium,需要可自行加)
- 日志共享:9000 端口
- adb:容器暴露 5555 端口,宿主机
-
关键版本信息
- 当前支持:Android 16(API 36),系统镜像 system-images;android-36;google_apis;x86_64
- emulator:37.1.11(sdkmanager 安装的 channel-0 稳定版)
- 架构:仅 x86_64;需要宿主虚拟化 /dev/kvm
修改点涉及
-
多阶段构建瘦身(docker/emulator)
构建阶段(build stage):
- FROM appium/appium:v3.6.0-p0(提供 JDK + cmdline-tools/sdkmanager/avdmanager)
- 安装 system-images;android-36;google_apis;x86_64 + emulator + platform-tools
- 不装 platforms / build-tools(运行期用不到)
运行阶段(runtime stage): - FROM ubuntu:24.04
- 保留:emulator + 系统镜像、adb、JRE(avdmanager 需要)、Python CLI、
supervisord、Xvfb/openbox/x11vnc/noVNC、socat - 砍掉:Node/Appium(约 1GB)、JDK->JRE、build-tools、platforms
- 结果:7.92GB -> 6.64GB
-
CLI 版本支持(Android 16 适配,真正改动只有几行)
-
CLI 健壮性 / 兼容性修复(版本无关,但必要)
-screen multi-touch:保证 guest 创建触摸设备(X/noVNC 点击可用)-feature -Vulkan:禁用 guest Vulkan,规避 SwiftShader Vulkan SIGSEGV- 自愈 keep_alive:检测 emulator 进程退出自动重启(原实现只是死循环)
- DISABLE_SYSTEM_UI:opt-in 开关(Android 17 需要,16 不需要)
-
运行时修复
- noVNC 响应加 Cache-Control: no-cache(防止浏览器混用新旧 JS 模块)
- run.sh 启动时清理残留 X lock(docker restart 后 Xvfb 能正常启动)
- data partition 2G(原 550m 太小导致安装应用空间不足)
- CMD 改为 exec-form(保证 SIGTERM 优雅退出)
- cli/setup.py 修复(py_modules 错误导致 CLI 无法 pip 安装)
- .fehbg 修复(openbox 壁纸 autostart)
修改后编译镜像
- 构建命令
sh
DOCKER_BUILDKIT=1 docker build \
-t yeshen/docker-android:emulator_16.0 \
--build-arg DOCKER_ANDROID_VERSION=<ver> \
--build-arg EMULATOR_ANDROID_VERSION=16.0 \
--build-arg EMULATOR_API_LEVEL=36 \
-f docker/emulator .
-
产物
yeshen/docker-android:emulator_16.0 (版本 tag,滚动更新)
yeshen/docker-android:emulator_16.0_v0.1 (具体发布 tag,与上面同镜像)
体积:6.64GB(原基于 appium 的镜像 7.92GB)
-
启动方式
sh
docker run -d -p 6080:6080 -p 5555:5555 \
-e EMULATOR_DEVICE="Pixel 8" -e WEB_VNC=true \
--device /dev/kvm --name android-16 \
yeshen/docker-android:emulator_16.0
- 访问 http://localhost:6080 看画面
- adb connect localhost:5555 用 adb
- 无需 DISABLE_SYSTEM_UI,SystemUI 完整可用
- 端到端验证结果(Android 16)
- boot 到 READY 约 50 秒
- SystemUI 完整运行,无崩溃
- 网络:wifi 连 AndroidWifi,ping 8.8.8.8 0% 丢失
- 触摸输入:X 点击 -> guest 触摸事件正常
- Chrome 压力:多页面导航 + 快速返回 x12,0 崩溃、qemu 存活
- scrcpy:MediaCodec 编码器可用,可正常投屏
- 单元测试 34 passed
遇到的问题
【阶段一:先试 Android 17(API 37),因上游 bug 放弃】
-
SurfaceFlinger 崩溃循环("模拟器一直重启")
- 现象:framework 每 20~90 秒整体重启,进不去稳定 launcher
- 根因:API 37 系统镜像的 mapper.ranchu gralloc HAL 断言崩溃
(Assertion failed: !rcEnc->featureInfo()->hasReadColorBufferDma)
emulator 的 gfxstream host 广播 ReadColorBufferDMA 特性,但 API 37
的 mapper 预期关闭 -> 版本错配,属上游 bug(API 33~36 均正常) - 处理:DISABLE_SYSTEM_UI=true(禁用 SystemUI 后亮度采样触发源消失)
- 结论:能压住但无法根治;同时连带触摸/投屏/任务快照多个问题
-
触摸输入无效
- 根因:emulator 在 hw.screen=touch 模式下不创建触摸设备(API 37)
- 处理:-screen multi-touch
-
任务快照间歇崩溃
- 现象:打开/切换 App(尤其 Chrome)时 system_server 崩溃
- 根因:TaskSnapshotPersister 截取窗口快照走同一 mapper.ranchu 读回路径
- 处理:无运行时开关(编译期配置),靠自愈兜底
-
scrcpy / screencap 不可用
- 根因:编码器与截屏都走 gralloc 读回 -> 同一断言
- 处理:无法规避,等上游修复
-
"CPU 494%" 假象
- ps -o %cpu 是生命周期平均值,被崩溃循环拉高;瞬时采样实际 ~40%
结论:Android 17 无法稳定使用,改为 Android 16(API 33~36 不受影响)。
【阶段二:Android 16 遇到的问题】
-
SwiftShader Vulkan SIGSEGV(打开 Chrome 偶发整个模拟器段错误)
- 现象:emulator 进程 exit code -11(SIGSEGV),崩溃前有
VkInstance application:'Chromium' 日志 - 根因:emulator 37.1.11 的 SwiftShader Vulkan 实现 bug(宿主机侧)
- 处理:-feature -Vulkan 禁用 guest Vulkan,Chrome 退回 GLES 路径
- 验证:修复后 Chrome 压力测试 0 次 SIGSEGV
- 现象:emulator 进程 exit code -11(SIGSEGV),崩溃前有
-
网络"没网"多为崩溃连锁 + wifi 关联慢
- 容器本身网络正常;guest wifi 关联需几分钟,且 framework 崩溃会掉线
- 稳定后 ping 0% 丢失
-
容器重建清空 /data
- 未挂持久卷时 docker rm+run 会清空 /data,应用私有目录需重新初始化,
曾出现应用写失败(目录缺失),补齐目录即可
- 未挂持久卷时 docker rm+run 会清空 /data,应用私有目录需重新初始化,
【阶段三:通用工程问题】
-
app.sh last_key bug
- keys-2 用未排序关联数组序导致 :latest tag 指错版本,改 sorted_keys-2
-
noVNC 浏览器报错 supportsWebCodecsH264Decode
- 服务端文件一致,是浏览器启发式缓存混用新旧 JS 模块
- 处理:websockify 响应加 Cache-Control: no-cache
-
多阶段构建运行时库风险
- emulator 二进制依赖众多系统库,精简镜像需按 ldd 实测补齐
- Java 不能省(avdmanager 创建 AVD 需要 JRE)