Ubuntu Android 虚拟机安装使用教程

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 安装

如果这篇教程对你有帮助,欢迎点赞收藏。有问题可以在评论区交流。

相关推荐
我命由我123451 小时前
Android 开发问题:Could not find com.github.PicnicSupermarket:FingerPaintView:1.2.
android·github·android studio·安卓·android jetpack·android-studio·android runtime
2023自学中1 小时前
imx6ull开发板 移植 ffmpeg 4.2.11 + x264 视频编码库
linux·ffmpeg·音视频·嵌入式·开发板
阿洛学长2 小时前
VMware安装虚拟机教程(超详细)
java·linux·开发语言
YOU OU2 小时前
Linux基本使用和程序部署
linux·运维·服务器
fred_kang2 小时前
如何找到 Linux 服务器上某个 URL 路径对应的实际部署位置
linux·运维·服务器
黄林晴3 小时前
Google Play 全面进化:AI 驱动增长,从上架到收入全链路重构
android·google
测试老哥3 小时前
接口测试详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
用户2367829801683 小时前
Linux iptables 深度解析:从规则匹配到 NAT 转发实战
linux