在嵌入式设备、双显示终端或定制系统中,Android 多屏幕控制(尤其是屏幕方向旋转)是一个兼具挑战与价值的功能模块。本文将深入分析如何识别多个显示、如何通过系统 API 控制旋转,并讨论为何某些 displayId
无法旋转。
📌 一、系统中的多屏幕结构
Android 通过 DisplayManager
和 WindowManagerService
管理显示设备:
- 每个屏幕对应一个唯一的 Display ID(如 0、2)
- 可使用如下命令查看:
adb shell dumpsys display | grep -E 'DisplayDeviceInfo|mDisplayId='
示例输出:
DisplayDeviceInfo{"Built-in Screen": ..., uniqueId="local:0", ...}
DisplayDeviceInfo{"HDMI Screen": ..., uniqueId="local:1", ...}
mDisplayId=0
mDisplayId=2
这表明主屏为 ID 0,HDMI 外接屏为 ID 2。
🎛️ 二、屏幕旋转 API 解析
Android 的系统服务 IWindowManager
提供旋转接口:
freezeRotation(int rotation)
:冻结主屏幕方向freezeDisplayRotation(int displayId, int rotation)
:理论上支持控制任意 Display,但部分实现未生效
⚠️ 这些 API 是隐藏的,仅在系统应用或具有平台签名权限下可用。
🧪 三、为什么某些屏幕旋转无效?
以 Display ID = 2 为例,旋转指令如下:
wm.freezeDisplayRotation(2, Surface.ROTATION_90);
但实际无效,原因可能包括:
❌ 1. 不支持旋转的屏幕标志
通过 dumpsys display
可见:
FLAGS: FLAG_PRESENTATION, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS
但缺少了关键的 FLAG_ROTATES_WITH_CONTENT
,说明系统不会主动旋转它。
❌ 2. 无实际内容
字段 mHasContent=false
表示该屏幕当前未显示任何窗口,旋转调用将无效。
❌ 3. 非主屏旋转能力被限制
AOSP 默认仅对主屏做全面旋转控制,非主屏往往依赖 Presentation 或 VirtualDisplay 机制。
✅ 四、实践替代方案
针对不能通过系统旋转控制的屏幕,可以采取以下方案:
1. 视图级旋转(推荐)
在 HDMI 屏幕上的 View 或 SurfaceView 设置旋转:
yourView.setRotation(90);
适用于 UI 层模拟旋转效果。
2. 创建旋转矩阵
如果使用 OpenGL 或 Surface 渲染,可直接应用旋转矩阵。
3. 修改系统源码(深度方案)
调整 WindowManagerService
及 DisplayContent
支持非主屏旋转 —— 适用于 ROM 开发者。
📘 五、推荐架构设计示例
-
启动时读取属性值决定旋转目标:
int targetDisplayId = SystemProperties.getInt("persist.sys.rotation.screen", 0); int rotation = SystemProperties.getInt("persist.sys.rotation.screen_rotation", 0);
-
根据属性调用旋转:
if (targetDisplayId == 0) {wm.freezeRotation(rotation); } else {wm.freezeDisplayRotation(targetDisplayId, rotation); }
-
无属性时默认控制主屏,避免异常。
🧠 六、总结
项目 | 主屏(displayId=0) | 外接屏(如 displayId=2) |
---|---|---|
旋转 API 是否生效 | ✅ 生效 | ❌ 多数情况无效 |
是否可通过 View 旋转 | ✅ 支持 | ✅ 支持 |
是否具备旋转标志 | ✅ FLAG_ROTATES_WITH_CONTENT | ❌ 通常缺失 |
可行的旋转方式 | freezeRotation() | View.setRotation() / 自定义渲染 |