الميزات وواجهات برمجة التطبيقات

يقدّم Android 17 ميزات وواجهات برمجة تطبيقات جديدة ورائعة للمطوّرين. تلخّص الأقسام التالية هذه الميزات لمساعدتك في البدء باستخدام واجهات برمجة التطبيقات ذات الصلة.

للحصول على قائمة مفصّلة بواجهات برمجة التطبيقات الجديدة والمعدَّلة والمُزالة، يُرجى قراءة تقرير مقارنة واجهات برمجة التطبيقات. للحصول على تفاصيل حول واجهات برمجة التطبيقات الجديدة، يُرجى الانتقال إلى مرجع واجهة برمجة تطبيقات Android. يتم تمييز واجهات برمجة التطبيقات الجديدة لتسهيل رؤيتها.

عليك أيضًا مراجعة المجالات التي قد تؤثر فيها تغييرات النظام الأساسي في تطبيقاتك. لمزيد من المعلومات، يُرجى الاطّلاع على الصفحات التالية:

الوظيفة الأساسية

يضيف Android 17 الميزات الجديدة التالية المتعلقة بالوظيفة الأساسية لنظام Android.

عوامل التشغيل الجديدة في ProfilingManager

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

新触发器包括:

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

应用异常的性能分析触发器

Android 17 引入了一项设备端异常检测服务,用于监控资源密集型行为和潜在的兼容性回归。此服务与ProfilingManager集成,可让您的应用接收由特定系统检测到的事件触发的性能分析工件。

使用 TRIGGER_TYPE_ANOMALY 触发器检测系统性能问题 例如 binder 调用过多和内存用量过高。当应用违反操作系统定义的内存限制时,异常触发器允许开发者接收特定于应用的堆转储,以帮助识别和修复内存问题。此外,对于 binder 垃圾内容过多,异常触发器会提供有关 binder 事务的堆栈抽样分析报告。

此 API 回调发生在系统强制执行任何操作之前。例如,它可以帮助开发者在应用因超出内存限制而被系统终止之前收集调试数据。

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

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

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

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

تقليل عمليات قفل التنشيط باستخدام ميزة متتبِّع المنبّهات التي يتم تفعيلها أثناء وضع غير مستخدَم من قِبل أي برنامج حاليًا

يقدّم نظام التشغيل Android 17 نوعًا جديدًا من AlarmManager.setExactAndAllowWhileIdle يقبل OnAlarmListener بدلاً من PendingIntent. تُعد الآلية الجديدة المستندة إلى معاودة الاتصال مثالية للتطبيقات التي تعتمد حاليًا على عمليات قفل التنشيط المستمرة لتنفيذ مهام دورية، مثل تطبيقات المراسلة التي تحافظ على اتصالات المقبس.

الخصوصية

يتضمّن Android 17 الميزات الجديدة التالية لتحسين خصوصية المستخدم.

توافُق النظام الأساسي مع 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

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

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

الأمان

يضيف Android 17 الميزات الجديدة التالية لتحسين أمان الجهاز والتطبيق.

وضع "الحماية المتقدّمة على Android"‏ (AAPM)

يوفّر "وضع الحماية المتقدّمة" على Android مجموعة جديدة وفعّالة من ميزات الأمان لمستخدمي Android، ما يمثّل خطوة مهمة في حماية المستخدمين، لا سيما المعرّضين لخطر أكبر، من الهجمات المتطورة. تم تصميم "إدارة التطبيقات تلقائيًا" كميزة اختيارية، ويتم تفعيلها من خلال إعداد واحد يمكن للمستخدمين تفعيله في أي وقت لتطبيق مجموعة من إجراءات الحماية الأمنية.

تشمل هذه الإعدادات الأساسية حظر تثبيت التطبيقات من مصادر غير معروفة (التثبيت الجانبي) وتقييد إشارات بيانات USB وفرض عمليات الفحص التي تجريها خدمة "Google Play للحماية"، ما يقلّل بشكل كبير من مساحة سطح الاختراق على الجهاز. يمكن للمطوّرين الاستفادة من هذه الميزة باستخدام واجهة برمجة التطبيقات AdvancedProtectionManager لرصد حالة الوضع، ما يتيح للتطبيقات اعتماد وضع أمان محسّن تلقائيًا أو حظر الوظائف العالية الخطورة عندما يوافق المستخدم على تفعيل الوضع.

توقيع حزمة APK باستخدام تقنية PQC

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

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

对开发者的影响

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

إمكانية الاتصال

يضيف Android 17 الميزات التالية لتحسين إمكانية اتصال الجهاز والتطبيق.

شبكات الأقمار الصناعية المقيّدة

