一:功能和 API 概览
Android 12 面向开发者引入了一些出色的新功能和 API。以下几部分内容可帮助您了解适用于您的应用的功能并开始使用相关 API。
有关新增、修改和移除的 API 的详细列表,请参阅 API 差异报告。如需详细了解新的 API,请访问 Android API 参考文档 – 新 API 会突出显示以方便查看。此外,如需了解平台变更可能会在哪些方面影响您的应用,请务必查看会影响以 Android 12 为目标平台的应用和所有应用的 Android 12 行为变更。
新体验
微件改进
Android 12 改进了现有的 Widgets API,以改善平台和启动器中的用户和开发者体验。我们编写了一个指南,帮助您确保您的微件与 Android 12 兼容并使用新功能对其进行更新。
如需了解详情,请参阅 Android 12 微件改进。
音频耦合触感反馈效果
Android 12 应用可使用手机的振动器产生来自音频会话的触感反馈。这可让您获得更身临其境的游戏和音频体验。例如,触感反馈效果增强的铃声有助于识别来电者,或者赛车游戏可以模拟在崎岖地形驾驶的感觉。
如需了解详情,请参阅 HapticGenerator 参考文档。
启动画面 API
Android 12 为所有应用引入了新的应用启动动画,包括启动时的进入应用运动、显示应用图标的启动画面,以及向应用本身的过渡。如需了解详情,请参阅启动画面 API。
允许按来电重要性排名的新通话通知
Android 12 为通话添加了新的通知样式 Notification.CallStyle
。使用此模板可让您的应用指明正在进行的通话的重要性,方法是在状态栏中显示一个显眼的条状标签,在其中显示通话的时间;用户可以点按此条状标签以返回他们的通话。
由于来电和进行中的通话对用户来说最为重要,因此这些通知在通知栏中排名最高。这种排名还让系统有可能将这些优先处理的通话转接到其他设备。
为所有类型的通话实现以下代码。
Kotlin
// Create a new call with the user as caller.
val incoming_caller = Person.Builder().setName("Jane Doe").setImportant(true).build()
Java
// Create a new call with the user as caller.
Person incoming_caller = new Person.Builder().setName("Jane Doe").setImportant(true).build();
使用 forIncomingCall()
为来电创建通话样式通知。
Kotlin
// Create a call style notification for an incoming call.
val builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent)).addPerson(incoming_caller)
Java
// Create a call style notification for an incoming call.
Notification.Builder builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent)).addPerson(incoming_caller);
使用 forOngoingCall()
为进行中的通话创建通话样式通知。
Kotlin
// Create a call style notification for an ongoing call.
val builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forOnGoingCall(caller, hangupIntent)).addPerson(second_caller)
Java
// Create a call style notification for an ongoing call.
Notification.Builder builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forOnGoingCall(caller, hangupIntent)).addPerson(second_caller);
使用 forScreeningCall()
为过滤来电创建通话样式通知。
Kotlin
// Create a call style notification for an ongoing call.
val builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forOnGoingCall(caller, hangupIntent)).addPerson(second_caller)
Java
Notification.Builder builder = Notification.Builder(context, CHANNEL_ID).setContentIntent(contentIntent).setSmallIcon(smallIcon).setStyle(Notification.CallStyle.forScreeningCall(caller, hangupIntent, answerIntent)).addPerson(second_caller);
通知的丰富图片支持
在 Android 12 中,您现在可以通过在 MessagingStyle()
和 BigPictureStyle()
通知中提供动画图片来丰富应用的通知体验。此外,您的应用现在还可以让用户在从通知栏回复消息时发送图片消息。
圆角 API
Android 12 引入了 RoundedCorner
和 WindowInsets.getRoundedCorner(int position)
,它们可以提供圆角的半径和中心点。借助这些 API,您的应用可以避免界面元素在带有圆角的屏幕上被截断。
在您的应用中实现这些 API 时,它们对具有非圆角屏幕的设备没有影响。
如需实现此功能,请相对于应用的边界通过 WindowInsets.getRoundedCorner(int position)
获取 RoundedCorner
信息。如果应用未占据整个屏幕,则该 API 通过让圆角的中心点基于应用的窗口边界来应用圆角。
以下代码段展示了一个简单的示例,说明了一个应用通过根据来自 RoundedCorner
的信息设置视图的外边距来避免界面截断。在本例中,它是右上角的圆角。
// Get the top-right rounded corner from WindowInsets.
final WindowInsets insets = getRootWindowInsets();
final RoundedCorner topRight = insets.getRoundedCorner(POSITION_TOP_RIGHT);
if (topRight == null) {return;
}// Get the location of the close button in window coordinates.
int [] location = new int[2];
closeButton.getLocationInWindow(location);
final int buttonRightInWindow = location[0] + closeButton.getWidth();
final int buttonTopInWindow = location[1];// Find the point on the quarter circle with a 45 degree angle.
final int offset = (int) (topRight.getRadius() * Math.sin(Math.toRadians(45)));
final int topBoundary = topRight.getCenter().y - offset;
final int rightBoundary = topRight.getCenter().x + offset;// Check whether the close button exceeds the boundary.
if (buttonRightInWindow < rightBoundary && buttonTopInWindow > topBoundary) {return;
}// Set the margin to avoid truncating.
int [] parentLocation = new int[2];
getLocationInWindow(parentLocation);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) closeButton.getLayoutParams();
lp.rightMargin = Math.max(buttonRightInWindow - rightBoundary, 0);
lp.topMargin = Math.max(topBoundary - buttonTopInWindow, 0);
closeButton.setLayoutParams(lp);
画中画 (PiP) 改进
Android 12 针对画中画 (PiP) 模式引入了新功能。如需了解详情,请参阅画中画改进。
沉浸模式下的手势导航改进
Android 12 简化了沉浸模式,使手势导航更易于操作且与其他活动体验(例如观看视频和阅读图书)更加一致。应用仍然可以在全屏游戏体验中防止意外手势,以免用户在玩游戏时意外退出游戏;所有其他全屏或沉浸式体验现在都允许用户通过滑动手势进行导航。
为了实现这一点,从 Android 12 开始,我们已弃用非粘性沉浸式体验(BEHAVIOR_SHOW_BARS_BY_TOUCH
、BEHAVIOR_SHOW_BARS_BY_SWIPE
)的现有行为。它们已被默认行为 (BEHAVIOR_DEFAULT
) 取代,即在隐藏系统栏后,允许使用滑动手势。此标志会根据模式显示不同的视觉和功能行为:
- 在“三按钮”模式下,视觉和功能行为与 Android 12 之前的版本中的沉浸模式相同。
- 在手势导航模式下,行为如下:
- 在视觉上,它与 Android 11 及更低版本中的沉浸模式相同。
- 从功能上讲,即使系统栏被隐藏,也允许使用手势;只需滑动屏幕一次便可调用系统返回操作,而 Android 11 需要滑动两次。下拉通知栏或转到主屏幕无需另外滑动屏幕。
Android 12 的粘性沉浸模式 (BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
) 没有任何变化。请注意此功能的以下向后兼容性:
- 如果应用以 Android 11 及更低版本为目标平台,则在 Android 12 上运行时:
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
展示出的功能和呈现出的视觉效果相同。- 默认值会映射到
BEHAVIOR_SHOW_BARS_BY_SWIPE
。
- 如果应用以 Android 12 为目标平台,则在 Android 11(API 级别 30)及更低版本上运行时:
- 展示出的功能和呈现出的视觉效果应该是相同的,但
BEHAVIOR_SHOW_BARS_BY_TOUCH
映射到BEHAVIOR_SHOW_BARS_BY_SWIPE
的情况除外。 - 务必更新您的 SDK 级别,以获得新的默认设置 (
BEHAVIOR_SHOW_BARS_BY_SWIPE
)。否则,BEHAVIOR_SHOW_BARS_BY_TOUCH
仍然是默认设置。
- 展示出的功能和呈现出的视觉效果应该是相同的,但
富媒体内容插入
Android 12 引入了一个新的统一 API,可让您从任何可用来源(剪贴板、键盘或拖放操作)接收富媒体内容。
如需了解详情,请参阅用于接收内容的统一 API。
摄像头
Quad Bayer 摄像头传感器支持
如今,许多 Android 设备都配备了超高分辨率摄像头传感器(通常采用 Quad/Nona Bayer 模式),这些传感器在图片质量和弱光性能方面提供了极大的灵活性。Android 12 引入了新的平台 API,可让第三方应用充分利用这些多功能传感器。新 API 支持这些传感器的独特行为,并且考虑到它们在全分辨率或“最大分辨率”模式下而不是“默认”模式下运行时可能支持不同的流配置和组合。
图形和图片
让应用能够直接访问 Tombstone 跟踪记录
从 Android 12 开始,您可以通过 ApplicationExitInfo.getTraceInputStream()
方法以协议缓冲区的形式访问应用的原生代码崩溃 Tombstone。协议缓冲区使用此架构进行序列化。以前,只有通过 Android 调试桥 (adb) 才能访问此信息。
以下示例说明了如何在您的应用中实现此功能:
ActivityManager activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE);
MutableList<ApplicationExitInfo> exitReasons = activityManager.getHistoricalProcessExitReasons(/* packageName = */ null, /* pid = */ 0, /* maxNum = */ 5);
for ( ApplicationExitInfo aei: exitReasons ) {if ( aei.getReason() == REASON_CRASH_NATIVE ) {// Get the tombstone input stream.InputStream tombstoneInputStream = aei.getTraceInputStream();// The tombstone parser built with protoc uses the tombstone schema, then parses the trace.Tombstone tombstone = Tombstone.parseFrom(trace);}
}
AVIF 图片支持
Android 12 引入了对使用 AV1 图片文件格式 (AVIF) 的图片的支持。AVIF 是一种使用 AV1 编码的图片和图片序列的容器格式。它利用了视频压缩的帧内编码内容。与以前的图片格式(例如 JPEG)相比,这种格式可显著提升相同文件大小下的图片质量。如需深入了解此格式的优势,请参阅 Jake Archibald 的博文。
更简单的模糊处理、颜色滤镜及其他效果
Android 12 添加了新的 RenderEffect
,它可将常见的图片效果(如模糊处理、颜色滤镜、Android 着色器效果及更多效果)应用于 View
和渲染层次结构。效果可以组合为连锁效果(构成一种内外效果)或混合效果。由于处理能力有限,不同的 Android 设备可能支持该功能,也可能不支持。
也可以通过调用 View.setRenderEffect(RenderEffect)
将效果应用于 View
的底层 RenderNode
。
如需实现 RenderEffect
,请编写以下代码:
view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))
原生动画图片解码
在 Android 12 中,NDK ImageDecoder
API 已进行了扩展,可以对使用以下文件格式的图片的所有帧和时间数据进行解码:动画 GIF 和动画 WebP 文件格式。在 Android 11 中引入该 API 时,该 API 仅从这些格式的动画中解码第一张图片。
使用 ImageDecoder
(而非第三方库)可进一步缩减 APK 大小,并从未来与安全性和性能相关的更新中受益。
如需详细了解该 API,请参阅 API 参考文档和 GitHub 上的示例。
媒体
兼容的媒体转码
Android 12 可以自动将设备上录制的 HEVC(H.265) 和 HDR(HDR10 和 HDR10+)视频转码为 AVC (H.264),AVC (H.264) 是与标准播放器广泛兼容的格式。因此,当现代编解码器可用且不会影响与旧应用的兼容性时,便会利用现代编解码器。
如需了解详情,请参阅兼容的媒体转码。
性能等级
从 Android 12 开始,Android 引入了一种称为“性能等级”的标准。性能等级指定超出 Android 基准要求的硬件功能。每个 Android 设备都会声明其支持的性能等级。开发者可以在运行时检查设备的性能等级,并提供充分利用设备功能的升级体验。
如需了解详情,请参阅性能等级。
视频编码改进
Android 12 定义了一组标准键来控制视频编码的量化参数 (QP) 值,这样可让开发者避免供应商专用代码。
MediaFormat
API 以及 NDK 媒体库中提供了这些新键。
从 Android 12 开始,视频编码器强制执行一个最低质量阈值。这样可保证用户在对场景复杂性较高的视频进行编码时不会体验到极低的质量。
音频焦点
从 Android 12 开始,当一个应用在另一个应用具有焦点且正在播放音频的同时请求音频焦点时,框架会淡出正在播放音频的应用。
如需了解详情,请参阅音频焦点改进。
MediaDrm 更新
为了确定当前的 MediaDrm
API 是否需要安全的解码器组件,您必须按照以下步骤操作:
- 创建
MediaDrm
。 - 打开会话以获取会话 ID。
- 使用会话 ID 创建
MediaCrypto
。 - 调用
MediaCrypto.requiresSecureDecoderComponent(mimeType)
。
借助新方法 requiresSecureDecoder(@NonNull String mime)
和 requiresSecureDecoder(@NonNull String mime, @SecurityLevel int level)
,您可以在创建 MediaDrm
后立即确定这一点。
安全和隐私设置
蓝牙权限
Android 12 引入了 BLUETOOTH_SCAN
、BLUETOOTH_ADVERTISE
和 BLUETOOTH_CONNECT
权限。这些权限可让以 Android 12 为目标平台的应用更轻松地与蓝牙设备互动,尤其是不需要访问设备位置信息的应用。
注意:配套设备管理器提供了一种更精简的方法来连接到配套设备。系统会代表您的应用提供配对界面。如果您想要更好地控制配对和连接体验,请使用新的蓝牙权限。
如需了解详情,请参阅有关新蓝牙权限的指南。
隐私信息中心
图 1. “位置信息使用情况”屏幕,“隐私信息中心”的一部分。
在搭载 Android 12 的受支持设备上,系统设置中会显示“隐私信息中心”屏幕。在此屏幕上,用户可以访问一些单独的屏幕,这些屏幕显示了应用何时访问位置、摄像头和麦克风信息。每个屏幕都会显示一个时间轴,指明不同的应用何时访问过特定类型的数据。图 1 显示了位置信息的数据访问时间轴。
您的应用可以向用户提供一个理由,帮助他们了解为什么您的应用访问位置、摄像头或麦克风信息。此理由可以显示在新的“隐私信息中心”屏幕和/或您应用的权限屏幕上。
显示数据访问的理由
如需解释为什么您的应用访问位置、摄像头和麦克风信息,请完成以下步骤:
-
添加一个 activity,它在启动后会提供某种理由,说明为什么您的应用执行特定类型的数据访问操作。
如果您的应用以 Android 12 或更高版本为目标平台,则您必须明确地为
android:exported
属性定义一个值。 -
向新添加的 activity 添加以下 intent 过滤器:
<!-- android:exported required if you target Android 12. --> <activity android:name=".DataAccessRationaleActivity"android:permission="android.permission.START_VIEW_PERMISSION_USAGE"android:exported="true"><!-- VIEW_PERMISSION_USAGE shows a selectable information icon onyour app permission's page in system settings.VIEW_PERMISSION_USAGE_FOR_PERIOD shows a selectable informationicon on the Privacy Dashboard screen. --><intent-filterandroid:action="android.intent.action.VIEW_PERMISSION_USAGE"android:action="android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD" ... ></intent-filter> </activity>
-
决定您的数据访问理由 activity 应显示什么内容。例如,您可以显示应用的网站或帮助中心文章。如需提供更详细的解释来说明您的应用访问的数据类型以及访问发生的时间,请处理系统在调用权限使用 intent 时包含的 extra:
- 如果系统调用
ACTION_VIEW_PERMISSION_USAGE
,您的应用可以检索EXTRA_PERMISSION_GROUP_NAME
的值。 - 如果系统调用
ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD
,您的应用可以检索EXTRA_PERMISSION_GROUP_NAME
、EXTRA_ATTRIBUTION_TAGS
、EXTRA_START_TIME
和EXTRA_END_TIME
的值。
- 如果系统调用
根据您添加的 intent 过滤器,用户会在某些屏幕上看到应用的名称旁边有一个信息图标:
- 如果您添加包含
VIEW_PERMISSION_USAGE
操作的 intent 过滤器,用户会在系统设置中的应用权限页面上看到该图标。 - 如果您添加包含
VIEW_PERMISSION_USAGE_FOR_PERIOD
操作的 intent 过滤器,每当您的应用显示在“隐私信息中心”屏幕中,用户都会在应用的名称旁边看到该图标。
当用户选择该图标时,系统会启动应用的理由 activity。
隐藏应用叠加窗口
为了让开发者能够更好地控制用户在与开发者的应用互动时会看到什么内容,Android 12 引入了隐藏由具有 SYSTEM_ALERT_WINDOW
权限的应用绘制的叠加窗口的功能。
声明 HIDE_OVERLAY_WINDOWS
权限后,应用可以调用 setHideOverlayWindows()
以指明当应用自己的窗口可见时所有 TYPE_APPLICATION_OVERLAY
类型的窗口都应隐藏。在显示敏感屏幕(如交易确认流程)时,应用可能会选择这样做。
显示 TYPE_APPLICATION_OVERLAY
类型窗口的应用应考虑可能更适合其用例的替代方案,如画中画或气泡。
已知签名者权限保护标志
Android 12 引入了签名级权限的 knownCerts
属性。此属性可让您在声明时引用已知签名证书的摘要。
您的应用可以声明此属性,并在给定签名级权限的 protectionLevel
属性中使用新的 knownSigner
标志。当您的应用执行此操作时,如果某个发起请求的应用的签名谱系中的任何签名者(包括当前签名者)与使用 knownCerts
属性中的权限声明的某个摘要匹配,则系统会向该发起请求的应用授予权限。
knownSigner
标志可让设备和应用向其他应用授予签名权限,而不必在设备制造和装运时为应用签名。
设备属性认证
Android 12 扩展了一组应用,当这些应用生成新密钥时,可以验证认证证书中的设备属性。
自 Android 9(API 级别 28)起,使用 Keymaster 4.0 或更高版本的设备政策所有者 (DPO) 可以验证这些认证证书中的设备属性。从 Android 12 开始,任何以 Android 12 为目标平台的应用都可以使用 setDevicePropertiesAttestationIncluded()
方法执行此验证。
生成的设备属性包含以下 Build
字段:
BRAND
DEVICE
MANUFACTURER
MODEL
PRODUCT
安全锁定屏幕通知操作
Android 12 向 Notification.Action.Builder
添加了新的 setAuthenticationRequired
标志。此标志可让您为锁定设备上的通知增添一层额外的安全保障。
将此标志应用于给定的通知操作且值为 true
时,在锁定设备上调用该操作的用户始终都会收到身份验证请求。以前,只有在用户对通知操作的调用启动了 activity 或者是直接回复时,系统才会请求在锁定设备上进行身份验证。
如需实现此功能,请向通知操作添加 setAuthenticationRequired
:
Notification n1 = new Notification.Builder(context, NotificationListenerVerifierActivity.TAG)
...
.addAction(new Notification.Action.Builder(R.drawable.ic_stat_charlie,
context.getString(R.string.action_test_title), makeBroadcastIntent(context))// Make sure this notification action will always request authentication when
// invoked from a lock screen
.setAuthenticationRequired(true).build()).build();
连接性
带宽估测改进
在 Android 12 中,由 getLinkDownstreamBandwidthKbps()
和 getLinkUpstreamBandwidthKbps()
提供的带宽估测功能针对 Wi-Fi 和移动网络连接都得到了改进。现在,返回的值表示用户在设备上的所有应用中的每个运营商或 WiFi SSID、网络类型和信号电平的全时加权平均吞吐量。这样可返回对预期吞吐量的更准确且更实际的估测数据,提供对应用冷启动的估测数据,并且与使用其他吞吐量估测方法相比需要更少的周期。
使配套应用保持唤醒状态
为了满足让配套应用保持运行状态以管理设备的需求,Android 12 引入了具有以下作用的 API:
- 当配套设备处于范围内时,让您可以唤醒某个应用。
- 保证该过程在设备处于范围内时将继续运行。
如需使用相应的 API,必须使用配套设备管理器连接您的设备。如需了解详情,请参阅 CompanionDeviceManager.startObservingDevicePresence()
和 CompanionDeviceService.onDeviceAppeared()
。
配套设备管理器配置文件
现在,以 Android 12 及更高版本为目标平台的合作伙伴应用在连接到手表时可以使用配套设备配置文件。使用配置文件可将一组设备类型专用权限的授予操作捆绑在一个步骤中,从而简化注册过程。
设备连接后就会向配套应用授予捆绑的权限,且这些权限仅在设备关联时持续有效。删除应用或移除关联时会移除权限。
如需了解详情,请参阅 AssociationRequest.Builder.setDeviceProfile()
。
Wi-Fi 感知 (NAN) 增强功能
Android 12 增强了 Wi-Fi 感知功能:
- 在搭载 Android 12 及更高版本的设备上,您可以使用
onServiceLost()
回调,以便在应用因服务停止或超出范围而导致已发现的服务丢失时收到提醒。 - 设置多个数据路径(NAN 数据路径)的方式将发生变化以提高效率。较低的版本使用 L2 消息功能来交换发起方的对等信息,由此导致了延迟。在搭载 Android 12 及更高版本的设备上,可以将响应方(服务器)配置为接受任何对等方,也就是说,它不需要预先知道发起方信息。这可加快数据路径启动,并只需一个网络请求即可实现多个点对点链接。
- 为了防止框架因资源不足而拒绝发现请求或连接请求,在搭载 Android 12 及更高版本的设备上,您可以调用
WifiAwareManager.getAvailableAwareResources()
。通过此方法的返回值,您可以获得可用数据路径的数量、可用发布会话的数量以及可用的订阅会话数量。
并发点对点 + 互联网连接
以 Android 12 及更高版本为目标平台的设备在具有硬件支持的设备上运行时,在与对等设备建立连接时,使用点对点连接不会断开与现有 Wi-Fi 的连接。如需检查是否支持此功能,请使用 WifiManager.isMultiStaConcurrencySupported()
。
存储
Android 12 引入了对存储管理 API 的几项变更,下面几部分对此进行了介绍。
语音录音的新目录
系统会将存储在新 Environment.DIRECTORY_RECORDINGS
文件夹中的音频文件识别为录音。当您的应用对系统的媒体库执行查询时,您可以使用 IS_RECORDING
标志来检索录音。
媒体管理访问权限
用户可能会信任特定的应用来执行媒体管理,如频繁地修改媒体文件。如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台且不是设备的默认图库应用,则每次您的应用尝试修改或删除文件时,您都必须向用户显示一个确认对话框。
如果您的应用以 Android 12 为目标平台,您可以请求用户向您的应用授予执行以下各项操作的权限,而无需针对每项文件操作提示用户:
- 使用
createWriteRequest()
修改文件。 - 使用
createTrashRequest()
将文件移入和移出回收站。 - 使用
createDeleteRequest()
删除文件。
为此,请完成以下步骤:
-
在应用的清单文件中声明新的
MANAGE_MEDIA
权限和READ_EXTERNAL_STORAGE
权限。为了调用
createWriteRequest()
而不显示确认对话框,请同时声明ACCESS_MEDIA_LOCATION
权限。 -
在您的应用中,向用户显示一个界面,说明为什么他们可能需要向您的应用授予媒体管理访问权限。
-
调用
ACTION_REQUEST_MANAGE_MEDIA
intent 操作。这样会将用户引导至系统设置中的媒体管理应用屏幕。在此处,用户可以授予特殊应用访问权限。
应用存储访问权限
应用可以声明并创建一个自定义 activity,该 activity 在启动后可让用户管理应用存储在用户设备上的数据。应用可以在清单文件中使用 android:manageSpaceActivity
属性声明此自定义“管理空间”activity。文件管理器应用可以启动此“管理空间”activity,即使应用未导出该 activity(即,该 activity 将 android:exported
设置为 false
)也是如此。
在 Android 12 中,同时具有 MANAGE_EXTERNAL_STORAGE
权限和 QUERY_ALL_PACKAGES
权限的应用(如文件管理应用)可使用新的 getManageSpaceActivityIntent()
将用户引导至其他应用的自定义“管理空间”activity(如果为该应用定义了一个)。
getManageSpaceActivityIntent()
方法接受软件包名称和请求代码,它返回以下某一项:
PendingIntent
– 如果具有指定软件包名称的应用已定义自定义“管理空间”activity。调用getManageSpaceActivityIntent()
方法的应用随后可以调用返回的 intent 以将用户引导至该自定义 activity。null
– 如果具有指定软件包名称的应用未定义“管理空间”activity。
扩展的文件访问权限支持
除了对 ExternalStorageProvider
URI 的现有支持以外,getMediaUri()
方法现在还支持 MediaDocumentsProvider
URI。现在,系统在返回这些 URI 之前将其授予调用方。
此外,由 createWriteRequest()
授予的媒体 URI 现在支持 File
类中的 API。这些 API 提供了读取、写入、重命名和删除文件的功能。
核心功能
自动更新应用
Android 12 针对使用 PackageInstaller
API 的应用引入了 setRequireUserAction()
方法。此方法可让安装程序应用执行应用更新而无需用户确认操作。
设备芯片组信息
Android 12 向 android.os.Build
添加了两个常量,它们可通过 SDK 公开 SoC 芯片组供应商和型号信息。您可以通过分别调用 Build.SOC_MANUFACTURER
和 Build.SOC_MODEL
来检索此信息。
二:
目录
一:功能和 API 概览
新体验
微件改进
音频耦合触感反馈效果
启动画面 API
允许按来电重要性排名的新通话通知
通知的丰富图片支持
圆角 API
画中画 (PiP) 改进
沉浸模式下的手势导航改进
富媒体内容插入
摄像头
Quad Bayer 摄像头传感器支持
图形和图片
让应用能够直接访问 Tombstone 跟踪记录
AVIF 图片支持
更简单的模糊处理、颜色滤镜及其他效果
原生动画图片解码
媒体
兼容的媒体转码
性能等级
视频编码改进
音频焦点
MediaDrm 更新
安全和隐私设置
蓝牙权限
隐私信息中心
隐藏应用叠加窗口
已知签名者权限保护标志
设备属性认证
安全锁定屏幕通知操作
连接性
带宽估测改进
使配套应用保持唤醒状态
配套设备管理器配置文件
Wi-Fi 感知 (NAN) 增强功能
并发点对点 + 互联网连接
存储
语音录音的新目录
媒体管理访问权限
应用存储访问权限
扩展的文件访问权限支持
核心功能
自动更新应用
设备芯片组信息
二:
行为变更:所有应用
用户体验
滚动效果
前台服务通知用户体验延迟
沉浸模式下的手势导航改进
网络 intent 解析
限制性应用待机模式存储分区
Display#getRealSize 和 getRealMetrics:废弃和沙盒
图形和图片
改进了刷新率切换
安全和隐私设置
麦克风和摄像头切换开关
麦克风和摄像头指示标志
应用无法关闭系统对话框
不受信任的触摸事件被屏蔽
权限软件包可见性
移除了 Bouncy Castle 实现
剪贴板访问通知
连接性
Passpoint 更新
更新后的非 SDK 接口限制
行为变更:所有应用
Android 12 平台包含一些行为变更,这些变更可能会影响您的应用。以下行为变更将影响在 Android 12 上运行的所有应用,无论采用哪种 targetSdkVersion
都不例外。您应该测试您的应用,然后根据需要进行修改,以适当地支持这些变更。
此外,请务必查看仅影响以 Android 12 为目标平台的应用的行为变更列表。
用户体验
滚动效果
滚动事件的行为在 Android 12 中发生了变化。如需了解详情,请参阅滚动效果。
前台服务通知用户体验延迟
为了在 Android 12 上提供针对短时间运行的前台服务的流畅体验,系统可以为某些前台服务延迟 10 秒显示前台服务通知。此更改使某些短期任务在显示通知之前完成。
如果某项前台服务至少具有以下特征之一,则系统会在服务启动后立即显示相关通知:
- 该服务与包含操作按钮的通知相关联。
- 该服务的
foregroundServiceType
为mediaPlayback
、mediaProjection
或phoneCall
。 - 该服务根据通知的类别属性中的定义,提供与通话、导航或媒体播放相关的用例。
- 该服务通过在设置通知时将
FOREGROUND_SERVICE_IMMEDIATE
传入setForegroundServiceBehavior()
,已停用行为变更。
沉浸模式下的手势导航改进
Android 12 简化了沉浸模式,使手势导航更简单且与其余活动体验(例如观看视频和阅读图书)更加一致。如需了解详情,请参阅功能页面上的相应条目。
网络 intent 解析
为了在用户选择网页链接时提供更流畅的体验,如果某个给定的网络 intent 包含未批准的网域,Android 12 会在用户的默认浏览器中打开该 intent。您的应用可以使用以下某种方法来获准处理网域:
- 使用 Android App Links 验证网域。
- 请求用户将您的应用与网域相关联。
如果您的应用调用网络 intent,不妨考虑添加一个提示或对话框,要求用户确认操作。
详细了解网络 intent 解析的变更。
限制性应用待机模式存储分区
应用待机模式存储分区有助于系统根据使用应用的时间新近度和频率来确定应用的资源请求的优先级。
每个存储分区代表一个优先级。低优先级意味着系统会对运行您的应用施加更多限制。
从 Android 12 开始,有一个名为“受限”的新存储分区。在所有存储分区中,受限存储分区的优先级最低(限制最高)。存储分区按优先级从高到低的顺序排列如下:
- 活跃:应用目前正在使用中,或者最近刚刚使用过
- 工作集:会定期使用应用
- 常用:会经常使用应用,但不是每天都使用
- 极少使用:不经常使用应用
- 受限
除了使用模式之外,系统还会考虑应用的行为,以决定是否要将您的应用放在受限存储分区中。如果您的应用更负责地使用系统资源,就不太可能被放在受限存储分区中。
如果用户直接与您的应用互动,系统会将其放在一个限制较少的存储分区中。
电源管理限制
如果系统将您的应用放在受限存储分区中,会受到以下限制:
- 您每天可以在 10 分钟的批处理会话中运行作业一次。在此会话期间,系统会将您应用的作业与其他应用的作业分组在一起。
- 与系统将您的应用放在限制较少的存储分区中相比,您的应用可以运行较少的加急作业。
- 您应用的不精确的闹钟每天传送一次。您在调用
set()
、setInexactRepeating()
、setAndAllowWhileIdle()
或setWindow()
方法时创建不精确的闹钟。 - 您的应用每天可以及时接收五条高优先级 Firebase Cloud Messaging (FCM) 消息。所有后续 FCM 消息都按普通优先级传送,因此如果设备在节能模式下,这些消息可能会延迟。
注意:与其他存储分区不同,即使设备正在充电,受限存储分区也会受到这些电源管理限制。不过,当设备正在充电、处于空闲状态以及在不按流量计费的网络上时,系统会放宽限制。
允许运行前台服务
如果系统将您的应用放在受限存储分区中,您的应用仍可运行前台服务。不过,如果您的应用以 Android 12 为目标平台,它仍会受到前台服务启动限制的影响。
检查您的应用是否在受限存储分区中
如需检查系统是否已将您的应用放在受限存储分区中,请调用 getAppStandbyBucket()
。如果此方法的返回值为 STANDBY_BUCKET_RESTRICTED
,则您的应用在受限存储分区中。
测试受限存储分区行为
如需测试您的应用在系统将其放在受限存储分区中时的行为,您可以手动将您的应用移至该存储分区。为此,请在终端窗口中运行以下命令:
adb shell am set-standby-bucket PACKAGE_NAME restricted
Display#getRealSize 和 getRealMetrics:废弃和沙盒
Android 设备有许多不同的外形规格,如大屏设备、平板电脑和可折叠设备。为了针对每种设备适当地呈现内容,您的应用需要确定屏幕或显示屏尺寸。随着时间的推移,Android 提供了不同的 API 来检索此信息。在 Android 11 中,我们引入了 WindowMetrics
API 并废弃了以下方法:
Display.getSize()
Display.getMetrics()
在 Android 12 中,我们继续建议使用 WindowMetrics
,并且正在逐步废弃以下方法:
Display.getRealSize()
Display.getRealMetrics()
为了缓解应用使用 Display API 检索应用边界的行为,Android 12 添加了一种新的沙盒机制来更正这些 API 返回的信息。这可能会对将此信息与 MediaProjection
一起使用的应用产生影响。
应用应使用 WindowMetrics
API 查询其窗口的边界,并使用 Configuration.densityDpi
查询当前的密度。
为了与较低的 Android 版本实现更广泛的兼容性,您可以使用 Jetpack WindowManager
库,它包含一个 WindowMetrics
类,该类支持 Android 4.0(API 级别 14)及更高版本。
关于如何使用 WindowMetrics 的示例
首先,确保应用的 activity 完全可调整大小。
activity 应依赖于来自 activity 上下文的 WindowMetrics
来执行任何与界面相关的工作,特别是 WindowManager.getCurrentWindowMetrics()
。
如果您的应用创建了 MediaProjection
,则必须正确地调整边界的大小,因为投影会捕捉显示内容。如果应用完全可调整大小,则 activity 上下文会返回正确的边界,如下所示:
WindowMetrics projectionMetrics = activityContext.getSystemService(WindowManager.class).getMaximumWindowMetrics();
如果应用并非完全可调整大小,则必须从 WindowContext
实例查询边界,并使用 WindowManager.getMaximumWindowMetrics()
检索应用可用的最大显示区域的 WindowMetrics
。
Context windowContext = mContext.createWindowContext(mContext.getDisplay(),TYPE_APPLICATION, null /* options */);
WindowMetrics projectionMetrics = windowContext.getWindowManager().getMaximumWindowMetrics();
注意:使用 MediaProjection
的任何库也应遵循此建议,并查询应用窗口的相应 WindowMetrics
。
图形和图片
改进了刷新率切换
在 Android 12 中,无论显示屏是否支持无缝过渡到新的刷新率,都会发生使用 setFrameRate()
实现的刷新率变化;无缝过渡是指没有任何视觉中断,比如一两秒钟的黑屏。以前,如果显示屏不支持无缝过渡,它在调用 setFrameRate()
后通常会继续使用同一刷新率。您可以调用 getAlternativeRefreshRates()
来提前确定向新刷新率的过渡是否有可能是无缝过渡。通常,会在刷新率切换完成后调用回调 onDisplayChanged()
,但对于某些外接显示屏,会在非无缝过渡期间调用该回调。
以下示例说明了您可以如何实现此行为:
Kotlin
// Determine whether the transition will be seamless.
// Non-seamless transitions may cause a 1-2 second black screen.
val refreshRates = this.display?.mode?.alternativeRefreshRatesval willbeSeamless = Arrays.asList<FloatArray>(refreshRates).contains(newRefreshRate)// Set the frame rate even if the transition will not be seamless.
surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
Java
// Determine whether the transition will be seamless.
// Non-seamless transitions may cause a 1-2 second black screen.
Display display = context.getDisplay(); // API 30+
Display.Mode mode = display.getMode();
float[] refreshRates = mode.getAlternativeRefreshRates();
boolean willbeSeamless = Arrays.asList(refreshRates).contains(newRefreshRate);// Set the frame rate even if the transition will not be seamless.
surface.setFrameRate(newRefreshRate, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS);
安全和隐私设置
麦克风和摄像头切换开关
图 1. “快捷设置”中的麦克风和摄像头切换开关。
在 Android 12 中,受支持的设备允许用户通过按一个切换开关选项,为设备上的所有应用启用和停用摄像头和麦克风使用权限。用户可以从快捷设置访问可切换的选项(如图 1 所示),也可以从系统设置中的“隐私设置”屏幕访问。
摄像头和麦克风切换开关会影响设备上的所有应用:
- 当用户关闭摄像头使用权限后,应用会收到空白的摄像头画面。
-
当用户关闭麦克风使用权限后,应用会收到无声音频。此外,无论您是否声明
HIGH_SAMPLING_RATE_SENSORS
权限,移动传感器都有采样率限制。注意:当用户拨打应急服务电话(如 911)时,系统会开启麦克风使用权限。此行为可保护用户安全。
当用户关闭摄像头或麦克风的使用权限,然后启动需要使用摄像头或麦克风信息的应用时,系统会提醒用户,设备范围的切换开关已关闭。
检查给定的设备是否支持麦克风和摄像头切换开关
如需检查设备是否支持麦克风和摄像头切换开关,请添加以下代码段中所示的逻辑:
Kotlin
val sensorPrivacyManager = applicationContext.getSystemService(SensorPrivacyManager::class.java)as SensorPrivacyManager
val supportsMicrophoneToggle = sensorPrivacyManager.supportsSensorToggle(Sensors.MICROPHONE)
val supportsCameraToggle = sensorPrivacyManager.supportsSensorToggle(Sensors.CAMERA)
Java
SensorPrivacyManager sensorPrivacyManager = getApplicationContext().getSystemService(SensorPrivacyManager.class);
boolean supportsMicrophoneToggle = sensorPrivacyManager.supportsSensorToggle(Sensors.MICROPHONE);
boolean supportsCameraToggle = sensorPrivacyManager.supportsSensorToggle(Sensors.CAMERA);
检查响应麦克风和摄像头切换开关的应用行为
麦克风和摄像头切换开关不应影响您的应用处理 CAMERA
和 RECORD_AUDIO
权限的方式,前提是您遵循关于 Android 权限的最佳做法。
特别是,确保您的应用做到以下几点:
- 等到用户向您的应用授予
CAMERA
权限后再使用设备的摄像头。 - 等到用户向您的应用授予
RECORD_AUDIO
权限后再使用设备的麦克风。
麦克风和摄像头指示标志
麦克风和摄像头指示标志,显示了最近的数据访问。
在搭载 Android 12 的设备上,当应用使用麦克风或摄像头时,图标会出现在状态栏中。如果应用处于沉浸模式,则图标会出现在屏幕的右上角。用户可以打开“快捷设置”,并选择图标以查看哪些应用当前正在使用麦克风或摄像头。图 2 显示了包含图标的示例屏幕截图。
为了提供更好的用户体验,在用户明确向您的应用授予权限之前,请勿使用麦克风或摄像头。
应用无法关闭系统对话框
为了加强用户与应用和系统互动时的控制,从 Android 12 开始,弃用了 ACTION_CLOSE_SYSTEM_DIALOGS
intent 操作。除了一些特殊情况之外,当应用尝试调用包含此操作的 intent 时,系统会基于应用的目标 SDK 版本执行以下操作之一:
- 如果应用以 Android 12 为目标平台,则会发生
SecurityException
。 -
如果应用以 Android 11(API 级别 30)或更低版本为目标平台,则系统不会执行 intent,并且 Logcat 中会显示以下消息:
E ActivityTaskManager Permission Denial: \ android.intent.action.CLOSE_SYSTEM_DIALOGS broadcast from \ com.package.name requires android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, \ dropping broadcast.
异常
在以下情况下,应用仍然可以在 Android 12 上关闭系统对话框:
- 您的应用运行的是插桩测试。
-
您的应用以 Android 11 或更低版本为目标平台,并在抽屉式通知栏顶部显示一个窗口。
注意:如果您的应用以 Android 12 为目标平台,在这种情况下您无需使用
ACTION_CLOSE_SYSTEM_DIALOGS
。这是因为,如果在窗口位于抽屉式通知栏的顶部时您的应用调用startActivity()
,系统会自动关闭抽屉式通知栏。 -
您的应用以 Android 11 或更低版本为目标平台。此外,用户已与通知互动,可能使用了通知的操作按钮,您的应用正在处理服务或广播接收器来响应该用户操作。
-
您的应用以 Android 11 或更低版本为目标平台并且具有有效的无障碍服务。如果您的应用以 Android 12 为目标平台并且想要关闭通知栏,请改用
GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE
无障碍操作。
不受信任的触摸事件被屏蔽
为了维持系统安全并保持良好的用户体验,Android 12 会阻止应用使用触摸事件,使用触摸事件时叠加层会以不安全的方式遮掩应用。 换言之,系统会屏蔽穿透某些窗口的触摸操作,但有一些例外情况。
受影响的应用
此变更会影响选择让触摸操作穿透其窗口的应用,例如使用 FLAG_NOT_TOUCHABLE
标志。包括但不限于以下示例:
- 需要
SYSTEM_ALERT_WINDOW
权限并使用FLAG_NOT_TOUCHABLE
标志的叠加层,例如使用TYPE_APPLICATION_OVERLAY
的窗口。 - 使用
FLAG_NOT_TOUCHABLE
标志的 activity 窗口。
异常
在以下情况下,允许执行“穿透”触摸操作:
- 应用中的互动。您的应用会显示叠加层,并且只有当用户与您的应用进行互动时才会显示叠加层。
-
可信窗口。包括但不限于以下窗口:
- 无障碍窗口
- 输入法 (IME) 窗口
- Google 助理窗口
注意:类型为
TYPE_APPLICATION_OVERLAY
的窗口不受信任。 -
不可见窗口。窗口的根视图是
GONE
或INVISIBLE
。 -
全透明窗口。窗口的
alpha
属性为 0.0。 -
足够半透明的系统警报窗口。当组合后的不透明度小于或等于系统针对触摸的最大遮掩不透明度时,系统会将一组系统警报窗口视为足够半透明。在 Android 12 Beta 版中,这一最大不透明度为 0.8。此值在未来的 Beta 版中可能会发生变化。
在不受信任的触摸操作被屏蔽时能够检测到
如果系统屏蔽触摸操作,Logcat 会记录以下消息:
Untrusted touch due to occlusion by PACKAGE_NAME
测试变更
在搭载 Android 12 开发者预览版 3 的设备上,默认情况下会屏蔽不受信任的触摸操作。如需允许不受信任的触摸操作,请在终端窗口中运行以下 ADB 命令:
# A specific app
adb shell am compat disable BLOCK_UNTRUSTED_TOUCHES com.example.app# All apps
# If you'd still like to see a Logcat message warning when a touch would be
# blocked, use 1 instead of 0.
adb shell settings put global block_untrusted_touches 0
如需将行为还原为默认设置(不受信任的触摸操作被屏蔽),请运行以下命令:
# A specific app
adb shell am compat reset BLOCK_UNTRUSTED_TOUCHES com.example.app# All apps
adb shell settings put global block_untrusted_touches 2
权限软件包可见性
在搭载 Android 12 的设备上,根据应用对其他应用的软件包可见性,以 Android 11(API 级别 30)或更高版本为目标平台且调用以下某种方法的应用会收到一组过滤后的结果:
getAllPermissionGroups()
getPermissionGroupInfo()
getPermissionInfo()
queryPermissionsByGroup()
移除了 Bouncy Castle 实现
Android 12 移除了之前废弃的加密算法(包括所有 AES 算法)的许多 BouncyCastle 实现。系统改用这些算法的 Conscrypt 实现。
如果符合以下任何条件,则此变更会影响您的应用:
- 您的应用使用 512 位的密钥大小。Conscrypt 不支持此密钥大小。如有必要,请更新您应用的加密逻辑以使用其他密钥大小。
-
您的应用将无效的密钥大小与
KeyGenerator
一起使用。与 BouncyCastle 相比,Conscrypt 的KeyGenerator
实现会对密钥参数执行额外的验证。例如,Conscrypt 不允许您的应用生成 64 位 AES 密钥,因为 AES 仅支持 128 位、192 位和 256 位密钥。BouncyCastle 允许生成大小无效的密钥,但如果稍后这些密钥与
Cipher
一起使用,验证会失败。如果使用 Conscrypt,验证失败的时间会更早。 -
您使用并非 12 字节的大小初始化伽罗瓦/计数器模式 (GCM) 加密。Conscrypt 的
GcmParameterSpec
实现要求初始化为 12 字节,这是 NIST 推荐的做法。
剪贴板访问通知
在 Android 12 中,当某个应用首次调用 getPrimaryClip()
以访问来自其他应用的 ClipData
时,系统会显示一条消息框消息,通知用户此次剪贴板访问。
消息框消息内的文本包含以下格式:APP pasted from your clipboard.
检索剪贴说明时未显示消息
您的应用可能会调用 getPrimaryClipDescription()
以接收有关剪贴板上当前数据的信息。当您的应用调用此方法时,系统不会显示消息框消息。
Android 12 增强了此方法以检测下面这些额外的详细信息:
- 使用
isStyledText()
检测样式化文本。 - 使用
getConfidenceScore()
检测文本的不同分类,如网址。
连接性
Passpoint 更新
Android 12 中添加了以下 API:
isPasspointTermsAndConditionsSupported()
:“条款及条件”是一项 Passpoint 功能,允许网络部署将不安全的强制门户(使用开放网络)替换为安全的 Passpoint 网络。当要求用户接受条款及条件时,系统会向用户显示一条通知。如果应用建议的 Passpoint 网络受条款及条件制约,应用必须先调用此 API,以确保设备支持该功能。如果设备不支持该功能,就不能连接到此网络,并且必须建议一个替代网络或旧网络。-
isDecoratedIdentitySupported()
:对带有前缀修饰的网络进行身份验证时,修饰的身份前缀允许网络运营商更新网络访问标识符 (NAI),以通过 AAA 网络内的多个代理执行显式路由(如需详细了解这一点,请参阅 RFC 7542)。Android 12 实现了此功能,以符合 PPS-MO 扩展的 WBA 规范。如果应用建议的 Passpoint 网络需要修饰的身份,应用必须先调用此 API,以确保设备支持该功能。如果设备不支持该功能,身份就不会进行修饰,并且对网络的身份验证可能会失败。
如需创建 Passpoint 建议,应用必须使用 PasspointConfiguration
、Credential
和 HomeSp
类。这些类描述了 Wi-Fi Alliance Passpoint 规范中定义的 Passpoint 配置文件。
更新后的非 SDK 接口限制
Android 12 包含更新后的受限制非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。
如果您的应用并非以 Android 12 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API。
如需详细了解此 Android 版本中的变更,请参阅 Android 12 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。