Tính năng và API

Android 17 mang đến cho nhà phát triển các tính năng và API mới tuyệt vời. Các phần sau đây tóm tắt những tính năng này để giúp bạn làm quen với các API liên quan.

Để biết danh sách chi tiết về các API mới, đã được sửa đổi và đã bị xoá, hãy đọc báo cáo điểm khác biệt về API. Để biết thông tin chi tiết về các API mới, hãy truy cập vào tài liệu tham khảo API cho Android (các API mới được trình bày nổi bật).

Bạn cũng nên xem xét những khía cạnh mà các thay đổi của nền tảng có thể ảnh hưởng đến ứng dụng của bạn. Để biết thêm thông tin, hãy xem các trang sau:

Chức năng cốt lõi

Android 17 bổ sung các tính năng mới sau đây liên quan đến chức năng cốt lõi của Android.

Điều kiện kích hoạt ProfilingManager mới

Android 17 向 ProfilingManager 添加了多个新的系统触发器,可帮助您收集深入的数据来调试性能问题。

新增的触发条件包括:

如需了解如何设置系统触发器,请参阅有关基于触发器的分析以及如何检索和分析分析数据的文档

API JobDebugInfo

Android 17 引入了新的 JobDebugInfo API,可帮助开发者调试 其 JobScheduler 作业,包括作业为何未运行、运行了多长时间以及 其他汇总信息。

扩展后的 JobDebugInfo API 的第一个方法是 getPendingJobReasonStats(),该方法会返回一个映射,其中包含作业处于 待执行状态的原因及其各自的累计待执行 时长。此方法将 getPendingJobReasonsHistory()getPendingJobReasons() 方法结合在一起,让您能够了解预定 作业为何未按预期运行,但通过在单个方法中提供时长和作业原因,简化了信息检索。

例如,对于指定的 jobId,该方法可能会返回 PENDING_JOB_REASON_CONSTRAINT_CHARGING 和 60000 毫秒的时长,表明 作业因未满足充电限制而处于待执行状态 60000 毫秒。

Giảm số lượng khoá đánh thức bằng tính năng hỗ trợ trình nghe cho các báo thức cho phép ở trạng thái rảnh

Android 17 引入了 AlarmManager.setExactAndAllowWhileIdle 的新变体,该变体 接受 OnAlarmListener 而不是 PendingIntent。这种基于回调的新机制非常适合目前依赖于连续唤醒锁来执行定期任务的应用,例如维护套接字连接的消息传递应用。

Quyền riêng tư

Android 17 có các tính năng mới sau đây để cải thiện quyền riêng tư của người dùng.

Trình chọn người liên hệ trên Android

Android 联系人选择工具是一个标准化的可浏览界面,供用户与您的应用分享联系人。该选择工具适用于搭载 Android 17(API 级别 37)或更高版本的设备,可作为 READ_CONTACTS 权限的可保护隐私的替代方案。您的应用无需请求访问用户的整个地址簿,而是指定所需的数据字段(例如电话号码或电子邮件地址),然后用户选择要分享的特定联系人。这样,您的应用便只能读取所选数据,从而确保精细控制,同时提供一致的用户体验,并具有内置搜索、个人资料切换和多选功能,而无需构建或维护界面。

如需了解详情,请参阅联系人选择器文档

Bảo mật

Android 17 bổ sung các tính năng mới sau đây để cải thiện tính bảo mật của thiết bị và ứng dụng.

Chế độ Bảo vệ nâng cao trên Android (AAPM)

Android 高级保护模式为 Android 用户提供了一套强大的新安全功能,标志着在保护用户(尤其是面临较高风险的用户)免遭复杂攻击方面迈出了重要一步。AAPM 是一项选择启用功能,只需进行一项配置设置即可激活。用户可以随时启用该功能,以应用一套主观的安全保护措施。

这些核心配置包括:禁止安装未知来源的应用(旁加载)、限制 USB 数据信号传输,以及强制执行 Google Play 保护机制扫描,从而显著减小设备的攻击面。 开发者可以使用 AdvancedProtectionManager API 与此功能集成,以检测模式的状态,从而使应用能够在用户选择启用此模式时自动采用强化型安全姿态或限制高风险功能。

Ký APK bằng PQC

