OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 147

Waydroid 属性、多窗口与 ARM 转译

按应用需求调整窗口、输入与转译,并保留能退回的配置记录。

所在专题Waydroid ↗
约 31 分钟阅读开始阅读

适用范围与操作前提#

命令和输出使用通用示例。内核、镜像、包与服务版本会影响具体行为,阅读时应结合所用版本的文档核对。

修改容器、镜像、挂载和防火墙前先备份 Android 数据并停止相关会话;带删除或强制停止的示例可能丢失数据。NixOS 的系统与用户环境配置应通过 Nix 管理,通用 Linux 的 apt/pacman、直接写 /etc 和 pip 安装段落不用于 NixOS 宿主。重建的 build、test、switch 与启动后的功能验证是不同阶段。

Waydroid 的行为由一套 persist.waydroid.* 系统属性控制,涵盖多窗口模式、显示尺寸、输入伪装、内核兼容等维度。本章提供常用属性与版本检查表、多窗口深度配置、ARM 转译器安装流程以及 memfd 内核兼容调优。


1. persist 属性体系#

Waydroid 通过 Android 系统属性 (system properties) 暴露调优接口。常见运行期行为属性以 persist.waydroid. 为前缀,启动期属性还有 ro.、sys. 等,不能泛称全部,遵循 Android 的 persist 语义:属性值在容器重启后保留,不随会话结束而丢失。

1.1 常用属性参考与版本检查

属性 可选值 默认值 作用
persist.waydroid.multi_windows true / false false 多窗口模式:每个 App 作为独立 Linux 窗口出现,可同时运行
persist.waydroid.width 正整数 (px) 0 (自适应) 覆盖显示屏宽:固定 Android 内部渲染宽度
persist.waydroid.height 正整数 (px) 0 (自适应) 覆盖显示屏高:固定 Android 内部渲染高度
persist.waydroid.fake_wifi 逗号分隔包名/通配符,91 字符限制 空 为指定应用伪造 WiFi 连接状态
persist.waydroid.fake_touch 逗号分隔包名/通配符,91 字符限制 空 为指定应用伪造触屏输入事件
原笔记 persist.waydroid.xkb_layout 未核实为官方接口 不作默认值保证 官方属性页未列出;输入布局按实际镜像与合成器检查
persist.waydroid.invert_colors true / false 按版本 RGBA/BGRA 颜色空间交换,用于显示调试,并非暗色主题开关
原笔记 persist.waydroid.stretch 未核实为官方接口 不作默认值保证 不把属性写入成功当作镜像会消费它的证据
persist.waydroid.suspend true / false 支持内核中通常 true 无活动应用且显示超时后允许容器休眠,具体按版本核对
persist.waydroid.cursor_on_subsurface true / false true 光标渲染方式:true 为 subsurface 层,false 为 overlay
persist.waydroid.uevent true / false false 启用完整 uevent 转发(兼容需要硬件事件检测的 App)

1.2 多窗口模式

waydroid prop set persist.waydroid.multi_windows true

设置为 true 后,Waydroid 的"主界面"图标会从应用菜单中消失。此时每个 Android App 在 Linux 桌面环境中表现为一个独立窗口,可以自由缩放、移动,并在任务栏中独立显示。多个 App 可以同时运行而不会互相暂停。

多窗口模式对竖屏 App 尤其有用 默认全屏模式下,竖屏 App 两侧会出现大片空白。多窗口模式下你可以将窗口调整为竖长形,让 App 以更自然的比例运行。

1.3 显示尺寸覆盖

# 将 Android 内部渲染宽度固定为 1080px
waydroid prop set persist.waydroid.width 1080
# 将 Android 内部渲染高度固定为 1920px
waydroid prop set persist.waydroid.height 1920

这个设置不是调整 Waydroid 窗口大小,而是修改 Android 系统内部感知的屏幕分辨率。对需要特定分辨率的游戏 App 有效,例如某些手游要求最小 720p 才能启动。设为零或删除该属性则恢复自适应。

1.4 伪装 WiFi 与触屏

部分 App(尤其是某些手游)会检测设备是否连接 WiFi 和是否支持触屏。在 Waydroid 中,网络通过 bridge 提供,系统判定为"以太网连接";鼠标点击不被识别为 touch 事件。这两项伪装属性可以逐个应用注入假的硬件状态:

