Ubuntu Android 虚拟机安装使用教程
在 Ubuntu 上安装、使用、停止、删除 Android 虚拟机的完整操作手册。
涵盖两种方案:Redroid(容器化)和 AVD(Google 官方模拟器)。
适用环境 :Ubuntu 20.04 / 22.04 / 24.04,x86_64 架构
测试验证 :Ubuntu 24.04 + 内核 6.17,实测通过
关键词:Android 虚拟机、Redroid、AVD、Docker、ADB、scrcpy
30 秒快速开始(Redroid)
已有 Docker + binder 模块的情况下,三条命令启动一台 Android 虚拟机:
bash
docker run -d --name android1 --privileged -p 5555:5555 redroid/redroid:12.0.0-latest
sleep 30
adb connect localhost:5555 && adb devices
需要完整环境搭建或使用 AVD,请继续阅读下方详细教程。
目录
- [方案一:Redroid(容器化 Android)](#方案一:Redroid(容器化 Android)) --- 轻量快速,适合批量 CI
- [方案二:AVD(Google 官方模拟器)](#方案二:AVD(Google 官方模拟器)) --- 兼容性最好,支持快照
- 方案对比与选择 --- 不确定用哪个?看这里
- [进阶:容器间连接 Redroid](#进阶:容器间连接 Redroid) --- 测试容器连 Redroid 的三种方式
- [实战经验:SSH 远程操作 AVD](#实战经验:SSH 远程操作 AVD) --- 踩坑总结与解决方案
- 附录:完整生命周期操作一览 --- 速查表
方案一:Redroid(容器化 Android)
在 Docker 容器里运行完整 Android 系统,轻量快速,适合批量测试。
1. 环境准备
1.1 检查系统要求
bash
# 系统版本(需要 Ubuntu 20.04+)
lsb_release -a
# 内核版本(需要 5.0+)
uname -r
# CPU 架构
uname -m
# 需要 x86_64 或 aarch64
1.2 安装 Docker
bash
# 如果已安装 Docker 可跳过
curl -fsSL https://get.docker.com | sudo sh
# 将当前用户加入 docker 组(免 sudo)
sudo usermod -aG docker $USER
# 重新登录生效(或执行)
newgrp docker
# 验证
docker --version
docker run hello-world
1.3 加载内核模块
Redroid 依赖 Linux 内核的 binder 模块(Android IPC 通信机制)。
bash
# 先检查 binder 是否已加载(已加载则跳过后续步骤)
if lsmod | grep -q binder_linux; then
echo "binder 模块已加载,无需操作"
else
# 检查 linux-modules-extra 是否已安装
dpkg -l | grep -q "linux-modules-extra-$(uname -r)" || {
sudo apt update
sudo apt install -y linux-modules-extra-$(uname -r)
}
# 加载 binder 模块
sudo modprobe binder_linux devices="binder,hwbinder,vndbinder"
fi
# 验证加载成功
lsmod | grep binder
# 输出类似:binder_linux 241664 0
# 设置开机自动加载(避免重启后失效)
echo "binder_linux" | sudo tee /etc/modules-load.d/binder.conf
echo "options binder_linux devices=binder,hwbinder,vndbinder" | sudo tee /etc/modprobe.d/binder.conf
⚠️ 常见问题 :如果
modprobe binder_linux报错Module not found:
bash
# 1. 确认 linux-modules-extra 已安装
sudo apt install -y linux-modules-extra-$(uname -r)
sudo modprobe binder_linux devices="binder,hwbinder,vndbinder"
# 2. 如果仍然失败,检查 binderfs(部分内核已内置)
ls /dev/binderfs/ 2>/dev/null && echo "已内置 binderfs,无需额外加载"
# 3. 如果以上都不行,需要升级到支持 binder 的内核版本
# Ubuntu 22.04:
# sudo apt install -y linux-generic-hwe-22.04
# Ubuntu 24.04(通常已自带,无需额外操作):
# sudo apt install -y linux-generic-hwe-24.04
# 升级后需重启:sudo reboot
1.4 安装 ADB
bash
sudo apt install -y android-tools-adb
# 验证
adb version
2. 创建并启动虚拟机
2.1 拉取 Redroid 镜像
bash
# Android 12(推荐,兼容性较好)
docker pull redroid/redroid:12.0.0-latest
# Android 13
docker pull redroid/redroid:13.0.0-latest
# Android 14
docker pull redroid/redroid:14.0.0-latest
# 查看已下载的镜像
docker images | grep redroid
2.2 启动单台虚拟机
bash
# 基础启动(软件渲染,最通用)
docker run -d \
--name android1 \
--privileged \
--memory 2g \
-p 5555:5555 \
redroid/redroid:12.0.0-latest \
androidboot.redroid_gpu_mode=guest \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920 \
androidboot.redroid_fps=60
# 检查宿主机是否支持 GPU 直通
ls -la /dev/dri # 存在 card0、renderD128 等设备文件则支持
lspci | grep -i vga # 查看 GPU 设备信息
lsmod | grep -i drm # 检查内核 DRM 模块是否加载
# 如果 /dev/dri 不存在:无独显 或 GPU 驱动未安装(Intel=i915, NVIDIA=nvidia-drm, AMD=amdgpu)
# 带 GPU 加速(宿主机有独立显卡时)
docker run -d \
--name android1 \
--privileged \
--memory 2g \
--device /dev/dri:/dev/dri \
-p 5555:5555 \
redroid/redroid:12.0.0-latest \
androidboot.redroid_gpu_mode=host \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920 \
androidboot.redroid_fps=60
参数说明:
| 参数 | 含义 |
|---|---|
-d |
后台运行 |
--name android1 |
容器命名(方便后续操作) |
--privileged |
赋予特权(Redroid 必需) |
--memory 2g |
限制内存 2GB |
-p 5555:5555 |
将容器 ADB 端口映射到宿主机 |
--device /dev/dri:/dev/dri |
GPU 直通(可选) |
androidboot.redroid_gpu_mode |
guest=软件渲染,host=GPU 直通 |
androidboot.redroid_width/height |
屏幕分辨率 |
androidboot.redroid_fps |
帧率上限 |
💡 选 guest 还是 host? 如果宿主机有 NVIDIA/AMD 独显且已装驱动,用
host性能更好。否则(集显、无显卡服务器)统一用guest,稳定不报错。
2.3 启动多台虚拟机
bash
# 启动 3 台(端口分别映射到 5555/5556/5557)
for i in 1 2 3; do
docker run -d \
--name "android${i}" \
--privileged \
--memory 2g \
-p "555$((i+4)):5555" \
redroid/redroid:12.0.0-latest \
androidboot.redroid_gpu_mode=guest \
androidboot.redroid_width=1080 \
androidboot.redroid_height=1920
done
# 查看运行状态
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
2.4 等待启动完成
bash
# Redroid 启动需要约 20-30 秒
echo "等待 Android 系统启动..."
sleep 30
# 或者循环检测
timeout 60 bash -c '
while ! adb connect localhost:5555 2>/dev/null | grep -q "connected"; do
sleep 2
done
'
echo "设备就绪"
3. 连接与使用
3.1 ADB 连接
bash
# 连接单台
adb connect localhost:5555
# 连接多台
adb connect localhost:5555
adb connect localhost:5556
adb connect localhost:5557
# 查看已连接设备
adb devices
# 输出示例:
# localhost:5555 device
# localhost:5556 device
# localhost:5557 device
3.2 安装 App
bash
# 安装到默认设备
adb install ~/Downloads/MyApp.apk
# 安装到指定设备
adb -s localhost:5555 install ~/Downloads/MyApp.apk
# 覆盖安装(已存在时)
adb -s localhost:5555 install -r ~/Downloads/MyApp.apk
# 批量安装到所有设备
for port in 5555 5556 5557; do
adb -s "localhost:${port}" install ~/Downloads/MyApp.apk &
done
wait
echo "全部安装完成"
3.3 启动 App
bash
# 启动 App
adb -s localhost:5555 shell am start -n com.example.myapp/.MainActivity
# 查看当前前台 Activity
adb -s localhost:5555 shell dumpsys activity activities | grep mResumedActivity
3.4 基本交互操作
bash
# 模拟点击(x=540, y=960,屏幕中央)
adb -s localhost:5555 shell input tap 540 960
# 模拟滑动(从 (500,1500) 滑到 (500,500),持续 300ms)
adb -s localhost:5555 shell input swipe 500 1500 500 500 300
# 模拟按键(HOME 键)
adb -s localhost:5555 shell input keyevent KEYCODE_HOME
# 输入文字
adb -s localhost:5555 shell input text "hello"
# 截图
adb -s localhost:5555 shell screencap -p /sdcard/screenshot.png
adb -s localhost:5555 pull /sdcard/screenshot.png ./screenshot.png
# 录屏(最长 180 秒)
adb -s localhost:5555 shell screenrecord /sdcard/demo.mp4
# Ctrl+C 停止后拉取
adb -s localhost:5555 pull /sdcard/demo.mp4 ./
3.5 模拟 GPS 定位
bash
# 需要先开启 mock location
adb -s localhost:5555 shell appops set com.example.myapp android:mock_location allow
# 方法一:使用 mock location App(推荐)
# 安装一个 GPS mock 工具如 Lockito,在工具中设置坐标
# 方法二:通过开发者工具 API 注入(需要 App 支持)
adb -s localhost:5555 shell am broadcast \
-a android.intent.action.MOCK_LOCATION \
--ef latitude 39.9042 --ef longitude 116.4074
3.6 文件传输
bash
# 推送文件到设备
adb -s localhost:5555 push ./testdata.json /sdcard/Download/
# 从设备拉取文件
adb -s localhost:5555 pull /sdcard/Download/result.log ./
# 查看设备文件列表
adb -s localhost:5555 shell ls /sdcard/
3.7 查看设备信息
bash
# Android 版本
adb -s localhost:5555 shell getprop ro.build.version.release
# 屏幕分辨率
adb -s localhost:5555 shell wm size
# 已安装的包
adb -s localhost:5555 shell pm list packages | grep example
# 设备内存信息
adb -s localhost:5555 shell cat /proc/meminfo | head -5
3.8 查看 Android 界面(scrcpy)
Redroid 是无头容器,没有自带窗口。需要通过 scrcpy 在 Ubuntu 桌面上显示 Android 画面并支持鼠标键盘交互。
bash
# 安装 scrcpy(仅需一次)
sudo apt install -y scrcpy
# 确保已连接设备
adb connect localhost:5555
# 在 Ubuntu 桌面终端中启动(会弹出 Android 画面窗口)
scrcpy -s localhost:5555
# 常用参数
scrcpy -s localhost:5555 --window-title "Android 1" # 自定义窗口标题
scrcpy -s localhost:5555 --max-size 800 # 限制窗口最大尺寸
scrcpy -s localhost:5555 --bit-rate 4M # 降低码率(减少卡顿)
scrcpy -s localhost:5555 --no-audio # 不转发音频
# 多台设备同时查看(每台一个窗口)
scrcpy -s localhost:5555 --window-title "设备1" &
scrcpy -s localhost:5556 --window-title "设备2" &
注意事项:
- scrcpy 必须在有桌面环境的终端中执行(Ubuntu 本机桌面或 X11 转发),SSH 无桌面环境下无法弹窗
- 如果报错
ERROR: Could not open video stream,等几秒重试(Android 系统可能还没完全启动) - scrcpy 窗口中可以直接用鼠标点击、拖拽、键盘输入,操作方式与手机一致
💡 SSH 远程想看界面? 参见本文「实战经验」部分的「远程查看模拟器界面」,通过 SSH 端口转发 + scrcpy 可以将画面串流到本地电脑。
4. 停止虚拟机
4.1 停止单台
bash
# 停止容器(Android 系统关机,容器保留,可以再次启动)
docker stop android1
# 查看状态(STATUS 显示 Exited)
docker ps -a --filter name=android1
4.2 停止多台
bash
# 停止所有 android 开头的容器
docker stop $(docker ps -q --filter "name=android")
# 或逐个停止
docker stop android1 android2 android3
4.3 重新启动(停止后恢复)
bash
# 启动之前停止的容器(数据保留,App 还在)
docker start android1
# 等待 Android 启动
sleep 20
adb connect localhost:5555
5. 删除虚拟机
5.1 删除单台
bash
# 先停止再删除
docker stop android1
docker rm android1
# 或者强制删除(即使正在运行)
docker rm -f android1
⚠️
docker rm会删除容器内所有数据 (已安装的 App、文件等),不可恢复。如果只是暂时不用,用docker stop保留状态。
5.2 删除多台
bash
# 删除所有 android 开头的容器
docker rm -f $(docker ps -a -q --filter "name=android")
# 验证已清除
docker ps -a | grep android
# 应该没有输出
5.3 删除镜像(彻底清理)
bash
# 查看 Redroid 镜像
docker images | grep redroid
# 删除指定镜像
docker rmi redroid/redroid:12.0.0-latest
# 删除所有 Redroid 镜像
docker rmi $(docker images -q redroid/redroid)
# 清理悬空镜像(释放磁盘)
docker image prune -f
5.4 完全卸载(恢复干净环境)
bash
# 1. 删除所有 Redroid 容器
docker rm -f $(docker ps -a -q --filter "name=android") 2>/dev/null
docker rm -f $(docker ps -a -q --filter ancestor=redroid/redroid) 2>/dev/null
# 2. 删除所有 Redroid 镜像
docker rmi $(docker images -q redroid/redroid) 2>/dev/null
# 3. 卸载 binder 模块(可选)
sudo modprobe -r binder_linux
sudo rm -f /etc/modules-load.d/binder.conf
sudo rm -f /etc/modprobe.d/binder.conf
# 4. 如果不再需要 Docker
# sudo apt remove --purge docker-ce docker-ce-cli containerd.io
# sudo rm -rf /var/lib/docker
6. 日常管理命令速查
bash
# ===== 查看状态 =====
docker ps # 运行中的容器
docker ps -a # 所有容器(含已停止)
docker ps -a --filter name=android # 只看 android 相关
# ===== 生命周期 =====
docker run -d ... # 创建并启动(新建)
docker start <name> # 启动已停止的容器
docker stop <name> # 优雅停止(等待关机)
docker restart <name> # 重启
docker rm -f <name> # 强制删除
# ===== 调试 =====
docker logs <name> # 查看容器日志
docker logs -f <name> # 实时跟踪日志
docker exec -it <name> sh # 进入容器内部 shell
docker stats # 实时资源占用(CPU/内存)
# ===== 磁盘清理 =====
docker system df # 查看 Docker 磁盘占用
docker image prune # 清理无用镜像
docker container prune # 清理已停止的容器
方案二:AVD(Google 官方模拟器)
使用 Google 官方 Android Emulator,兼容性最好,支持快照秒级恢复。
1. 环境准备
1.1 安装 KVM(硬件加速)
AVD 在 Linux 上使用 KVM 虚拟化加速,没有 KVM 会极慢(不可用级别)。
bash
# 检查 CPU 是否支持虚拟化
egrep -c '(vmx|svm)' /proc/cpuinfo
# 输出 > 0 表示支持
# 安装 KVM
sudo apt update
sudo apt install -y qemu-kvm libvirt-daemon-system
# 将用户加入 kvm 组
sudo usermod -aG kvm $USER
newgrp kvm
# 验证 KVM 可用
ls /dev/kvm
# 输出 /dev/kvm 表示成功
# 额外验证工具
sudo apt install -y cpu-checker
kvm-ok
# 输出 "KVM acceleration can be used" 表示成功
1.2 安装 Java
bash
sudo apt install -y openjdk-17-jdk
java -version
# 输出:openjdk version "17.x.x"
1.3 安装 Android SDK Command Line Tools
bash
# 创建 SDK 目录
mkdir -p ~/android-sdk/cmdline-tools
# 下载(替换为最新版本号)
cd /tmp
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
# 解压到正确位置
unzip commandlinetools-linux-*.zip -d ~/android-sdk/cmdline-tools
mv ~/android-sdk/cmdline-tools/cmdline-tools ~/android-sdk/cmdline-tools/latest
# 设置环境变量(写入 ~/.bashrc 或 ~/.zshrc)
cat >> ~/.bashrc << 'EOF'
export ANDROID_HOME=~/android-sdk
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
export PATH=$PATH:$ANDROID_HOME/platform-tools
export PATH=$PATH:$ANDROID_HOME/emulator
EOF
source ~/.bashrc
# 验证
sdkmanager --version
1.4 安装模拟器组件
bash
# 接受所有许可
yes | sdkmanager --licenses
# 安装核心组件
sdkmanager "platform-tools" # ADB 等工具
sdkmanager "emulator" # 模拟器引擎
# 安装系统镜像(根据需要选择版本)
# Android 14 (API 34) - x86_64 架构(Intel/AMD CPU)
sdkmanager "system-images;android-34;google_apis;x86_64"
# Android 13 (API 33)
sdkmanager "system-images;android-33;google_apis;x86_64"
# Android 12 (API 31)
sdkmanager "system-images;android-31;google_apis;x86_64"
# 查看已安装的镜像
sdkmanager --list_installed | grep system-images
2. 创建虚拟机
2.1 创建单台设备
bash
# 创建 AVD(Android Virtual Device)
avdmanager create avd \
--name "test_phone" \
--package "system-images;android-34;google_apis;x86_64" \
--device "pixel_6"
# 查看已创建的设备列表
avdmanager list avd
# 查看所有可选的设备模板
avdmanager list device
--device 参数说明
--device 指定模拟器的硬件配置模板(屏幕尺寸、分辨率、RAM 等),不同设备模板模拟不同的物理手机/平板规格:
| 设备模板 | 屏幕尺寸 | 分辨率 | 典型用途 |
|---|---|---|---|
pixel_6 |
6.4 英寸 | 1080×2400 | 主流手机,推荐默认选择 |
pixel_7 |
6.3 英寸 | 1080×2400 | 同上,略新 |
pixel_7_pro |
6.7 英寸 | 1440×3120 | 大屏高分辨率手机 |
pixel_tablet |
10.95 英寸 | 1600×2560 | 平板(测试平板适配) |
Nexus 5X |
5.2 英寸 | 1080×1920 | 小屏旧机型(测试低配兼容) |
Nexus 10 |
10.1 英寸 | 1600×2560 | 旧平板 |
automotive_1024p_landscape |
车机横屏 | 1024×768 | 车载系统(导航测试适用) |
选择建议:
- 常规 App 测试 →
pixel_6(主流分辨率,覆盖面最广) - 车载导航 App 测试 →
automotive_1024p_landscape(模拟车载屏幕) - 平板适配测试 →
pixel_tablet - 低配兼容性测试 →
Nexus 5X(小屏、低分辨率) - 高分屏渲染测试 →
pixel_7_pro(2K 分辨率)
注意:--device 只决定屏幕和硬件参数,Android 系统版本由 --package 中的 API 级别决定。同一个设备模板可以搭配不同 Android 版本。
2.2 创建多台设备
bash
# 创建 3 台不同配置的设备
avdmanager create avd \
--name "device_api34" \
--package "system-images;android-34;google_apis;x86_64" \
--device "pixel_6"
avdmanager create avd \
--name "device_api33" \
--package "system-images;android-33;google_apis;x86_64" \
--device "pixel_6"
avdmanager create avd \
--name "device_api31" \
--package "system-images;android-31;google_apis;x86_64" \
--device "pixel_6"
# 列出所有设备
avdmanager list avd
2.3 自定义设备配置
AVD 配置文件位于 ~/.android/avd/<name>.avd/config.ini:
bash
# 修改内存大小
# 编辑 ~/.android/avd/test_phone.avd/config.ini
# hw.ramSize=4096 # 4GB RAM
# disk.dataPartition.size=8G # 8GB 存储
# hw.lcd.width=1080
# hw.lcd.height=2400
3. 启动虚拟机
3.1 GUI 模式(有桌面环境时)
bash
# 带窗口启动(可以看到 Android 界面)
emulator -avd test_phone
# 指定窗口大小
emulator -avd test_phone -skin 1080x1920
3.2 Headless 模式(服务器/CI 用)
bash
# 无窗口无声音(最常用)
emulator -avd test_phone \
-no-window \
-no-audio \
-no-boot-anim \
-gpu swiftshader_indirect
# 后台启动
emulator -avd test_phone \
-no-window \
-no-audio \
-no-boot-anim \
-gpu swiftshader_indirect &
EMULATOR_PID=$!
echo "模拟器 PID: $EMULATOR_PID"
3.3 等待启动完成
bash
# 等待设备上线
adb wait-for-device
# 等待系统完全启动(关键!不等会导致后续操作失败)
echo "等待系统启动..."
while [ "$(adb shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
sleep 2
done
echo "设备已就绪"
# 一行命令版本(带超时)
timeout 120 bash -c 'adb wait-for-device && while [ "$(adb shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do sleep 2; done' && echo "就绪" || echo "超时"
3.4 多实例启动
bash
# 每个 AVD 实例用不同端口
emulator -avd device_api34 -no-window -no-audio -port 5554 &
emulator -avd device_api33 -no-window -no-audio -port 5556 &
emulator -avd device_api31 -no-window -no-audio -port 5558 &
# 等待所有设备就绪
for port in 5554 5556 5558; do
adb -s "emulator-${port}" wait-for-device
while [ "$(adb -s emulator-${port} shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
sleep 2
done
echo "emulator-${port} 就绪"
done
3.5 使用 Snapshot 加速启动(强烈推荐)
💡 为什么用快照? 首次冷启动需要 60-120 秒,但保存快照后每次启动只需 5-10 秒,效率提升 10 倍以上。CI 环境中几乎必备。
首次启动较慢(60-120s),但保存快照后,后续启动只需 5-10 秒。
bash
# 第一次:正常启动,等系统完全启动后保存快照
emulator -avd test_phone -no-window -no-audio &
sleep 60 # 等启动
adb wait-for-device
adb shell getprop sys.boot_completed # 确认输出 "1"
# 保存快照
adb emu avd snapshot save clean_state
echo "快照已保存"
# 关闭模拟器
adb emu kill
# 后续启动:直接加载快照(5-10秒就绪)
emulator -avd test_phone \
-no-window \
-no-audio \
-snapshot clean_state \
-no-snapshot-save &
# 几乎立即就绪
sleep 5
adb shell getprop sys.boot_completed # 输出 "1"
4. 使用虚拟机
使用方式与 Redroid 的 ADB 操作完全相同:
bash
# 查看设备
adb devices
# 输出:emulator-5554 device
# 安装 App
adb install ~/Downloads/MyApp.apk
# 启动 App
adb shell am start -n com.example.myapp/.MainActivity
# 模拟点击/滑动
adb shell input tap 540 960
adb shell input swipe 500 1500 500 500 300
# 截图
adb shell screencap -p /sdcard/screen.png
adb pull /sdcard/screen.png ./
# 模拟 GPS(AVD 专属,比 Redroid 更方便)
adb emu geo fix 116.4074 39.9042 # 经度 纬度(北京)
adb emu geo fix 121.4737 31.2304 # 上海
# 模拟电话/短信(AVD 专属)
adb emu sms send 13800138000 "测试短信"
# 模拟网络状态(AVD 专属)
adb emu network speed gsm # 2G 网速
adb emu network speed lte # 4G 网速
adb emu network speed full # 无限制
adb emu network delay gprs # 高延迟
5. 停止虚拟机
5.1 优雅关闭
bash
# 方式一:通过 ADB 关机(推荐,保存状态)
adb emu kill
# 方式二:通过 ADB 关闭设备电源
adb shell reboot -p
# 方式三:发送关闭信号(如果知道 PID)
kill $EMULATOR_PID
5.2 强制关闭
bash
# 找到模拟器进程
ps aux | grep emulator | grep -v grep
# 强制杀死
kill -9 $(pgrep -f "emulator.*test_phone")
# 或杀死所有模拟器进程
pkill -f "qemu-system-x86_64"
5.3 关闭多台
bash
# 关闭所有运行中的模拟器
adb devices | grep emulator | awk '{print $1}' | while read device; do
adb -s "$device" emu kill
done
# 或直接杀进程
pkill -f emulator
6. 删除虚拟机
6.1 删除单台 AVD
bash
# 方式一:使用 avdmanager 删除
avdmanager delete avd --name test_phone
# 方式二:手动删除文件
rm -rf ~/.android/avd/test_phone.avd
rm -f ~/.android/avd/test_phone.ini
# 验证已删除
avdmanager list avd
6.2 删除多台 AVD
bash
# 删除所有 AVD
for avd in $(avdmanager list avd -c); do
avdmanager delete avd --name "$avd"
echo "已删除: $avd"
done
6.3 删除系统镜像(释放磁盘)
bash
# 查看已安装的镜像(每个约 1-3GB)
sdkmanager --list_installed | grep system-images
# 删除指定镜像
sdkmanager --uninstall "system-images;android-34;google_apis;x86_64"
# 查看 SDK 占用空间
du -sh ~/android-sdk/system-images/*
6.4 删除快照
bash
# 列出快照
adb emu avd snapshot list
# 删除指定快照
adb emu avd snapshot delete clean_state
# 快照文件位置(手动清理)
ls ~/.android/avd/test_phone.avd/snapshots/
rm -rf ~/.android/avd/test_phone.avd/snapshots/clean_state
6.5 完全卸载(恢复干净环境)
bash
# 1. 杀死所有模拟器
pkill -f emulator 2>/dev/null
# 2. 删除所有 AVD
for avd in $(avdmanager list avd -c 2>/dev/null); do
avdmanager delete avd --name "$avd"
done
# 3. 删除 Android SDK(约 5-20GB)
rm -rf ~/android-sdk
# 4. 删除 AVD 配置和缓存
rm -rf ~/.android/avd
rm -rf ~/.android/cache
# 5. 清理环境变量(从 ~/.bashrc 中移除 ANDROID_HOME 相关行)
sed -i '/ANDROID_HOME/d' ~/.bashrc
sed -i '/android-sdk/d' ~/.bashrc
source ~/.bashrc
7. 日常管理命令速查
bash
# ===== 设备管理 =====
avdmanager list avd # 列出所有 AVD
avdmanager create avd --name X ... # 创建
avdmanager delete avd --name X # 删除
# ===== 启动/停止 =====
emulator -avd X -no-window & # 启动(后台)
adb emu kill # 优雅关闭
pkill -f emulator # 强制关闭所有
# ===== 快照 =====
adb emu avd snapshot save <name> # 保存快照
adb emu avd snapshot load <name> # 加载快照
adb emu avd snapshot delete <name> # 删除快照
emulator -avd X -snapshot <name> # 从快照启动
# ===== 镜像管理 =====
sdkmanager --list_installed # 查看已安装
sdkmanager "system-images;..." # 安装镜像
sdkmanager --uninstall "system-..." # 卸载镜像
方案对比与选择
| 维度 | Redroid | AVD |
|---|---|---|
| 安装难度 | 中(需要 Docker + binder 模块) | 中(需要 KVM + SDK) |
| 启动速度 | 20-30 秒 | 冷启动 60-120s,snapshot 5-10s |
| 资源占用 | 低(1GB/台) | 高(2-4GB/台) |
| 并发能力 | 10+ 台 | 3-4 台 |
| App 兼容性 | 一般(部分 App 有兼容问题) | 好(Google 官方) |
| GPU 渲染 | 依赖直通,可能有兼容问题 | swiftshader 软件渲染稳定 |
| GPS 模拟 | 需要第三方工具 | 内置支持(adb emu geo fix) |
| 网络模拟 | 不支持 | 支持(限速、延迟) |
| 适合场景 | 大规模 CI、批量并发 | 开发调试、兼容性要求高 |
建议:
- 需要大量并发、跑自动化 → Redroid
- 需要真实 App 表现、调试单个问题 → AVD
- 两种结合使用效果最好:日常 CI 用 Redroid 提速,遇到兼容性问题时用 AVD 复现
💡 快速选择:不确定选哪个?先试 Redroid,三条命令就能跑起来。遇到 App 兼容性问题再换 AVD。
进阶:容器间连接 Redroid
当你的测试程序本身也跑在 Docker 容器中(如 CI/CD 流水线),需要从容器内部连接 Redroid 设备。
方式一:同一 Docker 网络(推荐)
将测试容器和 Redroid 容器加入同一个自定义 bridge 网络,通过容器 IP 或容器名互通。
bash
# 1. 创建自定义网络
docker network create test_net
# 2. 启动 Redroid 并加入网络
docker run -d \
--name android1 \
--privileged \
--network test_net \
--memory 2g \
redroid/redroid:12.0.0-latest \
androidboot.redroid_gpu_mode=guest
# 3. 获取 Redroid 容器 IP
ANDROID_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' android1)
echo "Redroid IP: $ANDROID_IP"
# 4. 启动测试容器(同一网络),注入 IP 作为环境变量
docker run -it --rm \
--network test_net \
-e TARGET_IP="$ANDROID_IP" \
my-test-image:latest /bin/bash
# 5. 在测试容器内连接 Redroid
adb connect ${TARGET_IP}:5555
adb devices
💡 为什么用自定义网络而不是默认 bridge?
自定义网络支持容器名 DNS 解析(
adb connect android1:5555也能用),且提供隔离------多条流水线并发时各自网络互不干扰。
方式二:宿主机端口映射
如果测试容器使用 --network host 模式,或 Redroid 已做端口映射(-p 5555:5555),可通过宿主机 IP 连接:
bash
# Redroid 启动时已有 -p 5555:5555
# 测试容器中通过宿主机 IP(Docker 内部 gateway)连接
adb connect host.docker.internal:5555 # Docker Desktop
adb connect 172.17.0.1:5555 # Linux Docker 默认网关
⚠️ 注意 :
host.docker.internal仅在 Docker Desktop (macOS/Windows) 中可用。Linux 下用宿主机在 docker0 网桥的 IP(通常172.17.0.1),或用--add-host host.docker.internal:host-gateway。
方式三:完整流水线示例
模拟真实 CI 环境------创建网络、启动多台 Redroid、在测试容器中并发执行:
bash
#!/bin/bash
NETWORK="ci_net_$$"
trap 'docker rm -f android_a android_b 2>/dev/null; docker network rm $NETWORK 2>/dev/null' EXIT
# 创建隔离网络
docker network create $NETWORK
# 启动 2 台 Redroid
for name in android_a android_b; do
docker run -d --name $name --privileged --network $NETWORK \
--memory 2g redroid/redroid:12.0.0-latest \
androidboot.redroid_gpu_mode=guest
done
sleep 30 # 等待 Android 启动
# 获取 IP
IP_A=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' android_a)
IP_B=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' android_b)
# 启动测试容器,执行测试
docker run --rm --network $NETWORK \
-e TARGET_IP_1="$IP_A" \
-e TARGET_IP_2="$IP_B" \
my-test-image:latest bash -c '
adb connect ${TARGET_IP_1}:5555
adb connect ${TARGET_IP_2}:5555
adb devices
# 执行测试...
'
清理
bash
# 删除容器和网络
docker rm -f android_a android_b
docker network rm ci_net_$$
实战经验:SSH 远程操作 AVD
以下是在实际 Ubuntu 机器(内核 6.17)上通过 SSH 操作 AVD 的经验总结。
1. SSH 远程启动模拟器
SSH 连接远程 Ubuntu 时,没有图形界面,必须用 headless 模式:
bash
# 后台启动(nohup 防止 SSH 断开后进程退出)
nohup emulator -avd ci_device \
-no-window \
-no-audio \
-no-boot-anim \
-gpu swiftshader_indirect \
-port 5580 > /tmp/emulator.log 2>&1 &
# 自定义端口说明:
# -port 5580 → 占用 5580(控制台) + 5581(ADB)
# 设备序列号变为 emulator-5580
# 避免与其他程序冲突(默认 5554/5555 可能被占用)
2. 多设备环境下指定操作目标
如果机器同时连接了真机和模拟器,ADB 命令必须 加 -s 指定设备:
bash
# 查看所有设备
adb devices -l
# 输出示例:
# a1b2c3d4 device product:xxx model:Phone_Model_1 ← 真机 1
# SERIAL00112233 device product:yyy model:Tablet_Model_1 ← 真机(平板)
# XYZ98765 device product:zzz model:Tablet_Model_2 ← 真机(平板)
# emulator-5580 device product:sdk_gphone64_x86_64 ← 模拟器
# 操作模拟器时指定序列号
adb -s emulator-5580 install app.apk
adb -s emulator-5580 shell am start -n com.example.myapp/.MainActivity
# 不加 -s 会报错:error: more than one device/emulator
3. 被测 App 包名确认
项目中使用的被测 App 包名需根据实际情况替换:
bash
# 正确的包名和 Activity(根据你的 App 修改)
PACKAGE="com.example.myapp"
ACTIVITY="com.example.myapp/.MainActivity"
# 安装
adb -s emulator-5580 install MyApp.apk
# 启动
adb -s emulator-5580 shell am start -n com.example.myapp/.MainActivity
# 查看设备上已安装的包名(帮助确认正确包名)
adb -s emulator-5580 shell pm list packages | grep myapp
4. 处理无 GUI 下的弹窗
SSH 模式下看不到界面,App 启动时可能有弹窗(兼容性提示、权限请求等),通过 ADB 处理:
bash
# 方法一:模拟点击关闭弹窗(OK 按钮通常在屏幕下方中间)
adb -s emulator-5580 shell input tap 540 1580
sleep 1
adb -s emulator-5580 shell input tap 540 1580
# 方法二:按回车键(部分弹窗支持)
adb -s emulator-5580 shell input keyevent KEYCODE_ENTER
# 方法三:用 uiautomator 精确定位按钮
adb -s emulator-5580 shell uiautomator dump /sdcard/ui.xml
adb -s emulator-5580 pull /sdcard/ui.xml /tmp/ui.xml
# 在 xml 中搜索 "OK" 或 "确定" 找到 bounds 坐标
# 方法四:批量处理多个弹窗
for i in 1 2 3 4 5; do
adb -s emulator-5580 shell input tap 540 1580
sleep 2
done
5. 远程查看模拟器界面
方法一:截图(最简单,SSH 直接用)
bash
# 截图并拉到本地
adb -s emulator-5580 shell screencap -p /sdcard/s.png
adb -s emulator-5580 pull /sdcard/s.png /tmp/s.png
# 在 Mac 上拉过来查看
scp map@<Ubuntu的IP>:/tmp/s.png ~/Desktop/
open ~/Desktop/s.png
方法二:scrcpy 实时串流到 Mac
bash
# Mac 上安装
brew install scrcpy
# Mac 上 SSH 端口转发
ssh -L 5580:localhost:5580 -L 5581:localhost:5581 map@<Ubuntu的IP>
# Mac 上连接并查看
adb connect localhost:5581
scrcpy -s localhost:5581
方法三:Ubuntu 显示器直接看(机器在旁边时)
bash
# 停掉无头模式
adb -s emulator-5580 emu kill
# 在 Ubuntu 桌面终端(非 SSH)中用 GUI 模式启动
emulator -avd ci_device -gpu swiftshader_indirect -port 5580
方法四:SSH X11 转发到 Mac
bash
# Mac 需要先装 XQuartz
brew install --cask xquartz
# 重新登录 Mac
# SSH 时加 -X 参数
ssh -X map@<Ubuntu的IP>
# 远程启动 GUI 模式(窗口显示在 Mac 上,可能较卡)
emulator -avd ci_device -gpu swiftshader_indirect -port 5580
6. 模拟器与流水线共存注意事项
如果 Ubuntu 机器同时跑自动化测试流水线:
bash
# 问题:模拟器会出现在 adb devices 列表中,可能干扰自动化设备分配
# 自动化脚本通过 --device-index 按索引选设备,模拟器会导致索引偏移
# 解决方案一:跑自动化前停掉模拟器(推荐)
adb -s emulator-5580 emu kill
# 解决方案二:使用高端口号,减少冲突概率
emulator -avd ci_device -port 5680 # 远离常用端口范围
# 解决方案三:流水线脚本中过滤掉模拟器(长期方案)
# 在设备发现逻辑中排除 emulator-* 开头的设备
7. 启动失败排查
bash
# 查看启动日志
cat /tmp/emulator.log
# 常见错误及解决:
# 1. "Unknown AVD name [xxx]" → AVD 未创建,先执行 avdmanager create avd
# 2. "KVM is not installed" → 执行 sudo apt install qemu-kvm && sudo usermod -aG kvm $USER
# 3. "Could not initialize display" → 忘加 -no-window,SSH 下必须无头模式
# 4. 退出码 1 无明显错误 → 检查磁盘空间 df -h,AVD 需要约 5GB 空间
# 确认 AVD 存在
avdmanager list avd
# 确认 KVM 可用
ls /dev/kvm
# 确认磁盘空间
df -h ~/.android/
8. GPU 和 Qt 相关启动失败(常见!)
⚠️ 重点提醒 :这是 SSH 远程使用 AVD 最常踩的坑。记住一个原则:SSH 下永远用
-gpu swiftshader_indirect -no-window,不要尝试-gpu host。
在 SSH 远程或无桌面环境下用 -gpu host 启动,大概率遇到以下错误:
错误现象
ERROR | Your GPU cannot be used for hardware rendering. Consider using software rendering.
Warning: could not connect to display (:0, )
Warning: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
Fatal: This application failed to start because no Qt platform plugin could be initialized.
Available platform plugins are: minimal, offscreen, vnc, xcb, linuxfb.
已中止
原因分析
| 错误信息 | 原因 |
|---|---|
Your GPU cannot be used for hardware rendering |
Intel 集显(如 ARL)不被模拟器支持做 host 渲染 |
could not connect to display (:0, ) |
SSH 会话没有 X11 显示连接 |
xcb-cursor0 is needed |
缺少 Qt 依赖库 libxcb-cursor0 |
no Qt platform plugin could be initialized |
以上问题综合导致 GUI 无法启动 |
解决方案
SSH 远程使用(推荐):使用无头模式 + 软件渲染,完全避开 GPU 和 Qt 问题:
bash
emulator -avd ci_device -no-window -no-audio -gpu swiftshader_indirect -port 5580 -no-metrics &
Ubuntu 本地桌面使用:需安装 Qt 依赖,且必须在本地桌面终端(非 SSH)执行:
bash
# 安装缺失的依赖
sudo apt install -y libxcb-cursor0 mesa-utils
# 在本地桌面终端启动(用软件渲染)
emulator -avd ci_device -gpu swiftshader_indirect -port 5580 -no-metrics
GPU 模式说明
| 参数 | 含义 | 适用场景 |
|---|---|---|
-gpu host |
使用宿主机 GPU 硬件加速 | 仅限支持的 NVIDIA/AMD 独显 + 有桌面环境 |
-gpu swiftshader_indirect |
Google 软件渲染引擎 | SSH/无头/集显环境必选,兼容性最好 |
-gpu guest |
设备内软件渲染 | 最慢,仅作最后降级方案 |
关键结论 :Intel 集成显卡(如 ARL、UHD 系列)不支持 -gpu host,必须用 -gpu swiftshader_indirect。大多数服务器/办公机都是集显,养成默认用 swiftshader_indirect 的习惯。
9. 模拟器上跑稳定性测试的可行性
| 操作 | 模拟器能否执行 | 说明 |
|---|---|---|
adb install 安装 APK |
能 | 正常 |
adb push 推送文件 |
能 | 正常 |
adb shell am start 启动 App |
能 | 注意正确包名 |
adb shell input 模拟操作 |
能 | 坐标需适配分辨率 |
| 地图 GPU 渲染 | 可能有问题 | swiftshader 软件渲染,可能黑屏或 ANR |
| GPS 定位/导航 | 需要 mock | adb emu geo fix 经度 纬度 |
| 性能数据采集 | 无参考价值 | CPU/内存/帧率与真机差异大 |
结论:功能验证可以尝试,性能/稳定性测试必须用真机。
附录:完整生命周期操作一览
┌─────────────────────────────────────────────────────────┐
│ Redroid 生命周期 │
├─────────────────────────────────────────────────────────┤
│ docker pull → docker run -d → adb connect │
│ (下载镜像) (创建启动) (连接使用) │
│ │
│ docker stop → docker start (停止 ↔ 恢复,数据保留) │
│ │
│ docker rm -f → docker rmi (删除容器 → 删除镜像) │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ AVD 生命周期 │
├─────────────────────────────────────────────────────────┤
│ sdkmanager → avdmanager create → emulator -avd │
│ (下载镜像) (创建设备) (启动) │
│ │
│ adb emu kill → emulator -avd (停止 → 重新启动) │
│ │
│ avdmanager delete → sdkmanager --uninstall │
│ (删除设备) (删除镜像) │
└─────────────────────────────────────────────────────────┘
参考资料
| 资源 | 说明 |
|---|---|
| Redroid GitHub | 官方文档、镜像列表、已知问题 |
| Android Emulator CLI | AVD 命令行参数完整手册 |
| scrcpy GitHub | Android 投屏工具 |
| Docker 官方文档 | Ubuntu Docker 安装 |
如果这篇教程对你有帮助,欢迎点赞收藏。有问题可以在评论区交流。