Android 17 面向开发者引入了一些出色的新功能和 API。以下部分总结了这些功能,可帮助您开始使用相关 API。
有关新增、修改和移除的 API 的详细列表,请参阅 API 差异报告。如需详细了解新的 API,请访问 Android API 参考文档,新 API 会突出显示以方便查看。
您还应查看平台变更可能会在哪些方面影响您的应用。如需了解详情,请参阅以下页面:
核心功能
Android 17 添加了以下与 Android 核心功能相关的新功能。
新的 ProfilingManager 触发器
Android 17 adds several new system triggers to ProfilingManager to
help you collect in-depth data to debug performance issues.
The new triggers are:
TRIGGER_TYPE_COLD_START: Trigger occurs during app cold start. It provides both a call stack sample and a system trace in the response.TRIGGER_TYPE_OOM: Trigger occurs when an app throws anOutOfMemoryErrorand provides a Java Heap Dump in response.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Trigger occurs when an app is killed due to abnormal and excessive CPU usage and provides a call stack sample in response.TRIGGER_TYPE_ANOMALY: Detect system performance anomalies such as excessive binder calls and excessive memory usage.
To understand how to set up the system trigger, see the documentation on trigger-based profiling and how to retrieve and analyze profiling data documentation.
Profiling trigger for app anomalies
Android 17
introduces an on-device anomaly detection service that monitors for
resource-intensive behaviors and potential compatibility regressions. Integrated
with ProfilingManager, this service allows your app to receive profiling
artifacts triggered by specific system-detected events.
Use the TRIGGER_TYPE_ANOMALY trigger to detect system performance issues
such as excessive binder calls and excessive memory usage. When an app breaches
OS-defined memory limits, the anomaly trigger allows developers to receive
app-specific heap dumps to help identify and fix memory issues. Additionally,
for excessive binder spam, the anomaly trigger provides a stack sampling profile
on binder transactions.
This API callback occurs prior to any system imposed enforcements. For example, it can help developers collect debug data before the app is terminated by the system for exceeding memory limits.
val profilingManager =
applicationContext.getSystemService(ProfilingManager::class.java)
val triggers = ArrayList<ProfilingTrigger>()
triggers.add(ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_ANOMALY))
val mainExecutor: Executor = Executors.newSingleThreadExecutor()
val resultCallback = Consumer<ProfilingResult> { profilingResult ->
if (profilingResult.errorCode != ProfilingResult.ERROR_NONE) {
// upload profile result to server for further analysis
setupProfileUploadWorker(profilingResult.resultFilePath)
}
profilingManager.registerForAllProfilingResults(mainExecutor,
resultCallback)
profilingManager.addProfilingTriggers(triggers)
}
JobDebugInfo API
Android 17 introduces new JobDebugInfo APIs to help developers debug
their JobScheduler jobs--why they aren't running, how long they ran for, and
other aggregated information.
The first method of the expanded JobDebugInfo APIs is
getPendingJobReasonStats(), which returns a map of reasons why the job was in
a pending execution state and their respective cumulative pending
durations. This method joins the getPendingJobReasonsHistory() and
getPendingJobReasons() methods to give you insight into why a scheduled
job is not running as expected, but simplifies information retrieval by making
both duration and job reason available in a single method.
For example, for a specified jobId, the method might return
PENDING_JOB_REASON_CONSTRAINT_CHARGING and a duration of 60000 ms, indicating
the job was pending for 60000ms due to the charging constraint not being
satisfied.
通过监听器支持“空闲时允许”闹钟,减少唤醒锁定
Android 17
introduces a new variant of AlarmManager.setExactAndAllowWhileIdle that
accepts an OnAlarmListener instead of a PendingIntent. This new
callback-based mechanism is ideal for apps that currently rely on continuous
wakelocks to perform periodic tasks, such as messaging apps maintaining socket
connections.
隐私权
Android 17 包含以下新功能,可更好地保护用户隐私。
加密客户端 Hello (ECH) 平台支持
Android 17 引入了对加密客户端 Hello (ECH) 的平台支持,这是对网络通信的一项重大隐私增强功能。ECH 是一项 TLS 1.3 扩展,可在初始 TLS 握手期间加密服务器名称指示 (SNI)。这种加密有助于保护用户隐私,因为它可以让网络中介更难识别应用连接到的特定网域。
该平台现在包含网络库实现 ECH 所需的 API。这包括 DnsResolver 中的新功能,用于查询包含 ECH 配置的 HTTPS DNS 记录;以及 Conscrypt 的 SSLEngine 和 SSLSocket 中的新方法,用于在连接到网域时传入这些配置来启用 ECH。开发者可以通过网络安全配置文件中的新 <domainEncryption> 元素来配置 ECH 偏好设置,例如机会性地启用 ECH 或强制使用 ECH,这些设置可全局应用,也可按网域应用。
预计 HttpEngine、WebView 和 OkHttp 等热门联网库将在未来的更新中集成这些平台 API,从而使应用能够更轻松地采用 ECH 并增强用户隐私保护。
如需了解详情,请参阅加密的客户端 Hello 文档。
Android 联系人选择工具
Android 联系人选择工具是一个标准化的可浏览界面,可供用户与您的应用分享联系人。该选择工具适用于搭载 Android 17(API 级别 37)或更高版本的设备,可替代广泛的 READ_CONTACTS 权限,从而保护隐私。您的应用无需请求访问用户的整个通讯簿,而是指定所需的数据字段(例如电话号码或邮箱),然后由用户选择要分享的特定联系人。这会向您的应用授予对所选数据的读取权限,从而确保精细控制,同时通过内置搜索、个人资料切换和多选功能提供一致的用户体验,而无需构建或维护界面。
如需了解详情,请参阅联系人选择工具文档。
安全
Android 17 添加了以下新功能,以提高设备和应用的安全性。
Android 高级保护模式 (AAPM)
Android 高级保护模式为 Android 用户提供了一套强大的新安全功能,标志着在保护用户(尤其是面临较高风险的用户)免遭复杂攻击方面迈出了重要一步。AAPM 是一项选择启用功能,只需进行一项配置设置即可激活。用户可以随时启用该功能,以应用一套主观的安全保护措施。
这些核心配置包括:禁止安装未知来源的应用(旁加载)、限制 USB 数据信号传输,以及强制执行 Google Play 保护机制扫描,从而显著减小设备的攻击面。
开发者可以使用 AdvancedProtectionManager API 与此功能集成,以检测模式的状态,从而使应用能够在用户选择启用此模式时自动采用强化型安全姿态或限制高风险功能。
PQC APK 签名
Android now supports a hybrid APK signature scheme to future-proof your app's signing identity against the potential threat of attacks that make use of quantum computing. This feature introduces a new APK Signature Scheme, which lets you pair a classical signing key (such as RSA or EC) with a new post-quantum cryptography (PQC) algorithm (ML-DSA).
This hybrid approach ensures your app remains secure against future quantum attacks while maintaining full backward compatibility with older Android versions and devices that rely on classical signature verification.
Impact on developers
- Apps using Play App Signing: If you use Play App Signing, you can wait for Google Play to give you the option to upgrade a hybrid signature using a PQC key generated by Google Play, ensuring your app is protected without requiring manual key management.
- Apps using self-managed keys: Developers who manage their own signing keys can utilize updated Android build tools (like apksigner) to rotate to a hybrid identity, combining a PQC key with a new classical key. (You must create a new classical key, you cannot reuse the older one.)
连接
Android 17 添加了以下功能,以改进设备和应用连接。
受限卫星网络
Implements optimizations to enable apps to function effectively over low-bandwidth satellite networks.
用户体验和系统界面
Android 17 包含以下旨在提升用户体验的变更。
专用 Google 助理音量流
Android 17 为辅助应用引入了专用的 Google 助理音量流,以便使用 USAGE_ASSISTANT 进行播放。此项变更将 Google 助理音频与标准媒体流分离开来,让用户可以分别控制这两种音量。这样一来,您就可以在静音媒体播放的同时保持 Google 助理回答的可听性,反之亦然。
能够访问新 MODE_ASSISTANT_CONVERSATION 音频模式的辅助应用可以进一步提高音量控制的一致性。助理应用可以使用此模式向系统提供有关活跃助理会话的提示,确保可以在活跃的 USAGE_ASSISTANT 播放之外或通过连接的蓝牙外围设备控制助理音频流。
Handoff
Handoff is a new feature and API coming to Android 17 that app developers can integrate with to provide cross-device continuity for their users. It allows the user to start an app activity on one Android device and transition it to another Android device. Handoff runs in the background of a user's device and surfaces available activities from the user's other nearby devices through various entry points, like the launcher and taskbar, on the receiving device.
Apps can designate Handoff to launch the same native Android app, if it is installed and available on the receiving device. In this app-to-app flow, the user is deep-linked to the designated activity. Alternatively, app-to-web Handoff can be offered as a fallback option or directly implemented with URL Handoff.
Handoff support is implemented on a per-activity basis. To enable Handoff, call
the
setHandoffEnabled()
method for the activity. Additional data may need to be passed along with the
handoff so the recreated activity on the receiving device can restore
appropriate state. Implement the
onHandoffActivityDataRequested()
callback to return a
HandoffActivityData object which
contains details that specify how Handoff should handle and recreate the
activity on the receiving device.
实时动态 - 语义颜色 API
在 Android 17 中,实时动态推出了语义着色 API,以支持具有通用含义的颜色。
以下类支持语义着色:
NotificationNotification.MetricNotification.ProgressStyle.PointNotification.ProgressStyle.Segment
填色游戏
- 绿色:与安全相关。 此颜色应在让用户知道您处于安全情况时使用。
- 橙色:用于表示警告和标记物理危险。当用户需要注意设置更好的保护设置时,应使用此颜色。
- 红色:通常表示危险、停止。它应该在需要人们紧急关注的情况下显示。
- 蓝色:中性色,用于提供信息且应从其他内容中脱颖而出的内容。
以下示例展示了如何将语义样式应用于通知中的文本:
val ssb = SpannableStringBuilder()
.append("Colors: ")
.append("NONE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_UNSPECIFIED), 0)
.append(", ")
.append("INFO", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_INFO), 0)
.append(", ")
.append("SAFE", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_SAFE), 0)
.append(", ")
.append("CAUTION", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_CAUTION), 0)
.append(", ")
.append("DANGER", Notification.createSemanticStyleAnnotation(SEMANTIC_STYLE_DANGER), 0)
Notification.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle("Hello World!")
.setContentText(ssb)
.setOngoing(true)
.setRequestPromotedOngoing(true)
适用于 Android 17 的 UWB Downlink-TDoA API
下行链路到达时间差 (DL-TDoA) 测距功能可让设备通过测量信号的相对到达时间来确定其相对于多个锚点的位置。以下代码段演示了如何初始化测距管理器、验证设备功能以及启动 DL-TDoA 会话:
Kotlin
class RangingApp {
fun initDlTdoa(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Register for device capabilities
val capabilitiesCallback = object : RangingManager.RangingCapabilitiesCallback {
override fun onRangingCapabilities(capabilities: RangingCapabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.uwbCapabilities != null && capabilities.uwbCapabilities!!.isDlTdoaSupported) {
startDlTDoASession(context)
}
}
}
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback)
}
fun startDlTDoASession(context: Context) {
// Initialize the Ranging Manager
val rangingManager = context.getSystemService(RangingManager::class.java)
// Create session and configure parameters
val executor = Executors.newSingleThreadExecutor()
val rangingSession = rangingManager.createRangingSession(executor, RangingSessionCallback())
val rangingRoundIndexes = byteArrayOf(0)
val config: ByteArray = byteArrayOf() // OOB config data
val params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes)
val rangingDevice = RangingDevice.Builder().build()
val rawTagDevice = RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build()
val dtTagConfig = RawDtTagRangingConfig.Builder(rawTagDevice).build()
val preference = RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(SessionConfig.Builder().build())
.build()
// Start the ranging session
rangingSession.start(preference)
}
}
private class RangingSessionCallback : RangingSession.Callback {
override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
// Process measurement results here
}
}
Java
public class RangingApp {
public void initDlTdoa(Context context) {
// Initialize the Ranging Manager
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Register for device capabilities
RangingManager.CapabilitiesCallback capabilitiesCallback = new RangingManager.RangingCapabilitiesCallback() {
@Override
public void onRangingCapabilities(RangingCapabilities capabilities) {
// Make sure Dl-TDoA is supported before starting the session
if (capabilities.getUwbCapabilities() != null && capabilities.getUwbCapabilities().isDlTdoaSupported()) {
startDlTDoASession(context);
}
}
};
rangingManager.registerCapabilitiesCallback(Executors.newSingleThreadExecutor(), capabilitiesCallback);
}
public void startDlTDoASession(Context context) {
RangingManager rangingManager = context.getSystemService(RangingManager.class);
// Create session and configure parameters
Executor executor = Executors.newSingleThreadExecutor();
RangingSession rangingSession = rangingManager.createRangingSession(executor, new RangingSessionCallback());
byte[] rangingRoundIndexes = new byte[] {0};
byte[] config = new byte[0]; // OOB config data
DlTdoaRangingParams params = DlTdoaRangingParams.createFromFiraConfigPacket(config, rangingRoundIndexes);
RangingDevice rangingDevice = new RangingDevice.Builder().build();
RawRangingDevice rawTagDevice = new RawRangingDevice.Builder()
.setRangingDevice(rangingDevice)
.setDlTdoaRangingParams(params)
.build();
RawDtTagRangingConfig dtTagConfig = new RawDtTagRangingConfig.Builder(rawTagDevice).build();
RangingPreference preference = new RangingPreference.Builder(DEVICE_ROLE_DT_TAG, dtTagConfig)
.setSessionConfig(new SessionConfig.Builder().build())
.build();
// Start the ranging session
rangingSession.start(preference);
}
private static class RangingSessionCallback implements RangingSession.Callback {
@Override
public void onDlTdoaResults(RangingDevice peer, DlTdoaMeasurement measurement) {
// Process measurement results here
}
}
}
带外 (OOB) 配置
以下代码段提供了 Wi-Fi 和 BLE 的 DL-TDoA OOB 配置数据示例:
Java
// Wifi Configuration
byte[] wifiConfig = {
(byte) 0xDD, (byte) 0x2D, (byte) 0x5A, (byte) 0x18, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
// BLE Configuration
byte[] bleConfig = {
(byte) 0x2D, (byte) 0x16, (byte) 0xF4, (byte) 0xFF, // Header
(byte) 0x5F, (byte) 0x19, // FiRa Sub-Element
(byte) 0x02, (byte) 0x00, // Profile ID
(byte) 0x06, (byte) 0x02, (byte) 0x20, (byte) 0x08, // MAC Address
(byte) 0x14, (byte) 0x01, (byte) 0x0C, // Preamble Index
(byte) 0x27, (byte) 0x02, (byte) 0x08, (byte) 0x07, // Vendor ID
(byte) 0x28, (byte) 0x06, (byte) 0xCA, (byte) 0xC8, (byte) 0xA6, (byte) 0xF7, (byte) 0x6F, (byte) 0x08, // Static STS IV
(byte) 0x08, (byte) 0x02, (byte) 0x60, (byte) 0x09, // Slot Duration
(byte) 0x1B, (byte) 0x01, (byte) 0x0A, // Slots per RR
(byte) 0x09, (byte) 0x04, (byte) 0xE8, (byte) 0x03, (byte) 0x00, (byte) 0x00, // Duration
(byte) 0x9F, (byte) 0x04, (byte) 0x67, (byte) 0x45, (byte) 0x23, (byte) 0x01 // Session ID
};
如果您因缺少 OOB 配置而无法使用该配置,或者需要更改 OOB 配置中没有的默认值,则可以使用 DlTdoaRangingParams.Builder 构建参数,如以下代码段所示。您可以使用以下参数代替 DlTdoaRangingParams.createFromFiraConfigPacket():
Kotlin
val dlTdoaParams = DlTdoaRangingParams.Builder(1)
.setComplexChannel(UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(byteArrayOf(0x01, 0x02, 0x03, 0x04))
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(byteArrayOf(0x01, 0x05))
.build()
Java
DlTdoaRangingParams dlTdoaParams = new DlTdoaRangingParams.Builder(1)
.setComplexChannel(new UwbComplexChannel.Builder()
.setChannel(9).setPreambleIndex(10).build())
.setDeviceAddress(deviceAddress)
.setSessionKeyInfo(new byte[]{0x01, 0x02, 0x03, 0x04})
.setRangingIntervalMillis(240)
.setSlotDuration(UwbRangingParams.DURATION_2_MS)
.setSlotsPerRangingRound(20)
.setRangingRoundIndexes(new byte[]{0x01, 0x05})
.build();