# 让 com.example.game 以为设备连接了 WiFi
waydroid prop set persist.waydroid.fake_wifi com.example.game

# 让 com.example.game 以为设备支持触屏
waydroid prop set persist.waydroid.fake_touch com.example.game

多应用选择:fake_wifi 与 fake_touch 接受逗号分隔的包名列表,也支持 * 通配符;官方文档给出 91 字符限制。不要用换行在同一属性中写多个值。

1.5 设置属性的三种方式

方式 命令/文件 生效时机 持久性
waydroid prop set waydroid prop set <key> <value> 多数设置需重启用户会话 persist 属性按镜像保留;非 persist 属性另核对
在 waydroid.cfg 的 [properties] 中声明启动期覆盖 编辑 /var/lib/waydroid/waydroid.cfg,按版本应用配置 下次容器启动时 保留配置,生成文件仍可能重建
waydroid prop get waydroid prop get <key> 仅查询 N/A

推荐策略:

  • 快速测试:使用 waydroid prop set 记录旧值与新值;许多属性需重启会话才生效,可恢复旧值或设为空。
  • 永久配置:按当前版本在 waydroid.cfg 的 [properties] 管理启动期覆盖;避免直接修改生成的 base.prop,升级可能覆盖它。

避免竞态条件 同时使用运行期 prop set 与 waydroid.cfg 启动覆盖时,需理解镜像加载顺序与 persist 值的优先级。避免编辑生成的 base.prop;使用单一配置来源并验证重启后的值。

1.6 查询当前所有 Waydroid 属性

# 列出所有 persist.waydroid.* 属性及其当前值
waydroid prop get persist.waydroid.multi_windows
waydroid prop get persist.waydroid.width
waydroid prop get persist.waydroid.fake_wifi
waydroid prop get persist.waydroid.fake_touch

# 进入 Android shell 后用 getprop 列出所有
sudo waydroid shell
getprop | grep waydroid

2. 多窗口与多任务#

多窗口模式是 Waydroid 最具特色的桌面集成能力。它将 Android App 从 Waydroid 主界面的沙盒中解放出来,变成与原生 Linux 应用无异的独立窗口。

2.1 启用与行为变化

waydroid prop set persist.waydroid.multi_windows true
sudo systemctl restart waydroid-container

启用后的行为变化:

变化项 之前(默认模式) 之后(多窗口模式)
Waydroid 主界面图标 出现在应用菜单中 从菜单中消失(仍可通过 waydroid show-full-ui 打开)
App 启动方式 在 Waydroid 窗口内启动 每个 App 独立出现在 Linux 桌面
App 窗口管理 由 Android 窗口管理器控制 由 Host 窗口管理器(KWin / Mutter / Hyprland)控制
多 App 同时运行 切换 App 时前一个暂停 所有 App 同时运行,不互相暂停
窗口缩放 不支持 支持(受 App 自身限制)
关闭窗口 App 回到后台 App 不停止,需在 Android 设置中强制停止

2.2 Android 自由形式窗口

在多窗口模式基础上,建议同时开启 Android 开发者选项中的"自由形式窗口":

  1. 进入 Android 设置 → 关于手机 → 按 Android 提示连续点击“版本号”(通常 7 次),开启开发者选项
  2. 进入 设置 → 系统 → 开发者选项 → 开启"强制使用自由形式窗口"
  3. 同样在开发者选项中,将"最小宽度"设置为合适的 dp 值(如 600dp,便于平板布局)

"自由形式窗口"与 Waydroid 多窗口模式是两层不同机制:

  • Waydroid 多窗口模式:将每个 App 映射为 Host 端的独立窗口,由 Host 的窗口管理器控制
  • Android 自由形式窗口:在 Android 内部启用窗口化能力,如果某个 App 在多窗口模式下仍然表现为强制全屏,开启此选项后可能改善

最佳实践 按目标应用实际验证两项组合的行为,不保证总是更好。Waydroid 多窗口负责 Host 端窗口化,Android 自由形式窗口确保 App 内部行为兼容窗口化。

2.3 多任务行为

