下文介绍对这些与显示相关的部分进行的更新:
系统装饰
Android 10 增加了对于配置辅助屏幕的支持,以显示特定的系统装饰(例如壁纸、导航栏和启动器)。默认情况下,主屏幕会显示所有系统装饰项,而辅助屏幕会显示选择性启用的装饰项。对输入法 (IME) 的支持可以与其他系统装饰项分开设置。
使用 DisplayWindowSettings#setShouldShowSystemDecorsLocked()
在特定屏幕上添加对系统装饰的支持,或在 /data/system/display_settings.xml
中提供默认值。如需查看相关示例,请参阅屏幕窗口设置。
实现
DisplayWindowSettings#setShouldShowSystemDecorsLocked()
也会显示在 WindowManager#setShouldShowSystemDecors()
中,用于测试。出于支持系统装饰的目的触发此方法,既不会添加先前没有的装饰窗口,也不会移除先前已存在的装饰窗口。在大多数情况下,有关系统装饰支持的更改只有在设备重启后才会完全生效。
通常通过 DisplayContent#supportsSystemDecorations()
在 WindowManager 代码库中检查对于系统装饰的支持,而检查外部服务(例如系统界面,以检查是否应显示导航栏)则使用 WindowManager#shouldShowSystemDecors()
。
如需了解此设置控制的内容,请查看这些方法的调用点。
系统界面装饰窗口
Android 10 仅针对导航栏增加了系统装饰窗口支持,因为导航栏对于在 activity 和应用之间导航是必不可少的。默认情况下,导航栏会显示“返回”和“主屏幕”图标。仅当目标屏幕支持系统装饰时才包含此功能(请参阅 DisplayWindowSettings
)。
状态栏是一个更为复杂的系统窗口,因为它还包含通知栏、快捷设置和锁定屏幕。在 Android 10 中,辅助屏幕不支持状态栏。 因此,通知、设置和完整锁屏仅在主屏幕上可用。
辅助屏幕不支持“概览/最近”系统窗口。在 Android 10 中,AOSP 仅在默认屏幕上显示“最近”,并包含所有屏幕中的 activity。从“最近”启动时,默认会将辅助屏幕上的 activity 放到默认屏幕的最前端。此方法存在一些已知的问题,例如当应用显示在其他屏幕上时不会立即更新。
实现
要实现其他系统界面功能,设备制造商应使用单个系统界面组件来侦听添加/移除屏幕并显示适当的内容。
支持多屏幕 (MD) 的系统界面组件需要处理以下情况:
- 在启动时执行多屏幕初始化
- 在运行时添加屏幕
- 在运行时移除屏幕
如果系统界面先于 WindowManager 检测到添加屏幕,就会创建一个竞态条件。可以通过以下方法来避免发生这种情况,即在添加屏幕时实现一个从 WindowManager 到系统界面的自定义回调,而不是订阅 DisplayManager.DisplayListener
事件。有关参考实现,请参阅 CommandQueue.Callbacks#onDisplayReady
获取导航栏支持和 WallpaperManagerInternal#onDisplayReady
(适用于壁纸)。
此外,Android 10 还提供了以下更新:
NavigationBarController
类可控制特定于导航栏的所有功能。- 要查看自定义导航栏,请参阅
CarStatusBar
。 TYPE_NAVIGATION_BAR
不再局限于单个实例,而是可以在每个屏幕中使用。IWindowManager#hasNavigationBar()
已更新为仅包含系统界面的displayId
参数。
启动器
在 Android 10 中,每个配置为支持系统装饰的屏幕,默认情况下都有一个专用于启动器 activity 的主屏幕堆栈,该堆栈的类型为 WindowConfiguration#ACTIVITY_TYPE_HOME
。每个屏幕使用一个单独的启动器 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()
)。
安全限制
为了避免恶意应用创建启用了系统装饰的虚拟屏幕并从 surface 读取用户敏感信息,除了对辅助屏幕上的 activity 施加限制外,启动器还仅会在系统所有的虚拟屏幕上显示。启动器不会在非系统虚拟屏幕上显示内容。
壁纸
在 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
对象 (Context#createDisplayContext
),并从该屏幕中获取 WallpaperManager
实例。
安全限制
系统不会在不属于自己的虚拟屏幕上显示壁纸。 这是出于安全考虑,因为恶意应用可能会创建启用了系统装饰支持的虚拟屏幕,并从 surface 读取用户敏感信息(例如个人照片)。
实现
在 Android 10 中,IWallpaperConnection#attachEngine()
和 IWallpaperService#attach()
接口接受 displayId
参数来创建与每个屏幕之间的连接。
WallpaperManagerService.DisplayConnector
封装了每个屏幕的壁纸引擎和连接。在 WindowManager 中,会在构建时为每个 DisplayContent
对象创建壁纸控制器,而不是为所有屏幕创建一个 WallpaperController
。
一些公共 WallpaperManager
方法实现(如 WallpaperManager#getDesiredMinimumWidth()
)已更新,以计算并提供相应屏幕的信息。添加了 WallpaperInfo#supportsMultipleDisplays()
和相应的资源属性,以便应用开发者可以报告哪些壁纸已可供多个屏幕使用。
如果默认屏幕上显示的壁纸服务不支持多个屏幕,则系统会在辅助屏幕上显示默认壁纸。
图 3. 辅助屏幕的壁纸回退逻辑