Android 17 bietet viele neue Funktionen und APIs für Entwickler. In den folgenden Abschnitten werden diese Funktionen zusammengefasst, um Ihnen den Einstieg in die zugehörigen APIs zu erleichtern.
Eine detaillierte Liste der neuen, geänderten und entfernten APIs finden Sie im Bericht zu API-Unterschieden. Details zu neuen APIs finden Sie in der Android API-Refer1enz. Neue APIs sind zur besseren Sichtbarkeit hervorgehoben.
Sie sollten sich auch die Bereiche ansehen, in denen sich Plattformänderungen auf Ihre Apps auswirken können. Weitere Informationen finden Sie auf den folgenden Seiten:
- Verhaltensänderungen, die sich auf Apps auswirken, wenn sie auf Android 17 ausgerichtet sind
- Verhaltensänderungen, die sich unabhängig von
targetSdkVersionauf alle Apps auswirken
Hauptfunktion
Android 17 bietet die folgenden neuen Funktionen im Zusammenhang mit der Android-Kernfunktionalität.
Neue ProfilingManager-Trigger
In Android 17 wurden ProfilingManager mehrere neue Systemtrigger hinzugefügt, mit denen Sie
detaillierte Daten zur Behebung von Leistungsproblemen erfassen können.
Die neuen Trigger sind:
TRIGGER_TYPE_COLD_START: Der Trigger wird beim Kaltstart der App ausgelöst. Die Antwort enthält sowohl ein Aufrufstapelmuster als auch ein System-Trace.TRIGGER_TYPE_OOM: Der Trigger wird ausgelöst, wenn eine App einenOutOfMemoryErrorauslöst, und die Antwort enthält einen Java-Heap-Dump.TRIGGER_TYPE_KILL_EXCESSIVE_CPU_USAGE: Der Trigger wird ausgelöst, wenn eine App aufgrund einer ungewöhnlichen und übermäßigen CPU-Auslastung beendet wird, und die Antwort enthält ein Aufrufstapelmuster.TRIGGER_TYPE_ANOMALY: Hiermit können Sie Anomalien bei der Systemleistung erkennen, z. B. übermäßige Binder-Aufrufe und übermäßige Arbeitsspeichernutzung.
Informationen zum Einrichten des Systemtriggers finden Sie in der Dokumentation zu triggerbasierter Profilerstellung und zum Abrufen und Analysieren von Profilerstellungsdaten Dokumentation.
Profilerstellungstrigger für App-Anomalien
In Android 17 wurde ein On-Device-Dienst zur Erkennung von Anomalien eingeführt, der ressourcenintensive Verhaltensweisen und potenzielle Kompatibilitätsregressionen überwacht. Dieser Dienst ist in ProfilingManager integriert
und ermöglicht es Ihrer App, Profilerstellungs
artefakte zu empfangen, die durch bestimmte vom System erkannte Ereignisse ausgelöst werden.
Verwenden Sie den TRIGGER_TYPE_ANOMALY Trigger, um Leistungsprobleme des Systems zu erkennen, z. B. übermäßige Binder-Aufrufe und übermäßige Arbeitsspeichernutzung. Wenn eine App die vom Betriebssystem festgelegten Speicherlimits überschreitet, können Entwickler mit dem Anomalie-Trigger appspezifische Heap-Dumps erhalten, um Speicherprobleme zu identifizieren und zu beheben. Außerdem liefert der Anomalie-Trigger bei übermäßigem Binder-Spam ein Sampling-Profil für Binder-Transaktionen.
Dieser API-Callback erfolgt vor allen vom System auferlegten Maßnahmen. So können Entwickler beispielsweise Debug-Daten erfassen, bevor die App vom System beendet wird, weil sie die Speicherlimits überschritten hat.
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 APIs
Android 17 引入了新的 JobDebugInfo API,可帮助开发者调试其 JobScheduler 作业,了解作业未运行的原因、运行时长以及其他汇总信息。
扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于待执行状态的原因及其各自的累计待执行时长。此方法将 getPendingJobReasonsHistory() 和 getPendingJobReasons() 方法联接在一起,可让您了解预定作业未按预期运行的原因,但通过在单个方法中同时提供时长和作业原因,简化了信息检索。
例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表示作业因未满足充电约束而处于等待状态 60000 毫秒。
Weniger Wake Locks mit Listener-Unterstützung für Alarme vom Typ „allow-while-idle“
In Android 17 wird eine neue Variante von AlarmManager.setExactAndAllowWhileIdle eingeführt, die anstelle von PendingIntent ein OnAlarmListener akzeptiert. Dieser neue Callback-basierte Mechanismus eignet sich ideal für Apps, die derzeit auf kontinuierliche Wakelocks angewiesen sind, um regelmäßige Aufgaben auszuführen, z. B. Messaging-Apps, die Socket-Verbindungen aufrechterhalten.
Datenschutz
Android 17 enthält die folgenden neuen Funktionen, um den Datenschutz für Nutzer zu verbessern.
Plattformunterstützung für Encrypted Client 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-Kontaktauswahl
Android 联系人选择工具是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择工具适用于搭载 Android 17(API 级别 37)或更高版本的设备,可提供一种可保护隐私的替代方案,以取代广泛的 READ_CONTACTS 权限。您的应用无需请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这样,您的应用便只能读取所选数据,从而确保精细控制,同时提供一致的用户体验,并具有内置搜索、个人资料切换和多选功能,而无需构建或维护界面。
如需了解详情,请参阅联系人选择工具文档。
Sicherheit
Android 17 bietet die folgenden neuen Funktionen, um die Sicherheit von Geräten und Apps zu verbessern.
Erweiterter Sicherheitsprogramm-Modus für Android (Android Advanced Protection Mode, AAPM)
Der erweiterte Sicherheitsmodus für Android bietet Android-Nutzern eine Reihe leistungsstarker neuer Sicherheitsfunktionen. Er ist ein wichtiger Schritt, um Nutzer – insbesondere solche mit einem höheren Risiko – vor ausgeklügelten Angriffen zu schützen. AAPM ist als Opt-in-Funktion konzipiert und wird mit einer einzigen Konfigurationseinstellung aktiviert, die Nutzer jederzeit aktivieren können, um eine vordefinierte Reihe von Sicherheitsmaßnahmen anzuwenden.
Zu diesen Kernkonfigurationen gehören das Blockieren der App-Installation aus unbekannten Quellen (Sideloading), das Einschränken der USB-Datensignalisierung und das Erzwingen von Google Play Protect-Scans. Dadurch wird die Angriffsfläche des Geräts erheblich verringert.
Entwickler können diese Funktion über die AdvancedProtectionManager API einbinden, um den Status des Modus zu erkennen. So können Anwendungen automatisch eine verstärkte Sicherheitskonfiguration annehmen oder risikoreiche Funktionen einschränken, wenn ein Nutzer sich dafür entschieden hat.
PQC-APK-Signierung
Android 现在支持混合 APK 签名方案,以保护应用的签名身份免受利用量子计算的攻击的潜在威胁。此功能引入了一种新的 APK 签名方案,可让您将经典签名密钥(例如 RSA 或 EC)与新的后量子加密 (PQC) 算法 (ML-DSA) 配对。
这种混合方法可确保您的应用在未来免受量子攻击,同时与依赖于经典签名验证的旧版 Android 和设备保持完全的向后兼容性。
对开发者的影响
- 使用 Play 应用签名的应用:如果您使用 Play 应用签名,可以等待 Google Play 为您提供使用 Google Play 生成的 PQC 密钥升级混合签名的选项,从而确保您的应用受到保护,而无需手动管理密钥。
- 使用自行管理的密钥的应用:自行管理签名密钥的开发者可以利用更新后的 Android build 工具(例如 apksigner)轮换到混合身份,将 PQC 密钥与新的经典密钥相结合。(您必须创建新的经典密钥,无法重复使用旧密钥。)
Konnektivität
Android 17 bietet die folgenden Funktionen, um die Konnektivität von Geräten und Apps zu verbessern.
Eingeschränkte Satellitennetzwerke
实现优化,使应用能够在低带宽卫星网络上有效运行。
Nutzererfahrung und System-UI
Android 17 enthält die folgenden Änderungen, um die Nutzererfahrung zu verbessern.
Dedizierter Lautstärkestream für Assistant
Android 17 为 Google 助理应用引入了专用的 Google 助理音量流,
以便使用 USAGE_ASSISTANT 进行播放。此项更改将 Google 助理音频与标准媒体流分离,让用户可以单独控制这两个音量。这样便可实现以下场景:将媒体播放静音,同时保持 Google 助理响应的可听性,反之亦然。
有权访问新的 MODE_ASSISTANT_CONVERSATION 音频模式的 Google 助理应用可以进一步提高音量控制的一致性。Google 助理应用可以使用此模式向系统提供有关活跃 Google 助理会话的提示,确保可以在活跃 USAGE_ASSISTANT 播放之外或使用连接的蓝牙外设控制 Google 助理流。
Handoff
Handoff ist eine neue Funktion und API, die mit Android 17 eingeführt wird und die App-Entwickler in ihre Apps einbinden können, um ihren Nutzern eine geräteübergreifende Kontinuität zu bieten. Damit können Nutzer eine App-Aktivität auf einem Android-Gerät starten und auf ein anderes Android-Gerät übertragen. Handoff wird im Hintergrund des Geräts eines Nutzers ausgeführt und zeigt verfügbare Aktivitäten von den anderen Geräten des Nutzers in der Nähe über verschiedene Einstiegspunkte auf dem empfangenden Gerät an, z. B. über den Launcher und die Taskleiste.
Apps können Handoff so konfigurieren, dass dieselbe native Android-App gestartet wird, wenn sie auf dem empfangenden Gerät installiert und verfügbar ist. In diesem App-zu-App-Ablauf wird der Nutzer per Deeplink zur entsprechenden Aktivität weitergeleitet. Alternativ kann App-zu-Web-Handoff als Fallback-Option angeboten oder direkt mit URL-Handoff implementiert werden.
Die Handoff-Unterstützung wird pro Aktivität implementiert. Um Handoff zu aktivieren, rufen Sie
die
setHandoffEnabled()
Methode für die Aktivität auf. Möglicherweise müssen zusätzliche Daten mit der Übergabe weitergegeben werden, damit der entsprechende Status der neu erstellten Aktivität auf dem empfangenden Gerät wiederhergestellt werden kann. Implementieren Sie den
onHandoffActivityDataRequested()
Callback, um ein
HandoffActivityData-Objekt zurückzugeben, das
Details dazu enthält, wie Handoff die
Aktivität auf dem empfangenden Gerät verarbeiten und neu erstellen soll.
Live-Update – Semantische Farb-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)
UWB-Downlink-TDoA-API für Android 17
借助下行链路到达时间差 (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();