多窗口模式下,Android App 的表现与传统 Linux 应用趋近:

  • 后台 App 继续运行:游戏挂机、音乐播放、文件下载不会因为切换到其他 App 而暂停
  • 资源占用:所有可见 App 的 GPU 渲染都在持续进行,意味着多开 App 会消耗更多 CPU/GPU 资源
  • App 不随窗口关闭而退出:点击关闭按钮只是隐藏窗口,Android 进程仍在后台运行。也可用 Android 设置强制停止,或 sudo waydroid shell am force-stop com.example.app
flowchart LR
    subgraph Host["Linux 宿主"]
        WM["窗口管理器<br/>KWin / Mutter / Hyprland"]
        TASKBAR["任务栏"]
    end

    subgraph Android["Android 容器内"]
        AM["Activity Manager"]
        APP1["App A 进程"]
        APP2["App B 进程"]
        APP3["App C 进程"]
    end

    WM <-->|"Wayland protocol<br/>独立 wl_surface"| APP1
    WM <-->|"独立 wl_surface"| APP2
    WM <-->|"独立 wl_surface"| APP3
    TASKBAR -->|"显示每个 App 图标"| WM
    AM -->|"管理生命周期"| APP1
    AM -->|"管理生命周期"| APP2
    AM -->|"管理生命周期"| APP3

2.4 全屏 App 与虚拟桌面策略

某些 App(尤其是游戏)即使开启了多窗口模式,启动后仍会强制进入全屏。此时有以下应对策略:

F11 快捷键切换:Waydroid 提供 F11 在全屏和窗口模式之间切换。按一次 F11 退出全屏,再按一次恢复全屏。

F11 切换的前提 有些游戏在全屏模式下会独占键盘输入,导致 F11 无法被 Waydroid 捕获。此时需要先切换到另一个窗口再按 F11,或通过 Waydroid 的工具栏操作。

虚拟桌面策略:利用 Host 桌面环境的虚拟桌面/工作区特性:

桌面环境 虚拟桌面支持 操作方式
GNOME 动态工作区 Super + PageUp/PageDown 切换
KDE Plasma 虚拟桌面 Ctrl + F1/F2/F3... 切换,可自定义数量
Hyprland Workspace 1-10 Super + 1-0 切换
Sway Workspace Super + 数字 切换

策略流程:将强制全屏的 App A 放到工作区 1 → 切换到工作区 2 → 启动 App B → 两个 App 各自占据不同工作区,并行运行不冲突。

2.5 多实例限制与替代方案

Waydroid 不支持运行多个容器实例。上游工具通常围绕单一容器/会话状态设计,多实例不是标准支持接口;BinderFS 可以创建多个设备实例,不能把 Binder 本身说成必然只能单实例。

对于需要双开账号的场景,有以下替代方案:

方案 原理 复杂度
Shelter 利用 Android Work Profile 机制在同一个 Waydroid 内创建隔离的工作配置文件 低
Island 与 Shelter 类似,同样是 Work Profile 方案 低
Virtual Master Android 内的虚拟空间 App 中
另装模拟器 在 Waydroid 之外另开一个 Android 模拟器 (如 Android Studio AVD) 高

推荐:安装 Shelter(F-Droid 可用),在 Waydroid 内创建一个 Work Profile,即可实现同一 App 的双账号同时在线。Work Profile 内的 App 在多窗口模式下同样会以独立窗口出现。


3. ARM 转译 (libhoudini / libndk)#

3.1 为什么需要 ARM 转译

绝大多数 Android 设备运行在 ARM 架构上,因此大量 App——尤其是手游——仅编译了 ARM (arm64-v8a / armeabi-v7a) 原生库。Waydroid 运行在 x86_64 宿主机上,LineageOS 构建的 Android 系统本身是 x86_64 版本。当 App 尝试加载 ARM 原生 .so 库时,CPU 无法直接执行 ARM 指令,导致闪退 (crash) 或卡在启动画面。

ARM 转译器的作用:将 ARM 指令动态翻译为 x86_64 指令,使 x86_64 CPU 能够执行 ARM 原生代码。

3.2 libhoudini vs libndk 选型对比