تتضمّن هذه السمة تحسينات تتيح للتطبيقات العمل بفعالية على شبكات الأقمار الصناعية ذات معدل نقل البيانات المنخفض.

تجربة المستخدم وواجهة مستخدم النظام

يتضمّن Android 17 التغييرات التالية لتحسين تجربة المستخدم.

مصدر صوت مخصّص لمستوى صوت "مساعد Google"

يقدّم Android 17 مصدر صوت مخصّصًا لمستوى صوت "مساعد Google" لتطبيقات "مساعد Google"، وذلك لتشغيله باستخدام USAGE_ASSISTANT. يؤدي هذا التغيير إلى فصل صوت "مساعد Google" عن مصدر الوسائط العادي، ما يمنح المستخدمين تحكّمًا منفصلاً في كلا مستوىَي الصوت. يتيح ذلك سيناريوهات مثل كتم صوت تشغيل الوسائط مع الحفاظ على إمكانية سماع ردود "مساعد Google"، والعكس.

يمكن لتطبيقات المساعد التي يمكنها الوصول إلى وضع الصوت الجديد MODE_ASSISTANT_CONVERSATION audio تحسين اتساق التحكّم في مستوى الصوت بشكل أكبر. يمكن لتطبيقات "مساعد Google" استخدام هذا الوضع لتقديم تلميح إلى النظام بشأن جلسة نشطة في "مساعد Google"، ما يضمن إمكانية التحكّم في مصدر صوت "مساعد Google" خارج تشغيل USAGE_ASSISTANT النشط أو باستخدام الأجهزة الطرفية المتصلة عبر البلوتوث.

التسليم (Handoff)

‫Handoff هي ميزة وواجهة برمجة تطبيقات جديدة ستتوفّر في Android 17، ويمكن لمطوّري التطبيقات دمجها لتوفير تجربة متواصلة للمستخدمين على جميع الأجهزة. تتيح هذه الميزة للمستخدم بدء نشاط تطبيق على أحد أجهزة Android ونقله إلى جهاز Android آخر. تعمل ميزة &quot;نقل النشاط&quot; في خلفية جهاز المستخدم، وتعرض الأنشطة المتاحة من أجهزة المستخدم الأخرى القريبة من خلال نقاط دخول مختلفة، مثل مشغّل التطبيقات وشريط المهام، على الجهاز المستلِم.

يمكن للتطبيقات تحديد ميزة &quot;نقل البيانات&quot; لتشغيل تطبيق Android الأصلي نفسه، إذا كان مثبّتًا ومتوفّرًا على الجهاز المستلِم. في مسار التنقّل من تطبيق إلى تطبيق، يتم توجيه المستخدم إلى النشاط المحدّد من خلال رابط لصفحة في التطبيق. يمكن بدلاً من ذلك توفير ميزة "التسليم من التطبيق إلى الويب" كخيار احتياطي أو تنفيذها مباشرةً باستخدام ميزة "التسليم من عنوان URL".

يتم تنفيذ ميزة Handoff على أساس كل نشاط. لتفعيل ميزة "التسليم"، استدعِ طريقة setHandoffEnabled() للنشاط. قد يلزم تمرير بيانات إضافية مع عملية التسليم حتى يتمكّن النشاط الذي تم إنشاؤه من جديد على الجهاز المستلِم من استعادة الحالة المناسبة. نفِّذ onHandoffActivityDataRequested() الدالة التنفيذية لعرض عنصر HandoffActivityData يحتوي على تفاصيل تحدّد كيفية تعامل ميزة "التسليم" مع النشاط وإعادة إنشائه على الجهاز المستلِم.

تحديث مباشر: واجهة برمجة التطبيقات للألوان الدلالية

في نظام التشغيل Android 17، تطلق ميزة التحديث المباشر واجهات برمجة التطبيقات Semantic Coloring لدعم الألوان ذات المعنى العالمي.

تتيح الفئات التالية التلوين الدلالي:

ألعاب التلوين

  • الأخضر: يرتبط بالأمان. يجب استخدام هذا اللون في الحالات التي تشير إلى أنّك في وضع آمن.
  • اللون البرتقالي: يُستخدم للإشارة إلى الحذر ولتمييز المخاطر المادية. يجب استخدام هذا اللون في الحالات التي يحتاج فيها المستخدمون إلى الانتباه لضبط إعدادات حماية أفضل.
  • أحمر: يشير عادةً إلى الخطر أو التوقف. يجب أن يتم عرضها في الحالات التي تتطلّب جذب انتباه المستخدمين بشكل عاجل.
  • الأزرق: لون محايد للمحتوى الذي يقدّم معلومات ويجب أن يبرز عن المحتوى الآخر.

يوضّح المثال التالي كيفية تطبيق أنماط دلالية على النص في إشعار:

  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 لنظام 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.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();