Android 现在支持混合 APK 签名方案,以保护应用的签名身份免受利用量子计算的潜在攻击威胁。此功能引入了一种新的 APK 签名方案,可让您将经典签名密钥(例如 RSA 或 EC)与新的后量子密码 (PQC) 算法 (ML-DSA) 配对。

这种混合方法可确保您的应用在未来免受量子攻击,同时与依赖经典签名验证的旧版 Android 版本和设备保持完全向后兼容。

对开发者的影响

  • 使用 Play 应用签名的应用 :如果您使用 Play 应用签名,可以等待 Google Play 为您提供使用 Google Play 生成的 PQC 密钥升级混合签名的选项,确保您的应用受到保护,而无需手动管理密钥。
  • 使用自行管理的密钥的应用 :自行管理签名密钥的开发者可以利用更新后的 Android build 工具(例如 apksigner)轮替到混合身份,将 PQC 密钥与新的经典密钥相结合。(您必须创建新的经典密钥,无法重复使用旧密钥。)

Khả năng kết nối

Android 17 bổ sung các tính năng sau để cải thiện khả năng kết nối của thiết bị và ứng dụng.

Mạng vệ tinh bị hạn chế

实现优化,使应用能够在低带宽卫星网络上有效运行。

Trải nghiệm người dùng và giao diện người dùng hệ thống

Android 17 có những thay đổi sau đây để cải thiện trải nghiệm người dùng.

Luồng âm lượng riêng cho Trợ lý

Android 17 针对 Google 助理应用引入了专用的 Google 助理音量音频流,以便使用 USAGE_ASSISTANT 进行播放。此项变更将 Google 助理音频与标准媒体音频流分离,让用户可以单独控制这两个音频流的音量。这样一来,您就可以实现以下场景:在将媒体播放静音的同时,保持 Google 助理回答的可听性,反之亦然。

有权访问新的 MODE_ASSISTANT_CONVERSATION 音频模式的助理应用可以进一步提高音量控制的一致性。助理应用可以使用此模式向系统提供有关有效助理会话的提示,确保可以在有效 USAGE_ASSISTANT 播放之外或通过连接的蓝牙外围设备控制助理流。

Handoff

切换是 Android 17 中新增的一项功能和 API,应用开发者可以将其集成到应用中,以便为用户提供跨设备连续性。它允许用户在一个 Android 设备上启动应用 activity,然后将其转移到另一个 Android 设备。Handoff 在用户设备的后台运行,并通过各种入口点(例如接收设备上的启动器和任务栏)显示用户附近其他设备上的可用活动。

应用可以指定 Handoff 来启动相同的原生 Android 应用(如果该应用已安装在接收设备上且可供使用)。在此应用到应用流程中,用户通过深层链接跳转到指定 activity。或者,应用到网站切换功能可以作为后备选项提供,也可以通过网址切换功能直接实现。

切换支持是按 activity 实现的。如需启用 Handoff,请为 activity 调用 setHandoffEnabled() 方法。可能需要随切换传递额外数据,以便接收设备上重新创建的 activity 可以恢复适当的状态。实现 onHandoffActivityRequested() 回调以返回 HandoffActivityData 对象,该对象包含用于指定 Handoff 应如何处理并在接收设备上重新创建 activity 的详细信息。

Thông tin cập nhật trực tiếp – API màu sắc theo ngữ nghĩa

在 Android 17 中,实时更新启动了语义着色 API,以支持具有普遍意义的颜色。

以下类支持语义着色:

填色游戏

  • 绿色:与安全相关。 此颜色应在以下情况下使用:让别人知道您处于安全状态。
  • 橙色:用于表示警告和标记物理危险。当用户需要注意设置更好的保护设置时,应使用此颜色。
  • 红色:通常表示危险,停止。它应在需要紧急引起人们注意的情况下呈现。
  • 蓝色:中性颜色,适用于信息性内容,应与其他内容区分开来。

以下示例展示了如何将语义样式应用于通知中的文本:

  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 cho Android 17

下行链路到达时间差 (DL-TDoA) 测距技术可让设备通过测量信号的相对到达时间来确定其相对于多个锚点的位置。

以下代码段演示了如何初始化 Ranging Manager、验证设备功能并启动 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.CapabilitiesCallback {
            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 = intArrayOf(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.CapabilitiesCallback() {
            @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());
        int[] rangingRoundIndexes = new int[] {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();