维度 libhoudini libndk
开发者 Intel Google
技术基础 Intel BT (Binary Translation) Google 的 ARM → x86 翻译层
CPU 优化 针对 Intel CPU 指令调度优化 兼容性依转译版本、镜像和应用而变,不由 CPU 品牌保证
兼容性 按目标应用测试 按目标应用测试
许可 专有软件(Intel 闭源) 专有软件(Google 闭源)
安装来源 从 Google 的 ARM 系统镜像中提取 从 NDK 系统镜像中提取
包大小 较大 (~100MB+) 较小 (~60MB)

CPU 厂商选型建议 Intel CPU → 首选 libhoudini(Intel CPU 微架构优化,翻译效率更高) AMD CPU → 首选 libndk(Google 的翻译层对 AMD 指令调度更友好,houdini 在 AMD CPU 上可能不稳定) 这些选型偏好是社区经验,不能当作已证实的厂商性能优化;用同一镜像、同一 APK 对比验证。

3.3 安装 ARM 转译器 (通用 Linux)

使用社区维护的 waydroid_script 工具:

# 安装依赖
sudo apt install lzip python3-venv

# 克隆仓库
git clone https://github.com/casualsnek/waydroid_script
cd waydroid_script

# 创建虚拟环境
python3 -m venv venv
venv/bin/pip install -r requirements.txt

# 运行主脚本
sudo venv/bin/python3 main.py

交互式菜单操作:

  1. 选择 Android 版本(必须与你安装的 Waydroid 镜像版本严格对应,通常是 Android 11 或 13)
  2. 选择操作类型:Install
  3. 选择要安装的转译器:libhoudini 或 libndk(用空格键勾选,只能选一个)
  4. 确认安装,等待下载和部署完成
  5. 重启 Waydroid 容器:sudo systemctl restart waydroid-container

3.4 NixOS 通过 NUR 安装

# NixOS 用户通过 NUR 获取 waydroid-script
# 在 flake.nix 的 inputs 中添加:
{
  inputs.nur.url = "github:nix-community/NUR";
}
# 然后在 packages 中添加:
# nur.repos.ataraxiasjel.waydroid-script

转译器与 Waydroid 版本严格绑定 ARM 转译器对应的系统库文件与 Android 版本紧密耦合。更新 Waydroid 镜像后(waydroid upgrade),可能需要重新安装或适配转译器,先核对安装器与镜像版本。因为新的系统镜像可能包含不同版本的 framework 库,与旧转译器不兼容,导致所有需要 ARM 转译的 App 再次闪退。

3.5 验证 ARM 转译是否生效

方法一:使用 Package Manager App

  1. 在 Waydroid 中安装 Package Manager(开源 APK 分析工具)
  2. 打开 Package Manager,选择一个安装了 ARM 版 APK 的 App
  3. 查看该 App 的 "Libraries" 或 "Native libs" 标签
  4. 如果 App 正常运行但只有 lib/arm64-v8a 目录而没有 lib/x86_64,说明转译器在工作

方法二:命令行检查

# 进入 Android shell
sudo waydroid shell

# 检查 houdini 进程是否存在
ps -A | grep houdini

# 检查关键库文件
ls -la /system/lib64/libhoudini.so    # libhoudini
ls -la /system/lib64/arm/             # libndk 或 houdini 的 ARM 库目录

# 检查属性值
getprop ro.product.cpu.abilist
# 成功安装后应同时包含 x86_64 和 arm64-v8a

方法三:实际测试

从 APKMirror 下载一个确定只有 ARM 版本的应用(如某些日本手游),安装后尝试启动。如果能正常打开而不闪退,说明转译器工作正常。

3.6 常见问题与排查

问题 可能原因 解决方案
转译器安装后 App 仍闪退 该 App 使用了 ARM64-only 的特殊指令 尝试切换另一种转译器(houdini ↔ ndk)
安装过程中报错 "version mismatch" waydroid_script 版本与系统镜像 Android 版本不对应 更新 waydroid_script 到最新,确认 Android 版本号
特定游戏闪退(如《蔚蓝档案》) 已知兼容性问题,需 App 专用补丁 查找社区 patch(如 Blue Archive gist)
转译后性能极差 CPU 不支持某些 SIMD 指令或翻译效率低 尝试另一种转译器;检查 CPU 是否支持 SSSE3/SSE4.1
两个转译器同时安装导致系统不稳定 翻译库冲突,运行时行为不可预测 用 waydroid_script 的 Remove 功能卸载当前转译器,再安装另一个

