多窗口模式允许多个应用同时共享同一屏幕。应用可以并排显示或一上一下显示(分屏模式),一个应用显示在覆盖其他应用的小窗口中(画中画模式),或者各个应用显示在单独的可移动、可调整大小的窗口中(桌面窗口模式)。
如需了解如何在手机上访问分屏模式的用户说明,请访问 在 Pixel 手机上同时查看两个应用。
特定版本的多窗口功能
多窗口用户体验取决于 Android 版本和设备类型
Android 7.0(API 级别 24)在小屏幕设备上引入了分屏模式,并在部分设备上引入了画中画模式。
分屏模式用两个应用填充屏幕,让它们并排显示或一上一下显示。用户可以拖动分隔两个应用的分割线,使一个应用变大而另一个变小。
画中画模式让用户在与另一个应用交互时继续播放视频(参见画中画支持)。
桌面窗口模式允许用户自由调整每个 activity 的大小,大屏幕设备制造商可以启用此模式。
您可以通过指定 activity 的最小允许尺寸来配置应用如何处理多窗口模式。您也可以通过设置
resizeableActivity="false"
来禁用应用的多窗口模式,以确保系统始终全屏显示您的应用。
Android 8.0(API 级别 26)将画中画模式扩展到小屏幕设备。
Android 12(API 级别 31)将多窗口模式设为标准行为。
Android 16(API 级别 36)会覆盖屏幕方向、纵横比和可调整大小的限制。
在大屏幕上(最小宽度 >= 600dp),系统会忽略用于限制应用方向、纵横比和可调整大小的清单属性和运行时 API,从而优化所有设备外形规格上的用户体验。
要了解如何将游戏排除在 Android 16 的更改之外,请参阅应用方向、纵横比和可调整大小例外情况。
分屏模式
用户通过以下操作激活分屏模式
- 打开最近使用的应用屏幕
- 将应用滑动到视图中
- 按下应用标题栏中的应用图标
- 选择分屏菜单选项
- 从最近使用的应用屏幕中选择另一个应用,或者关闭最近使用的应用屏幕并运行另一个应用
用户可以通过将窗口分割线拖到屏幕边缘(向上、向下、向左或向右)来退出分屏模式。
启动相邻
如果您的应用需要通过 intent 访问内容,您可以使用 FLAG_ACTIVITY_LAUNCH_ADJACENT
在相邻的分屏窗口中打开内容。
FLAG_ACTIVITY_LAUNCH_ADJACENT
在 Android 7.0(API 级别 24)中引入,用于使在分屏模式下运行的应用能够在相邻窗口中启动 activity。
Android 12L(API 级别 32)及更高版本扩展了此标志的定义,允许全屏运行的应用激活分屏模式,然后在相邻窗口中启动 activity。
要启动相邻 activity,请结合使用 FLAG_ACTIVITY_LAUNCH_ADJACENT
和 FLAG_ACTIVITY_NEW_TASK
,例如:
Kotlin
fun openUrlInAdjacentWindow(url: String) {
Intent(Intent.ACTION_VIEW).apply { data = Uri.parse(url)
addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or Intent.FLAG_ACTIVITY_NEW_TASK)
}.also { intent -> startActivity(intent) }
}
Java
public void openUrlInAdjacentWindow(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
多窗口模式下的 activity 生命周期
多窗口模式不会改变activity 生命周期。但是,应用在多窗口中的恢复状态在不同 Android 版本上有所不同。
多重恢复
Android 10(API 级别 29)及更高版本支持多重恢复 (multi-resume)——当设备处于多窗口模式时,所有 activity 都保持在 RESUMED
状态。如果透明 activity 位于该 activity 之上,或者该 activity 不可聚焦(例如,该 activity 处于画中画模式),则该 activity 可能会暂停。在特定时间也可能没有任何 activity 获得焦点,例如,如果通知抽屉已打开。 onStop()
方法照常工作:当 activity 从屏幕上移除时,就会调用此方法。
在部分运行 Android 9(API 级别 28)的设备上也提供多重恢复。要在 Android 9 设备上选择启用多重恢复,请添加以下清单元数据
<meta-data android:name="android.allow_multiple_resumed_activities" android:value="true" />
要验证特定设备是否支持此清单元数据,请参阅设备规格。
Android 9
在 Android 9(API 级别 28)及更低版本的多窗口模式下,在给定时间只有一个用户最近交互过的 activity 是活动的。此 activity 被视为最顶部,并且是处于 RESUMED
状态的唯一 activity。所有其他可见 activity 处于 STARTED
状态,但未 RESUMED
。但是,系统会赋予这些可见但未恢复的 activity 比不可见的 activity 更高的优先级。如果用户与其中一个可见 activity 交互,该 activity 会恢复,并且之前最顶部的 activity 会进入 STARTED
状态。
当单个活动应用进程中存在多个 activity 时,z-order 最高的 activity 会恢复,而其他 activity 会暂停。
配置更改
当用户将应用置于多窗口模式时,系统会按照处理配置更改中的规定通知 activity 发生配置更改。当用户调整应用大小或将应用恢复为全屏模式时,也会发生这种情况。
从本质上讲,此更改具有与系统通知应用设备已从纵向切换到横向时相同的 activity 生命周期影响,只是应用的尺寸发生了变化,而不是仅仅互换。
如果用户正在调整窗口大小并在任一维度上使其更大,系统会调整 activity 大小以匹配用户操作并根据需要发出配置更改。如果应用在绘制新暴露区域时滞后,系统会暂时使用 windowBackground
属性指定的颜色或默认的 windowBackgroundFallback
样式属性填充这些区域。
独占资源访问
为帮助支持多重恢复功能,请使用 onTopResumedActivityChanged()
生命周期回调。
当 activity 获得或失去最顶部恢复 activity 位置时,会调用此回调,这在使用共享单例资源(例如麦克风或相机)时非常重要
Kotlin
override fun onTopResumedActivityChanged(topResumed: Boolean) {
if (topResumed) {
// Top resumed activity.
// Can be a signal to re-acquire exclusive resources.
} else {
// No longer the top resumed activity.
}
}
Java
@Override
public void onTopResumedActivityChanged(boolean topResumed) {
if (topResumed) {
// Top resumed activity.
// Can be a signal to re-acquire exclusive resources.
} else {
// No longer the top resumed activity.
}
}
请注意,应用可能因其他原因丢失资源,例如移除共享硬件。
无论如何,应用应优雅地处理影响可用资源的事件和状态变化。
对于使用相机的应用,CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()
提供了一个提示,表明此时可能是尝试获取相机访问权限的好时机。此方法自 Android 10(API 级别 29)起可用。
请记住,resizeableActivity=false
并不能保证独占相机访问权限,因为其他使用相机的应用可能在其他显示器上打开。

您的应用在失去焦点时不必释放相机。例如,当用户与新获得焦点的最顶部恢复应用交互时,您可能希望继续相机预览。当您的应用不是最顶部恢复应用时,继续运行相机是可以的,但必须正确处理断开连接的情况。当最顶部恢复应用想要使用相机时,它可以打开相机,您的应用将失去访问权限。当您的应用重新获得焦点时,可以重新打开相机。
在应用接收到 CameraDevice.StateCallback#onDisconnected()
回调后,对相机设备的后续调用将抛出 CameraAccessException
。
窗口指标
Android 11(API 级别 30)引入了以下 WindowManager
方法来提供在多窗口模式下运行的应用的边界
getCurrentWindowMetrics()
:返回表示系统当前窗口状态的WindowMetrics
对象。getMaximumWindowMetrics()
:返回表示系统最大潜在窗口状态的WindowMetrics
。
Jetpack WindowManager 库方法 computeCurrentWindowMetrics()
和 computeMaximumWindowMetrics()
分别提供了类似的功能,但向后兼容到 API 级别 14。
要获取当前显示器以外的显示器指标,请按以下步骤操作(如代码片段所示)
- 创建显示上下文
- 为显示器创建窗口上下文
- 获取窗口上下文的
WindowManager
- 获取应用可用的最大显示区域的
WindowMetrics
Kotlin
val windowMetrics = context.createDisplayContext(display)
.createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
.getSystemService(WindowManager::class.java)
.maximumWindowMetrics
Java
WindowMetrics windowMetrics = context.createDisplayContext(display)
.createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
已废弃方法
Display
方法 getSize()
和 getMetrics()
在 API 级别 30 中已废弃,取而代之的是新的 WindowManager
方法。
Android 12(API 级别 31)废弃了 Display
方法 getRealSize()
和 getRealMetrics()
,并更新了它们的行为,使其更接近 getMaximumWindowMetrics()
的行为。
多窗口模式配置
如果您的应用以 Android 7.0(API 级别 24)或更高版本为目标,您可以配置应用的 activity 如何以及是否支持多窗口模式。您可以在清单中设置属性来控制大小和布局。根 activity 的属性设置适用于其任务栈中的所有 activity。例如,如果根 activity 设置了 android:resizeableActivity="true"
,则任务栈中的所有 activity 都可以调整大小。在某些较大的设备(例如 Chromebook)上,即使您指定了 android:resizeableActivity="false"
,您的应用也可能在可调整大小的窗口中运行。如果这破坏了您的应用,您可以使用 Google Play 上的过滤器来限制应用在此类设备上的可用性。
Android 12(API 级别 31)默认为多窗口模式。在大屏幕上(中等或展开窗口大小类别),所有应用都在多窗口模式下运行,无论应用配置如何。在小屏幕上,系统会检查 activity 的 minWidth
、minHeight
和 resizeableActivity
设置,以确定该 activity 是否可以在多窗口模式下运行。
resizeableActivity
在清单的 <activity>
或 <application>
元素中设置此属性,以针对 API 级别 30 及更低版本启用或禁用多窗口模式
<application
android:name=".MyActivity"
android:resizeableActivity=["true" | "false"] />;
如果此属性设置为 true
,则 activity 可以以分屏和桌面窗口模式启动。如果此属性设置为 false
,则 activity 不支持多窗口模式。如果该值为 false,并且用户尝试在多窗口模式下启动 activity,则 activity 会占据整个屏幕。
如果您的应用以 API 级别 24 或更高版本为目标,但您未指定此属性的值,则该属性的值默认为 true。
如果您的应用以 API 级别 31 或更高版本为目标,则此属性在小屏幕和大屏幕上的工作方式不同
- 大屏幕(中等或展开窗口大小类别):所有应用都支持多窗口模式。该属性指示 activity 是否可以调整大小。如果
resizeableActivity="false"
,必要时应用会进入兼容模式以符合显示尺寸。 - 小屏幕(紧凑窗口大小类别):如果
resizeableActivity="true"
且 activity 的最小宽度和最小高度在多窗口要求范围内,则该 activity 支持多窗口模式。如果resizeableActivity="false"
,则无论 activity 的最小宽度和高度如何,该 activity 都不支持多窗口模式。
如果您的应用以 API 级别 36 或更高版本为目标,则在最小宽度 >= 600dp 的显示屏上会忽略此属性。但是,应用会完全尊重用户选择的纵横比(参见用户按应用覆盖)。
如果您正在构建游戏,请参阅应用方向、纵横比和可调整大小,了解如何将您的游戏排除在 Android 16(API 级别 36)的更改之外。
supportsPictureInPicture
在清单的 <activity>
节点中设置此属性,以指示该 activity 是否支持画中画模式。
<activity
android:name=".MyActivity"
android:supportsPictureInPicture=["true" | "false"] />
configChanges
要自行处理多窗口配置更改(例如用户调整窗口大小时),请在您的应用清单 <activity>
节点中添加 android:configChanges
属性,并至少包含以下值
<activity
android:name=".MyActivity"
android:configChanges="screenSize | smallestScreenSize
| screenLayout | orientation" />
添加 android:configChanges
后,您的 activity 和 fragment 会收到 onConfigurationChanged()
回调,而不是被销毁和重新创建。然后,您可以根据需要手动更新视图、重新加载资源以及执行其他操作。
<layout>
在 Android 7.0(API 级别 24)及更高版本中,<layout>
清单元素支持影响 activity 在多窗口模式下行为的多个属性
android:defaultHeight
,android:defaultWidth
:在桌面窗口模式下启动时 activity 的默认高度和宽度。android:gravity
:在桌面窗口模式下启动时 activity 的初始位置。请参阅Gravity
类了解合适的值。android:minHeight
,android:minWidth
:在分屏和桌面窗口模式下 activity 的最小高度和最小宽度。如果用户在分屏模式下移动分割线,使 activity 小于指定的最小值,系统会将 activity 裁剪到用户请求的大小。
以下代码展示了如何在 activity 以桌面窗口模式显示时指定其默认大小、位置和最小大小
<activity android:name=".MyActivity">
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end|..."
android:minHeight="450dp"
android:minWidth="300dp" />
</activity>
运行时多窗口模式
从 Android 7.0 开始,系统提供了支持应用在多窗口模式下运行的功能。
多窗口模式下禁用的功能
在多窗口模式下,Android 可能会禁用或忽略不适用于与设备屏幕共享的其他 activity 或应用的 activity 的功能。
此外,一些系统 UI 自定义选项会被禁用。例如,应用在多窗口模式下运行时无法隐藏状态栏(参见控制系统 UI 可见性)。
系统会忽略对 android:screenOrientation
属性的更改。
多窗口模式查询和回调
Activity
类提供了以下方法来支持多窗口模式
isInMultiWindowMode()
:指示 activity 是否处于多窗口模式。isInPictureInPictureMode()
:指示 activity 是否处于画中画模式。onMultiWindowModeChanged()
:系统在 activity 进入或退出多窗口模式时调用此方法。如果 activity 正在进入多窗口模式,系统会向该方法传递 true 值;如果 activity 正在退出多窗口模式,则传递 false 值。onPictureInPictureModeChanged()
:系统在 activity 进入或退出画中画模式时调用此方法。如果 activity 正在进入画中画模式,系统会向该方法传递 true 值;如果 activity 正在退出画中画模式,则传递 false 值。
Fragment
类公开了许多这些方法的版本;例如,Fragment.onMultiWindowModeChanged()
。
画中画模式
要将 activity 置于画中画模式,请调用 enterPictureInPictureMode()
如果设备不支持画中画模式,此方法无效。有关更多信息,请参阅使用画中画 (PiP) 添加视频。
多窗口模式下的新 activity
启动新 activity 时,您可以指明如果可能,新 activity 应显示在当前 activity 的旁边。使用 intent 标志 FLAG_ACTIVITY_LAUNCH_ADJACENT
,它会告诉系统尝试在相邻窗口中创建新 activity,以便两个 activity 共享屏幕。系统会尽力做到这一点,但不能保证一定实现。
如果设备处于桌面窗口模式并且您正在启动新 activity,您可以通过调用 ActivityOptions.setLaunchBounds()
指定新 activity 的尺寸和屏幕位置。如果设备未处于多窗口模式,该方法无效。
在 API 级别 30 及更低版本上,如果您在任务栈中启动 activity,该 activity 会替换屏幕上的 activity,并继承其所有多窗口属性。如果您想在多窗口模式下将新 activity 作为单独的窗口启动,则必须在新的任务栈中启动它。
Android 12(API 级别 31)允许应用在多个 activity 之间分割应用的 task 窗口。您可以通过创建 XML 配置文件或进行 Jetpack WindowManager API 调用来确定您的应用如何显示其 activity——全屏、并排或堆叠。
拖放
当两个 activity 共享屏幕时,用户可以将数据从一个 activity 拖放到另一个 activity。(在 Android 7.0 之前,用户只能在单个 activity 内拖放数据。)要快速添加对接受拖放内容的支持,请参阅 DropHelper
API。有关全面的拖放指南,请参阅启用拖放。
多实例
每个根 activity 都有自己的 task,并在自己的窗口中显示。要在单独的窗口中启动应用的新实例,请使用 FLAG_ACTIVITY_NEW_TASK
标志启动新 activity。您可以将此设置与多窗口属性结合使用,以请求新窗口的特定位置。例如,购物应用可以显示多个相邻窗口来比较商品。
Android 12(API 级别 31)及更高版本允许您在activity 嵌入中,在同一 task 窗口中并排启动同一 activity 的两个实例。
如果您想允许用户从应用启动器或任务栏启动应用的另一个实例,请在启动器 activity 的清单中设置 android:resizeableActivity="true"
,并且不要使用阻止多实例的启动模式。例如,当设置 FLAG_ACTIVITY_MULTIPLE_TASK
或 FLAG_ACTIVITY_NEW_DOCUMENT
时,singleInstancePerTask
activity 可以在不同的 task 中多次实例化。
在 Android 15(API 级别 35)及更高版本上,PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
允许您声明支持多实例。此属性是系统 UI 向用户公开控件以创建应用多个实例的明确信号。此属性与启动模式无关,但仅应在 activity 或应用的启动模式与此属性兼容时使用,例如,当启动模式不是 singleInstance
时。
当应用的多个实例在可折叠设备的单独窗口中运行时,如果设备姿态发生变化,一个或多个实例可能会被发送到后台。例如,假设设备展开,并且折叠两侧各有两个应用实例在单独的窗口中运行。如果设备折叠,其中一个实例可能会被终止,而不是试图将两个实例的窗口都适配到较小的屏幕上。
多窗口模式验证
无论您的应用是否以 API 级别 24 或更高版本为目标,您都应验证它在多窗口模式下的行为,以防用户尝试在运行 Android 7.0 或更高版本的设备上以多窗口模式启动它。
测试设备
运行 Android 7.0(API 级别 24)或更高版本的设备支持多窗口模式。
API 级别 23 或更低
当用户尝试在多窗口模式下使用应用时,除非应用声明了固定方向,否则系统会强制调整应用大小。
如果您的应用未声明固定方向,您应在运行 Android 7.0 或更高版本的设备上启动您的应用,并尝试将其置于分屏模式。验证应用被强制调整大小时的用户体验是否可接受。
如果应用声明了固定方向,您应尝试将其置于多窗口模式。验证此时应用是否保持全屏模式。
API 级别 24 到 30
如果您的应用以 API 级别 24 到 30 为目标且未禁用多窗口支持,请在分屏和桌面窗口模式下验证以下行为
全屏启动应用,然后长按最近使用的应用按钮切换到多窗口模式。验证应用是否正确切换。
直接在多窗口模式下启动应用,并验证应用是否正确启动。您可以通过按最近使用的应用按钮,然后长按应用标题栏并将其拖动到屏幕上突出显示的区域之一来在多窗口模式下启动应用。
通过拖动屏幕分割线在分屏模式下调整应用大小。验证应用在调整大小时未崩溃且必要的 UI 元素可见。
如果您为应用指定了最小尺寸,尝试调整应用大小使其窗口小于这些尺寸。验证您无法将应用调整到小于指定的最小尺寸。
在所有测试中,验证您的应用性能是否可接受。例如,验证应用调整大小后更新 UI 没有太长的延迟。
API 级别 31 或更高
如果您的应用以 API 级别 31 或更高版本为目标,并且主 activity 的最小宽度和最小高度小于或等于可用显示区域的相应尺寸,请验证为API 级别 24 到 30 列出的所有行为。
测试清单
要验证您的应用在多窗口模式下的性能,请尝试以下操作。除另有说明外,您应在分屏和桌面窗口模式下尝试这些操作。
进入和退出多窗口模式。
从您的应用切换到另一个应用,并验证应用在可见但不活动时是否正常运行。例如,如果您的应用正在播放视频,请验证在用户与另一个应用交互时视频是否继续播放。
在分屏模式下,尝试移动屏幕分割线以使您的应用变大或变小。在并排和一上一下配置下都尝试这些操作。验证应用未崩溃,关键功能可见,且调整大小操作不会花费太长时间。
快速连续执行多次调整大小操作。验证您的应用未崩溃或内存泄漏。Android Studio 的内存分析器提供了关于您的应用内存使用情况的信息(参见使用内存分析器检查应用内存使用情况)。
在多种不同的窗口配置下正常使用您的应用,并验证应用是否正常运行。验证文本是否可读以及 UI 元素是否不会过小而无法交互。
多窗口支持已禁用
在 API 级别 24 到 30 上,如果您通过设置 android:resizeableActivity="false"
禁用了多窗口支持,您应在运行 Android 7.0 到 11 的设备上启动您的应用,并尝试将其置于分屏和桌面窗口模式。验证此时应用是否保持全屏模式。
其他资源
有关 Android 中多窗口支持的更多信息,请参见
- Android MultiWindowPlayground 示例