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

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

相关推荐
李子红了时3 小时前
阿狸舞台APP(安卓端手机EncFS解密+压缩包解压+视频播放)
android·音视频
事圆则缓3 小时前
Android 使用 Jenkins 实现 CI/CD,并用 SonarQube 建立质量门禁
android·ci/cd·jenkins
其实防守也摸鱼3 小时前
DeepSeek Harness 开源贡献手记:从 Issue 到 Merge 的完整旅程
android·数据库·学习·ai·oracle·自动化
恋猫de小郭3 小时前
Android CLI 支持 AI Agent 通过 Device Streaming 调试云真机
android·前端·flutter
皓月盈江3 小时前
Hexo博客Butterfly5.7.0主题网站信息:本站访客数和本站总浏览量的数据一直加载不显示的解决方法
linux·hexo·butterfly·busuanzi·vercount·本站访客数·本站总浏览量
H.莓飛3 小时前
【数据结构】堆_OJ题
linux·数据结构·算法
硅基手札4 小时前
【linux内核专栏 09】内核模块与 kbuild
linux·架构
流浪0014 小时前
Linux系统篇43——线程(八) 线程的数据不一致问题,从 ticket-- 的非原子性说起
linux·操作系统·线程·线程安全·锁
Είναι η κοπέλα4 小时前
WSL2 部署 AI 开发环境:内核级 Linux + GPU 直通
linux·运维·人工智能
其实防守也摸鱼4 小时前
网安自测题:掌握核心知识点的实用练习
linux·运维·服务器·前端·数据库·sql·xss