禁止同时安装两个转译器 libhoudini 和 libndk 会向系统中安装不同的 ARM→x86 翻译库,两者同时存在会导致动态链接器 (linker64) 行为不确定——可能随机选择其中一个翻译库,甚至两个都加载导致符号冲突。必须先 Remove 旧的,再 Install 新的。

3.7 App 级调试:确认原生库架构

# 进入 Android shell
sudo waydroid shell

# 查看特定 App 的原生库路径(替换 com.example.app 为实际包名)
ls -la /data/app/~~*/com.example.app-*/lib/

# 输出示例:
# arm64/    -> App 只有 ARM64 库(需要转译器)
# x86_64/   -> App 有 x86_64 库(无需转译,原生性能更好)
# arm64/ + x86_64/ -> App 同时包含两种库(优先使用 x86_64,ARM 库仅作备份)

如果 App 同时包含 x86_64 和 arm64 的库,Android 会优先加载 x86_64 版本,此时即使安装转译器也不会使用到 ARM 翻译。真正的受益场景是:App 只有 arm64 或 armeabi-v7a 目录而没有 x86_64 目录。


4. memfd 与内核兼容性#

4.1 为什么需要 memfd

Binder IPC 与 ashmem 共享内存是不同机制。Android 的共享内存用户空间曾使用 ashmem;支持的镜像可以用 memfd 代替该共享内存路径,但 Binder 仍不可缺少。然而:

  • **Linux 5.18 时旧 ashmem 的主线 staging 实现已移除,转而使用标准 Linux 的 memfd (anonymous memory file descriptor) 机制
  • memfd 是 Linux 内核的原生功能,不需要额外模块
  • Waydroid 1.2.1+ 开始支持 sys.use_memfd=true,自动使用 memfd 替代 ashmem

4.2 配置 memfd

# 检查当前内核版本
uname -r

# 编辑 waydroid_base.prop
sudo vim /var/lib/waydroid/waydroid_base.prop

在文件中添加或修改:

# 镜像/Waydroid 支持时使用 memfd;不是 Linux 5.18 才引入
sys.use_memfd=true

设置后重启容器:

sudo systemctl restart waydroid-container

4.3 内核与组件兼容性矩阵

组件 最低版本 推荐版本 备注
Linux Kernel 按发行版和包要求检查 实际维护版本 binderfs 自 5.0 引入,memfd_create 自 3.17 引入;Android 集成是另一条件
LXC 4.0 5.0+ 4.0 支持 Android 容器基础功能;5.0+ 更好的 cgroup v2 兼容
Waydroid 按发行版依赖和镜像要求 实际维护版本 源笔记的精确版本边界未核实,检查对应 release 与镜像
python3 3.8 3.11+ waydroid_script 依赖,不影响容器本身
binder 模块 anbox-binder / binder_linux-dkms 当前内核实际提供的 Binder 实现 见下文

4.4 Binder 内核模块检测

Waydroid 依赖 Binder 实现 Android IPC。检查当前系统的 Binder 状态:

# 检查 binder 设备是否存在
ls -la /dev/binder /dev/binderfs

# 检查 binder 模块是否已加载
lsmod | grep binder

# 检查 binderfs 是否挂载
mount | grep binder

三种 Binder 实现方案:

方案 适用内核 特点
binder_linux-dkms 全系列 用于缺失 Binder 的受支持内核;每次更新需重新构建,不保证全系列兼容
anbox-binder 旧节点/模块方案,依实际实现 不以 5.7 或 Android 9/10 作未经核实的严格兼容边界
rust_binder 依内核/发行版 不根据 6.7 断言内置,检查配置与模块
flowchart TD
    A[检查运行内核与模块] --> B{Binder 节点可用?}
    B -- 是 --> C[检查 BinderFS 和权限]
    B -- 否 --> D[选择发行版支持的内核或模块]
    C --> E[检查镜像共享内存路径]
    E --> F[启动容器并看日志]

4.5 进程上限与会话卡死

不要把长时间卡死直接归因为 PID 回收不及时。先检查 Android/LXC 日志、OOM、内存压力、cgroup pids.max 和实际任务数:

