以下提供了对这些特定于显示区域的更新
系统装饰
Android 10 增加了对配置辅助显示设备以显示某些系统装饰(例如壁纸、导航栏和启动器)的支持。默认情况下,主显示设备显示所有系统装饰,而辅助显示设备显示选择性启用的系统装饰。输入法编辑器 (IME) 的支持可以与其他系统装饰分开设置。
使用 DisplayWindowSettings#setShouldShowSystemDecorsLocked()
在特定显示设备上添加对系统装饰的支持,或在 /data/system/display_settings.xml
中提供默认值。有关示例,请参阅显示窗口设置。
实现
DisplayWindowSettings#setShouldShowSystemDecorsLocked()
方法也在 WindowManager#setShouldShowSystemDecors()
中公开以供测试。使用启用系统装饰的意图触发此方法不会添加先前缺失的装饰窗口,也不会移除先前存在的装饰窗口。在大多数情况下,系统装饰支持的更改只有在设备重启后才能完全生效。
在 WindowManager 代码库中检查系统装饰支持通常会通过 DisplayContent#supportsSystemDecorations()
,而外部服务(例如 System UI 检查是否应显示导航栏)的检查则使用 WindowManager#shouldShowSystemDecors()
。要了解此设置控制的内容,请浏览这些方法的调用点。
系统界面装饰窗口
Android 10 仅为导航栏添加了系统装饰窗口支持,因为导航栏对于在应用和 Activity 之间导航至关重要。默认情况下,导航栏显示“返回”和“主页”控件。只有当目标显示器支持系统装饰时(请参阅 DisplayWindowSettings
),才会包含此功能。
状态栏是一个更复杂的系统窗口,因为它还包含通知栏、快速设置和锁屏界面。在 Android 10 中,辅助显示器不支持状态栏。因此,通知、设置和完整的锁屏界面仅在主显示器上可用。
辅助屏幕不支持 概览/最近任务 系统窗口。在 Android 10 中,AOSP 仅在默认显示器上显示最近任务,并包含来自所有显示器的 Activity。从最近任务启动时,默认情况下,辅助显示器上的 Activity 会被置于该显示器的前台。这种方法存在一些已知问题,例如,当应用出现在其他屏幕上时,不会立即更新。
实现
为了实现额外的 System UI 功能,设备制造商应使用单个 System UI 组件,该组件监听显示器的添加/移除并呈现适当的内容。
支持多显示器 (MD) 的 System UI 组件应处理以下情况:
- 启动时的多个显示器初始化
- 运行时添加显示器
- 运行时移除显示器
当 System UI 在 WindowManager 之前检测到显示器的添加时,会创建竞争条件。可以通过实现从 WindowManager 到 System UI 的自定义回调来避免这种情况,以便在添加显示器时通知 System UI,而不是订阅 DisplayManager.DisplayListener
事件。有关参考实现,请参阅导航栏支持的 CommandQueue.Callbacks#onDisplayReady
和壁纸支持的 WallpaperManagerInternal#onDisplayReady
。
此外,Android 10 还提供以下更新:
NavigationBarController
类控制所有特定于导航栏的功能。- 要查看自定义导航栏,请参阅
CarStatusBar
。 TYPE_NAVIGATION_BAR
不再限制为单个实例,并且可以按显示器使用。IWindowManager#hasNavigationBar()
已更新为包含仅供 System UI 使用的displayId
参数。
启动器
在 Android 10 中,每个配置为支持系统装饰的显示器都有一个专用的主屏幕堆栈,用于类型为 WindowConfiguration#ACTIVITY_TYPE_HOME
的启动器 Activity(默认情况下)。每个显示器都使用一个单独的启动器 Activity 实例。
图 1. platform/development/samples/MultiDisplay
的多显示器启动器示例
大多数现有启动器都不支持多个实例,并且未针对大屏幕尺寸进行优化。此外,在辅助/外部显示器上通常期望获得不同的体验。为了为辅助屏幕提供专用的 Activity,Android 10 在 intent 过滤器中引入了 SECONDARY_HOME
类别。此 Activity 的实例用于所有支持系统装饰的显示器,每个显示器一个实例。
<activity> ... <intent-filter> <category android:name="android.intent.category.SECONDARY_HOME" /> ... </intent-filter> </activity>
该 Activity 必须具有不允许多个实例的启动模式,并且应适应不同的屏幕尺寸。启动模式不能为 singleInstance
或 singleTask
。
实现
在 Android 10 中,RootActivityContainer#startHomeOnDisplay()
会根据启动主屏幕的显示器自动选择所需的组件和 intent。RootActivityContainer#resolveSecondaryHomeActivity()
包含查找启动器 Activity 组件的逻辑,具体取决于当前选择的启动器,并且可以在需要时使用系统默认启动器(请参阅 ActivityTaskManagerService#getSecondaryHomeIntent()
)。
安全限制
除了适用于辅助显示器上的 Activity 的限制之外,为了避免恶意应用创建启用 系统装饰 的虚拟显示器并从 Surface 读取用户敏感信息的可能性,启动器仅在系统拥有的虚拟显示器上显示。启动器不会在非系统虚拟显示器上显示内容。
壁纸
在 Android 10(及更高版本)中,辅助显示器支持壁纸
图 2. 内部显示器(上图)和外部显示器(下图)上的动态壁纸
开发者可以通过在 WallpaperInfo
XML 定义中提供 android:supportsMultipleDisplays="true"
来声明对壁纸功能的支持。还期望壁纸开发者在 WallpaperService.Engine#getDisplayContext()
中使用显示器上下文加载资源。
框架为每个显示器创建一个 WallpaperService.Engine
实例,因此每个引擎都有自己的 Surface 和显示器上下文。开发者需要确保每个引擎可以独立绘制,以不同的帧速率,并遵守 VSYNC。
为各个屏幕选择壁纸
Android 10 不提供直接的平台支持来为各个屏幕选择壁纸。为了实现这一点,需要一个稳定的显示器标识符来持久保存每个显示器的壁纸设置。Display#getDisplayId()
是动态的,因此无法保证物理显示器在重启后具有相同的 ID。
但是,Android 10 添加了 DisplayInfo.mAddress
,其中包含物理显示器的稳定标识符,可以在未来用于完整实现。遗憾的是,现在为 Android 10 实现此逻辑为时已晚。建议的解决方案是:
- 使用
WallpaperManager
API 设置壁纸。 WallpaperManager
是从Context
对象获得的,并且每个Context
对象都具有有关相应显示器的信息 (Context#getDisplay()/getDisplayId()
)。因此,您可以从WallpaperManager
实例获得displayId
,而无需添加新方法。- 在框架端,使用从
Context
对象获得的displayId
,并将其映射到静态标识符(例如物理显示器的端口)。使用静态标识符来持久保存所选壁纸。
此解决方法使用现有的壁纸选择器实现。如果在特定显示器上打开它并使用正确的上下文,那么当它调用以设置壁纸时,系统可以自动识别显示器。
如果需要为当前显示器以外的显示器设置壁纸,则为目标显示器创建一个新的 Context
对象 (Context#createDisplayContext
),并从该显示器获得 WallpaperManager
实例。
安全限制
系统不会在它不拥有的虚拟显示器上显示壁纸。这是出于安全考虑,因为恶意应用可能会创建一个启用系统装饰支持的虚拟显示器,并从 Surface 读取用户敏感信息(例如个人照片)。
实现
在 Android 10 中,IWallpaperConnection#attachEngine()
和 IWallpaperService#attach()
接口接受 displayId
参数以创建每个显示器的连接。WallpaperManagerService.DisplayConnector
封装了每个显示器的壁纸引擎和连接。在 WindowManager 中,壁纸控制器是在构造时为每个 DisplayContent
对象创建的,而不是为所有显示器创建一个 WallpaperController
。
一些公共 WallpaperManager
方法实现(例如 WallpaperManager#getDesiredMinimumWidth()
)已更新为计算和提供相应显示器的信息。WallpaperInfo#supportsMultipleDisplays()
和相应的资源属性已添加,以便应用开发者可以报告哪些壁纸已准备好用于多个屏幕。
如果默认显示器上显示的壁纸服务不支持多显示器,则系统会在辅助显示器上显示默认壁纸。
图 3. 辅助显示器的壁纸回退逻辑