cat /proc/sys/kernel/pid_max
cat /proc/pressure/memory
ps -eLf | wc -l
journalctl -u waydroid-container -b

原笔记的 65535 并非通用优化:若当前值是 4194304,它反而降低上限。只有确认实际限制被耗尽时再按证据调整,并在 NixOS 通过声明式 boot.kernel.sysctl 管理。

4.6 补充:共享目录与文件传输

虽然不是内核配置,但属于高频调优需求:在 Linux 宿主和 Waydroid 之间共享文件。

方案一:bind mount

# 在宿主上创建共享目录
mkdir -p ~/WaydroidShared

# 挂载到 Android 内部存储
mkdir -p ~/.local/share/waydroid/data/media/0/WaydroidShared
# 确认目标权限与敏感数据范围,再挂载专用目录
sudo mount --bind ~/WaydroidShared \
  ~/.local/share/waydroid/data/media/0/WaydroidShared

挂载后,Linux 宿主放在 ~/WaydroidShared/ 的文件会立即在 Android 文件管理器的 WaydroidShared 目录下可见。

bind mount 需要手动重启后重新挂载 mount --bind 在宿主系统重启后丢失。如需持久化,写入 /etc/fstab 或创建 systemd mount 单元。

方案二:ADB 推送

# 检查镜像的 ADB 设置与授权,不假定一定已开放
# 查看 Waydroid 的 IP 地址(Android 设置 → 关于手机 → IP 地址)
adb connect <EXAMPLE_IP>:5555

# 推送文件
adb push local_file.apk /sdcard/Download/

# 拉取文件
adb pull /sdcard/Download/file.txt .

方案三:应用音量独立控制

Waydroid 中所有 Android App 的音频混在一个 PulseAudio / PipeWire 流中。如需独立调整每个 App 的音量:

  • pavucontrol (PulseAudio GUI):在 Playback 标签页中单独调整每个音频流
  • Sound Quick Settings (Android App):在 Android 内部按 App 调整音量
  • ShizuTools MixedAudio (Android App / Xposed):更精细的音源分离

5. 调优速查表#

以下是一次性配置完整流程,适用于首次部署 Waydroid 后的优化:

#!/bin/bash
# Waydroid 调优一次性脚本

# === 1. 内核参数 ===
# 不默认修改 pid_max;只有确认耗尽才调整
# NixOS 中按证据使用 boot.kernel.sysctl,不直接写受管理配置

# === 2. memfd ===
# 在 waydroid.cfg 的 [properties] 管理 sys.use_memfd=true(按镜像支持),避免重复追加生成文件

# === 3. 多窗口 ===
waydroid prop set persist.waydroid.multi_windows true

# === 4. 显示尺寸(可选,根据需求调整)===
# waydroid prop set persist.waydroid.width 1440
# waydroid prop set persist.waydroid.height 900

# === 5. 重启容器使配置生效 ===
sudo systemctl restart waydroid-container

echo "Waydroid 调优完成。"
echo "如需安装 ARM 转译器,请手动运行 waydroid_script。"

核对依据#

Waydroid 官方安装说明。

Waydroid 官方命令说明。

Waydroid 上游源码。

官方属性选项。

社区转译安装工具。

memfd_create(2)。

参考与延伸

  1. Waydroid 官方安装说明 ↗
  2. Waydroid 官方命令说明 ↗
  3. Waydroid 上游源码 ↗
  4. 官方属性选项 ↗
  5. 社区转译安装工具 ↗
  6. memfd_create(2) ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

Waydroid

  1. 01Waydroid 架构:LXC、Binder 与会话桥接
  2. 02Waydroid 安装与基础设置
  3. 03NixOS 中的 Waydroid 集成
  4. 04Waydroid 的 GPU 检测与硬件加速
  5. 05Waydroid 网桥、NAT 与安全边界
  6. 06Waydroid 属性、多窗口与 ARM 转译
  7. 07Waydroid 故障排查与 FAQ
  8. 08Waydroid 使用技巧:窗口、转译和共享目录
CONTINUE READING / 下一篇Waydroid 故障排查与 FAQ

按内核、容器、网络、图形和应用层的证据定位故障。