التغييرات في السلوك: التطبيقات التي تستهدف الإصدار 15 Android أو الإصدارات الأحدث

كما هو الحال في الإصدارات السابقة، يتضمّن Android 15 تغييرات في السلوك قد تؤثر في تطبيقك. تنطبق تغييرات السلوك التالية حصريًا على التطبيقات التي تستهدف الإصدار 15 من نظام التشغيل Android أو الإصدارات الأحدث. إذا كان تطبيقك يستهدف الإصدار 15 من نظام التشغيل Android أو الإصدارات الأحدث، عليك تعديل تطبيقك ليتوافق مع هذه السلوكيات بشكل سليم، حيثما ينطبق ذلك.

احرص أيضًا على مراجعة قائمة التغييرات في السلوك التي تؤثر في جميع التطبيقات التي تعمل على Android 15 بغض النظر عن targetSdkVersion لتطبيقك.

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

يعدّل نظام التشغيل Android 15 العديد من الإمكانات الأساسية لنظام Android أو يوسّع نطاقها.

التغييرات على الخدمات التي تعمل في المقدّمة

我们将对 Android 15 中的前台服务进行以下更改。

数据同步前台服务超时行为

对于以 Android 15(API 级别 35)或更高版本为目标平台的应用,Android 15 为 dataSync 引入了新的超时行为。此行为也适用于新的 mediaProcessing 前台服务类型

系统允许应用的 dataSync 服务在 24 小时内共运行 6 小时,之后系统会调用正在运行的服务的 Service.onTimeout(int, int) 方法(在 Android 15 中引入)。此时,服务有几秒钟的时间来调用 Service.stopSelf()。调用 Service.onTimeout() 后,该服务将不再被视为前台服务。如果服务未调用 Service.stopSelf(),系统会抛出内部异常。系统会在 Logcat 中记录此异常,并显示以下消息:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"

为避免此行为变更出现问题,您可以执行以下一项或多项操作:

  1. 让您的服务实现新的 Service.onTimeout(int, int) 方法。当应用收到回调时,请务必在几秒钟内调用 stopSelf()。(如果您不立即停止应用,系统会生成故障。)
  2. 确保应用的 dataSync 服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。
  3. 仅在有直接用户互动时启动 dataSync 前台服务;由于服务启动时应用位于前台,因此您的服务在应用进入后台后有完整的 6 小时时间。
  4. 请使用替代 API,而不是使用 dataSync 前台服务。

如果您的应用的 dataSync 前台服务在过去 24 小时内运行了 6 小时,则您无法启动其他 dataSync 前台服务,除非用户已将您的应用切换到前台(这会重置计时器)。如果您尝试启动其他 dataSync 前台服务,系统会抛出 ForegroundServiceStartNotAllowedException,并显示类似“前台服务类型 dataSync 的时间限制已用尽”的错误消息。

测试

如需测试应用的行为,即使您的应用并非以 Android 15 为目标平台(只要该应用在 Android 15 设备上运行),您也可以启用数据同步超时。如需启用超时,请运行以下 adb 命令:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

您还可以调整超时期限,以便更轻松地测试应用在达到上限时的行为方式。如需设置新的超时期限,请运行以下 adb 命令:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

新的媒体处理前台服务类型

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

يسمح النظام بتشغيل خدمات mediaProcessing للتطبيق لمدة إجمالية تبلغ 6 ساعات خلال فترة 24 ساعة، وبعد ذلك يستدعي النظام أسلوب Service.onTimeout(int, int) للخدمة التي تعمل (تم تقديمه في Android 15). في هذه المرحلة، تتوفّر للخدمة بضع ثوانٍ للاتصال بالرقم Service.stopSelf(). إذا لم تُشغِّل الخدمة Service.stopSelf()، يُرسِل النظام استثناءً داخليًا. يتم تسجيل الاستثناء في Logcat مع الرسالة التالية:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"

لتجنُّب حدوث الاستثناء، يمكنك تنفيذ أحد الإجراءات التالية:

  1. اطلب من مقدّم الخدمة تنفيذ طريقة Service.onTimeout(int, int) الجديدة. عندما يتلقّى تطبيقك معاودة الاتصال، احرص على الاتصال بـ stopSelf() في غضون بضع ثوانٍ. (إذا لم توقف التطبيق على الفور، سيُنشئ النظام حالة تعطُّل.)
  2. تأكَّد من أنّ خدمات mediaProcessing في تطبيقك لا تعمل لأكثر من إجمالي 6 ساعات في أي فترة 24 ساعة (ما لم يتفاعل المستخدم مع التطبيق، يؤدي ذلك إلى إعادة ضبط الموقّت).
  3. لا تبدأ mediaProcessing الخدمات التي تعمل في المقدّمة إلا نتيجةً لتفاعل مباشر من العميل، لأنّ تطبيقك يكون في المقدّمة عند بدء الخدمة، ويكون لدى خدمتك ست ساعات كاملة بعد انتقال التطبيق إلى الخلفية.
  4. بدلاً من استخدام خدمة mediaProcessing تعمل في المقدّمة، استخدِم واجهة برمجة تطبيقات بديلة، مثل WorkManager.

إذا استمر تشغيل خدمات mediaProcessing التي تعمل في المقدّمة في تطبيقك لمدة 6 ساعات في آخر 24 ساعة، لا يمكنك بدء خدمة أخرى تعمل في المقدّمة mediaProcessing ما لم ينقل المستخدم تطبيقك إلى المقدّمة (ما يؤدي إلى إعادة ضبط الموقّت). إذا حاولت بدء خدمة "mediaProcessing" أخرى تعمل في المقدّمة، سيعرض النظام ForegroundServiceStartNotAllowedException رسالة خطأ مثل "تم استنفاد المهلة الزمنية لنوع الخدمة التي تعمل في المقدّمة mediaProcessing".

لمزيد من المعلومات عن نوع الخدمة mediaProcessing، يُرجى الاطّلاع على المقالة التغييرات التي طرأت على أنواع الخدمات التي تعمل في المقدّمة لنظام التشغيل Android 15: معالجة الوسائط.

الاختبار

لاختبار سلوك تطبيقك، يمكنك تفعيل مهلات معالجة الوسائط حتى إذا كان تطبيقك لا يستهدف الإصدار 15 من نظام التشغيل Android (ما دام التطبيق يعمل على جهاز يعمل بالإصدار 15 من نظام التشغيل Android). لتفعيل مهلات الانتظار، شغِّل الأمر adb التالي:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

يمكنك أيضًا تعديل فترة المهلة لتسهيل اختبار سلوك تطبيقك عند بلوغ الحدّ الأقصى. لضبط فترة مهلة جديدة، شغِّل adb الأمر التالي:

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

对启动前台服务的 BOOT_COMPLETED 广播接收器的限制

هناك قيود جديدة على إطلاق أجهزة استقبال بث BOOT_COMPLETED. والخدمات التي تعمل في المقدّمة. لا يُسمح لأجهزة استقبال BOOT_COMPLETED بتشغيل الأنواع التالية من الخدمات التي تعمل في المقدّمة:

إذا حاول مستقبل BOOT_COMPLETED بدء أيّ من هذه الأنواع من الخدمات التي تعمل في المقدّمة، يُرسِل النظام الخطأ ForegroundServiceStartNotAllowedException.

الاختبار

لاختبار سلوك تطبيقك، يمكنك تفعيل هذه القيود الجديدة حتى إذا كان تطبيقك لا يستهدف الإصدار 15 من Android (ما دام التطبيق يعمل على جهاز يعمل بالإصدار 15 من Android). شغِّل الأمر adb التالي:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

لإرسال بث BOOT_COMPLETED بدون إعادة تشغيل الجهاز، يُرجى اتّباع الخطوات التالية: شغِّل الأمر adb التالي:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name

在应用拥有 SYSTEM_ALERT_WINDOW 权限时启动前台服务的限制

في السابق، إذا كان التطبيق يمتلك إذن SYSTEM_ALERT_WINDOW، كان بإمكانه بدء خدمة تعمل في المقدّمة حتى إذا كان التطبيق قيد التشغيل في الخلفية (كما هو описан في الاستثناءات من القيود المفروضة على بدء التطبيقات في الخلفية).

إذا كان التطبيق يستهدف الإصدار 15 من نظام التشغيل Android، أصبح هذا الإعفاء الآن أكثر تقييدًا. يجب أن يحصل التطبيق الآن على إذن SYSTEM_ALERT_WINDOW وأيضًا أن يتضمّن نافذة ملف شخصي مثبّت مرئية. وهذا يعني أنّ التطبيق يجب أن يفتح أولاً نافذة TYPE_APPLICATION_OVERLAY و يجب أن تكون النافذة مرئية قبل بدء خدمة تعمل في المقدّمة.

إذا حاول تطبيقك بدء خدمة تعمل في المقدّمة من الخلفية بدون استيفاء هذه المتطلبات الجديدة (وليس لديه أي استثناء آخر)، يُرسِل النظام الخطأ ForegroundServiceStartNotAllowedException.

إذا كان تطبيقك يعلن عن إذن SYSTEM_ALERT_WINDOW ويشغّل الخدمات التي تعمل في المقدّمة من الخلفية، قد يتأثّر بالتغيير الذي تم إجراؤه. إذا حصل تطبيقك على ForegroundServiceStartNotAllowedException، تحقَّق من ترتيب عمليات تطبيقك وتأكَّد من أنّ تطبيقك لديه نافذة تراكب نشطة قبل أن يحاول بدء خدمة تعمل في المقدّمة من الخلفية. يمكنك التحقّق مما إذا كانت نافذة التراكب مرئية حاليًا من خلال استدعاء View.getWindowVisibility()، أو يمكنك إلغاء View.onWindowVisibilityChanged() للحصول على إشعارات عند تغيير مستوى العرض.

الاختبار

لاختبار سلوك تطبيقك، يمكنك تفعيل هذه القيود الجديدة حتى إذا كان تطبيقك لا يستهدف الإصدار 15 من نظام التشغيل Android (ما دام التطبيق يعمل على جهاز Android 15). لتفعيل هذه القيود الجديدة على بدء الخدمات التي تعمل في المقدّمة من الخلفية، شغِّل الأمر adb التالي:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

تغييرات على الأوقات التي يمكن فيها للتطبيقات تعديل الحالة العامة لوضع "عدم الإزعاج"

以 Android 15(API 级别 35)及更高版本为目标平台的应用无法再更改设备上的勿扰 (DND) 功能的全局状态或政策(无论是通过修改用户设置还是关闭勿扰模式)。相反,应用必须提供 AutomaticZenRule,系统会将其与现有的“最严格的政策优先”方案合并为一个全局政策。对之前会影响全局状态的现有 API 的调用(setInterruptionFiltersetNotificationPolicy)会导致创建或更新隐式 AutomaticZenRule,该 AutomaticZenRule 会根据这些 API 调用的调用周期开启和关闭。

请注意,只有当应用调用 setInterruptionFilter(INTERRUPTION_FILTER_ALL) 并希望该调用停用之前由其所有者激活的 AutomaticZenRule 时,此更改才会影响可观察到的行为。

التغييرات في واجهة برمجة تطبيقات OpenJDK

Android 15 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致。

以下变更可能会影响以 Android 15(API 级别 35)为目标平台的应用的兼容性:

  • 对字符串格式化 API 进行了更改:现在,使用以下 String.format()Formatter.format() API 时,对实参索引、标志、宽度和精度的验证要求变得更加严格:

    例如,当使用参数索引 0(格式字符串中的 %0)时,系统会抛出以下异常:

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    在这种情况下,可以使用实参索引 1(格式字符串中的 %1)来解决此问题。

  • Arrays.asList(...).toArray() 的组件类型的更改:使用 Arrays.asList(...).toArray() 时,所得数组的组件类型现在是 Object,而不是底层数组元素的类型。因此,以下代码会抛出 ClassCastException

    String[] elements = (String[]) Arrays.asList("one", "two").toArray();
    

    在这种情况下,为了在生成的数组中保留 String 作为组件类型,您可以改用 Collection.toArray(Object[])

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • 语言代码处理方面的变化:使用 Locale API 时,希伯来语、意第绪语和印度尼西亚语的语言代码不再转换为其过时的形式(希伯来语:iw、意第绪语:ji 和印度尼西亚语:in)。指定这些语言区域的语言代码时,请改用 ISO 639-1 中的代码(希伯来语:he、意第绪语:yi 和印度尼西亚语:id)。

  • 对随机整数序列的更改:根据 https://bugs.openjdk.org/browse/JDK-8301574 中所做的更改,以下 Random.ints() 方法现在返回的数字序列与 Random.nextInt() 方法返回的数字序列不同:

    一般来说,此更改不应导致应用行为中断,但您的代码不应期望从 Random.ints() 方法生成的序列与 Random.nextInt() 相匹配。

新的 SequencedCollection API 可能会影响您应用的兼容性,具体取决于您是否在应用的 build 配置中更新 compileSdk 以使用 Android 15(API 级别 35)

  • kotlin-stdlib 中的 MutableList.removeFirst()MutableList.removeLast() 扩展函数发生冲突

    Java 中的 List 类型会映射到 Kotlin 中的 MutableList 类型。 由于 List.removeFirst()List.removeLast() API 已在 Android 15(API 级别 35)中引入,因此 Kotlin 编译器会将函数调用(例如 list.removeFirst())静态解析为新的 List API,而不是 kotlin-stdlib 中的扩展函数。

    如果应用重新编译时将 compileSdk 设置为 35,并将 minSdk 设置为 34 或更低值,然后在 Android 14 及更低版本上运行该应用,则会抛出运行时错误:

    java.lang.NoSuchMethodError: No virtual method
    removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;
    

    Android Gradle 插件中现有的 NewApi lint 选项可以捕获这些新的 API 用法。

    ./gradlew lint
    
    MainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi]
          list.removeFirst()
    

    为了修复运行时异常和 lint 错误,可以在 Kotlin 中将 removeFirst()removeLast() 函数调用分别替换为 removeAt(0)removeAt(list.lastIndex)。如果您使用的是 Android Studio Ladybug | 2024.1.3 或更高版本,它还会针对这些错误提供快速修复选项。

    如果已停用 lint 选项,请考虑移除 @SuppressLint("NewApi")lintOptions { disable 'NewApi' }

  • 与 Java 中的其他方法发生冲突

    现有类型中添加了新方法,例如 ListDeque。这些新方法可能与具有相同名称和实参类型的其他接口和类中的方法不兼容。如果方法签名发生不兼容的冲突,javac 编译器会输出 build 时错误。例如:

    错误示例 1:

    javac MyList.java
    
    MyList.java:135: error: removeLast() in MyList cannot implement removeLast() in List
      public void removeLast() {
                  ^
      return type void is not compatible with Object
      where E is a type-variable:
        E extends Object declared in interface List
    

    错误示例 2:

    javac MyList.java
    
    MyList.java:7: error: types Deque<Object> and List<Object> are incompatible;
    public class MyList implements  List<Object>, Deque<Object> {
      both define reversed(), but with unrelated return types
    1 error
    

    错误示例 3:

    javac MyList.java
    
    MyList.java:43: error: types List<E#1> and MyInterface<E#2> are incompatible;
    public static class MyList implements List<Object>, MyInterface<Object> {
      class MyList inherits unrelated defaults for getFirst() from types List and MyInterface
      where E#1,E#2 are type-variables:
        E#1 extends Object declared in interface List
        E#2 extends Object declared in interface MyInterface
    1 error
    

    如需修复这些 build 错误,实现这些接口的类应使用兼容的返回类型替换相应方法。例如:

    @Override
    public Object getFirst() {
        return List.super.getFirst();
    }
    

الأمان

يتضمّن Android 15 تغييرات تعزّز أمان النظام للمساعدة في حماية التطبيقات والمستخدمين من التطبيقات الضارة.

إصدارات TLS المحظورة

Android 15 限制了对 TLS 版本 1.0 和 1.1 的使用。这些版本之前已在 Android 中被弃用,但现在不允许面向 Android 15 的应用使用。

عمليات إطلاق الأنشطة الآمنة في الخلفية

Android 15 可保护用户免受恶意应用的侵害,并让用户更好地控制 来防止恶意后台应用 将其他应用置于前台、提升其权限以及滥用 用户互动自以下时间以来,后台活动启动一直受到限制 Android 10(API 级别 29)。

禁止与堆栈中的顶部 UID 不匹配的应用启动 activity

恶意应用可以在同一任务中启动另一个应用的 activity,然后 叠加在上面,营造出像该应用一样的错觉。这个“任务” 劫持"攻击绕过了当前的后台启动限制, 会发生在同一个可见任务中。为了降低这种风险,Android 15 新增了 用于阻止与堆栈中的顶层 UID 不匹配的应用启动的标志 活动。如需选择启用应用的所有活动,请更新 allowCrossUidActivitySwitchFromBelow 属性:AndroidManifest.xml

<application android:allowCrossUidActivitySwitchFromBelow="false" >

如果满足以下所有条件,则启用新的安全措施:

  • 执行启动的应用以 Android 15 为目标平台。
  • 任务堆栈顶部的应用以 Android 15 为目标平台。
  • 所有可见活动都已选择启用新保护措施

如果启用了安全措施,应用可能会返回主屏幕,而不是返回 最后一个可见应用(如果他们自行完成任务)。

其他变更

除了限制 UID 匹配之外,这些其他变更也 包括:

  • 更改 PendingIntent 创作者,以阻止后台活动启动,具体方法是: 默认。这有助于防止应用意外创建 可能被恶意操作者滥用的 PendingIntent
  • 请勿将应用调到前台,除非 PendingIntent 发送者 允许它。此变更旨在防止恶意应用滥用 在后台启动 activity 的功能。默认情况下,应用 允许将任务堆栈转到前台,除非创建者允许 后台活动启动权限或发送者有后台活动 启动权限
  • 控制任务堆栈的顶层 activity 完成其任务的方式。如果 顶层 activity 完成一项任务后,Android 会返回到之前执行的 上次活动时间。此外,如果非顶层 activity 完成其任务,Android 将 返回主屏幕;因此不会阻碍这个非顶层的 活动。
  • 防止将其他应用中的任意 activity 启动到您自己的 activity 任务。这项变更旨在防止恶意应用 看起来像是来自其他应用的活动
  • 禁止将不可见窗口视为后台活动 发布。这有助于防止恶意应用滥用后台 activity 来向用户显示不需要或恶意的内容。

نوايا أكثر أمانًا

Android 15 introduces new optional security measures to make intents safer and more robust. These changes are aimed at preventing potential vulnerabilities and misuse of intents that can be exploited by malicious apps. There are two main improvements to the security of intents in Android 15:

  • Match target intent-filters: Intents that target specific components must accurately match the target's intent-filter specifications. If you send an intent to launch another app's activity, the target intent component needs to align with the receiving activity's declared intent-filters.
  • Intents must have actions: Intents without an action will no longer match any intent-filters. This means that intents used to start activities or services must have a clearly defined action.

In order to check how your app responds to these changes, use StrictMode in your app. To see detailed logs about Intent usage violations, add the following method:

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}

Java

public void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
            .detectUnsafeIntentLaunch()
            .build());
}

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

يتضمّن Android 15 بعض التغييرات التي تهدف إلى توفير تجربة مستخدم أكثر اتساقًا وسهولة.

تغييرات في مساحة العرض داخل النافذة

هناك تغييران مرتبطان بزوايا النافذة في Android 15: يتم تطبيق التمويه من الحافة إلى الحافة تلقائيًا، وهناك أيضًا تغييرات في الإعدادات، مثل الإعدادات التلقائية لأشرطة النظام.

التنفيذ الشامل

تكون التطبيقات معروضة حتى حافة الشاشة تلقائيًا على الأجهزة التي تعمل بنظام التشغيل Android 15 إذا كان التطبيق يستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات).

تطبيق يستهدف الإصدار 14 من نظام التشغيل Android ولا يتم عرضه حتى حافة الشاشة على جهاز يعمل بالإصدار 15 من نظام التشغيل Android


تطبيق يستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) ويعرض المحتوى من الحافة إلى الحافة على جهاز Android 15. يستخدم هذا التطبيق في الغالب مكوّنات Material 3 Compose التي يتم تطبيقها تلقائيًا. لن تتأثر هذه الشاشة سلبًا بعملية فرض العرض حتى حافة الشاشة في Android 15.

هذا تغيير غير متوافق قد يؤثر سلبًا في واجهة مستخدم تطبيقك. تؤثّر التغييرات في مناطق واجهة المستخدم التالية:

  • شريط التنقّل باستخدام مقبض الإيماءات
    • تكون شفافة تلقائيًا.
    • يتم إيقاف الإزاحة السفلية، لذا يتم رسم المحتوى خلف شريط التنقّل في النظام ما لم يتم تطبيق هوامش.
    • تم إيقاف setNavigationBarColor وR.attr#navigationBarColor نهائيًا، ولا يؤثران في التنقّل بالإيماءات.
    • لن يكون setNavigationBarContrastEnforced وR.attr#navigationBarContrastEnforced أي تأثير على التنقّل بالإيماءات.
  • التنقّل باستخدام ثلاثة أزرار
    • يتم ضبط مستوى الشفافية على 80% تلقائيًا، وقد يتطابق اللون مع خلفية النافذة.
    • تم إيقاف الإزاحة السفلية كي يتم عرض المحتوى خلف شريط التنقّل في النظام ما لم يتم تطبيق الحواف الداخلية.
    • يتم ضبط setNavigationBarColor وR.attr#navigationBarColor تلقائيًا ليتطابقا مع خلفية النافذة. يجب أن تكون خلفية النافذة قابلة للرسم بلون حتى يتم تطبيق هذا الإعداد التلقائي. تم إيقاف هذه الواجهة، ولكنها لا تزال تؤثر في التنقّل باستخدام 3 أزرار.
    • يتم تلقائيًا تفعيل الخيار setNavigationBarContrastEnforced وR.attr#navigationBarContrastEnforced، ما يؤدي إلى إضافة خلفية غير شفافة بنسبة% 80 في وضع "التنقّل باستخدام ثلاثة أزرار".
  • شريط الحالة
    • تكون شفافة تلقائيًا.
    • يتم إيقاف الإزاحة العلوية، وبالتالي يتم عرض المحتوى خلف شريط الحالة ما لم يتم تطبيق هوامش داخلية.
    • تم إيقاف setStatusBarColor وR.attr#statusBarColor نهائيًا ولن يكون لهما أي تأثير في Android 15.
    • تم إيقاف setStatusBarContrastEnforced وR.attr#statusBarContrastEnforced نهائيًا، ولكن لا يزال لهما تأثير على Android 15.
  • الفتحة في الشاشة
    • يجب أن تكون قيمة layoutInDisplayCutoutMode للنوافذ غير العائمة LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. يتم تفسير SHORT_EDGES وNEVER وDEFAULT على أنّها ALWAYS كي لا يظهر للمستخدمين شريط أسود بسبب فتحة الشاشة، بل يظهر المحتوى من الحافة إلى الحافة.

يوضّح المثال التالي تطبيقًا قبل وبعد استهداف Android 15 (المستوى 35 لواجهة برمجة التطبيقات)، وقبل وبعد تطبيق العناصر المضمّنة.

تطبيق يستهدف الإصدار 14 من نظام التشغيل Android ولا يتم عرضه حتى حافة الشاشة على جهاز يعمل بالإصدار 15 من نظام التشغيل Android
تطبيق يستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) ويعرض المحتوى من الحافة إلى الحافة على جهاز Android 15. ومع ذلك، يتم الآن إخفاء العديد من العناصر بواسطة شريط الحالة أو شريط التنقّل الذي يتضمّن 3 أزرار أو فتحة الشاشة، وذلك بسبب عمليات فرض ميزة "العرض حتى حافة الشاشة" في Android 15. تتضمّن واجهة المستخدم المخفية شريط التطبيق العلوي في Material 2 وأزرار الإجراء العائم وعناصر القائمة.
تطبيق يستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات)، ويعمل على كامل الشاشة على جهاز Android 15 ويطبّق هوامش داخلية حتى لا يتم إخفاء واجهة المستخدم.
ما يجب التحقّق منه إذا كان تطبيقك معروضًا من الحافة إلى الحافة

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

  • لديك نافذة غير عائمة، مثل Activity التي تستخدم SHORT_EDGES أو NEVER أو DEFAULT بدلاً من LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. إذا كان تطبيقك يتعطّل عند تشغيله، قد يكون ذلك بسبب شاشة البداية. يمكنك إما ترقية تبعية شاشة البداية الأساسية إلى الإصدار 1.2.0-alpha01 أو إصدار أحدث، أو ضبط window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always.
  • قد تكون هناك شاشات ذات عدد زيارات أقل مع واجهة مستخدم محجوبة. تأكَّد من أنّ الشاشات الأقل زيارة لا تتضمّن واجهة مستخدم محجوبة. تشمل الشاشات التي تسجّل عددًا أقل من الزيارات ما يلي:
    • شاشات الإعداد أو تسجيل الدخول
    • صفحات الإعدادات
الإجراءات التي يجب اتّخاذها إذا لم يكن تطبيقك معروضًا من الحافة إلى الحافة

إذا لم يكن تطبيقك معروضًا من الحافة إلى الحافة، من المرجّح أن تتأثّر بذلك. بالإضافة إلى سيناريوهات التطبيقات التي تعرض المحتوى من الحافة إلى الحافة، يجب مراعاة ما يلي:

  • إذا كان تطبيقك يستخدم مكوّنات Material 3 ( androidx.compose.material3) في Compose، مثل TopAppBar وBottomAppBar وNavigationBar، من المحتمل ألا تتأثر هذه المكوّنات لأنّها تتعامل تلقائيًا مع الهوامش الداخلية.
  • إذا كان تطبيقك يستخدم مكوّنات Material 2 ( androidx.compose.material) في Compose، لن تتعامل هذه المكوّنات تلقائيًا مع المساحات الداخلية. ومع ذلك، يمكنك الوصول إلى الهوامش الداخلية وتطبيقها يدويًا. في androidx.compose.material الإصدار 1.6.0 والإصدارات الأحدث، استخدِم المَعلمة windowInsets لتطبيق الهوامش الداخلية يدويًا على BottomAppBar وTopAppBar وBottomNavigation وNavigationRail. وبالمثل، استخدِم المَعلمة contentWindowInsets مع Scaffold.
  • إذا كان تطبيقك يستخدم طرق العرض ومكوّنات Material (com.google.android.material)، فإنّ معظم مكوّنات Material المستندة إلى طرق العرض، مثل BottomNavigationView أو BottomAppBar أو NavigationRailView أو NavigationView، تتعامل مع الحواف الداخلية ولا تتطلّب أي عمل إضافي. ومع ذلك، عليك إضافة android:fitsSystemWindows="true" إذا كنت تستخدم AppBarLayout.
  • بالنسبة إلى العناصر القابلة للإنشاء المخصّصة، طبِّق الحواف الداخلية يدويًا كمسافة بادئة. إذا كان المحتوى الخاص بك ضمن Scaffold، يمكنك استخدام الحواف الداخلية باستخدام قيم المساحة المتروكة Scaffold. بخلاف ذلك، طبِّق الحشو باستخدام أحد WindowInsets.
  • إذا كان تطبيقك يستخدم طرق عرض وBottomSheet أو SideSheet أو حاويات مخصّصة، طبِّق مساحة متروكة باستخدام ViewCompat.setOnApplyWindowInsetsListener. بالنسبة إلى RecyclerView، طبِّق الحشو باستخدام أداة معالجة الأحداث هذه، وأضِف أيضًا clipToPadding="false".
التحقّق مما إذا كان تطبيقك يجب أن يوفّر ميزة الحماية المخصّصة في الخلفية

إذا كان تطبيقك يوفّر حماية مخصّصة في الخلفية لشريط التنقّل بثلاثة أزرار أو شريط الحالة، يجب أن يضع تطبيقك عنصرًا قابلاً للإنشاء أو عرضًا خلف شريط النظام باستخدام WindowInsets.Type#tappableElement() للحصول على ارتفاع شريط التنقّل بثلاثة أزرار أو WindowInsets.Type#statusBars.

مراجع إضافية حول العرض من الحافة إلى الحافة

راجِع الدليلَين طرق العرض من الحافة إلى الحافة وإنشاء محتوى من الحافة إلى الحافة باستخدام Compose للاطّلاع على اعتبارات إضافية بشأن تطبيق عمليات الإزاحة.

واجهات برمجة التطبيقات المتوقّفة نهائيًا

تم إيقاف واجهات برمجة التطبيقات التالية نهائيًا ولكن لم يتم إيقافها:

تم إيقاف واجهات برمجة التطبيقات التالية:

الإعدادات الثابتة

إذا كان تطبيقك يستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، لن يتم استبعاد أشرطة النظام بعد الآن.Configuration إذا كنت تستخدم حجم الشاشة في الفئة Configuration لاحتساب التنسيق، عليك استبداله ببدائل أفضل، مثل ViewGroup أو WindowInsets أو WindowMetricsCalculator المناسبة، حسب احتياجاتك.

تتوفّر Configuration منذ الإصدار 1 من واجهة برمجة التطبيقات. يتم الحصول عليها عادةً من Activity.onConfigurationChanged. ويوفّر معلومات مثل كثافة النافذة والاتجاه والأحجام. من الخصائص المهمة بشأن أحجام النوافذ التي يتم عرضها من خلال Configuration أنّها كانت تستبعد أشرطة النظام.

يتم عادةً استخدام حجم الإعدادات لاختيار الموارد، مثل /res/layout-h500dp، ولا يزال هذا الاستخدام صالحًا. ومع ذلك، لم يُنصح مطلقًا باستخدامها في حسابات التنسيق. في حال حدوث ذلك، عليك الابتعاد عن الجهاز الآن. يجب استبدال استخدام Configuration بشيء أكثر ملاءمة حسب حالة الاستخدام.

إذا كنت تستخدمها لاحتساب التنسيق، استخدِم ViewGroup مناسبًا، مثل CoordinatorLayout أو ConstraintLayout. إذا كنت تستخدمها لتحديد ارتفاع شريط التنقّل في النظام، استخدِم WindowInsets. إذا أردت معرفة حجم نافذة تطبيقك الحالي، استخدِم computeCurrentWindowMetrics.

توضّح القائمة التالية الحقول المتأثرة بهذا التغيير:

  • لم يعُد حجمَا Configuration.screenWidthDp وscreenHeightDp يستبعدان أشرطة النظام.
  • يتأثر Configuration.smallestScreenWidthDp بشكل غير مباشر بالتغييرات التي تطرأ على screenWidthDp وscreenHeightDp.
  • يتأثر Configuration.orientation بشكل غير مباشر بالتغييرات التي تطرأ على screenWidthDp وscreenHeightDp على الأجهزة القريبة من الشكل المربّع.
  • يتأثر Display.getSize(Point) بشكل غير مباشر بالتغييرات في Configuration. تم إيقافها نهائيًا بدءًا من المستوى 30 لواجهة برمجة التطبيقات.
  • كانت ميزة Display.getMetrics() تعمل بهذه الطريقة منذ المستوى 33 لواجهة برمجة التطبيقات.

تكون القيمة التلقائية لسمة elegantTextHeight هي true

For apps targeting Android 15 (API level 35), the elegantTextHeight TextView attribute becomes true by default, replacing the compact font used by default with some scripts that have large vertical metrics with one that is much more readable. The compact font was introduced to prevent breaking layouts; Android 13 (API level 33) prevents many of these breakages by allowing the text layout to stretch the vertical height utilizing the fallbackLineSpacing attribute.

In Android 15, the compact font still remains in the system, so your app can set elegantTextHeight to false to get the same behavior as before, but it is unlikely to be supported in upcoming releases. So, if your app supports the following scripts: Arabic, Lao, Myanmar, Tamil, Gujarati, Kannada, Malayalam, Odia, Telugu or Thai, test your app by setting elegantTextHeight to true.

elegantTextHeight behavior for apps targeting Android 14 (API level 34) and lower.
elegantTextHeight behavior for apps targeting Android 15.

تغيير عرض TextView لأشكال الحروف المعقّدة

在以前的 Android 版本中,某些具有复杂形状的手写字体或语言可能会在上一个或下一个字符的区域绘制字母。在某些情况下,此类字母会在开头或结尾处被剪裁。从 Android 15 开始,TextView 会分配宽度,以便为此类字母绘制足够的空间,并允许应用请求向左额外添加内边距以防止剪裁。

由于此更改会影响 TextView 确定宽度的方式,因此如果应用以 Android 15(API 级别 35)或更高版本为目标平台,TextView 会默认分配更多宽度。您可以通过对 TextView 调用 setUseBoundsForWidth API 来启用或停用此行为。

由于添加左内边距可能会导致现有布局未对齐,因此默认情况下不会添加内边距,即使以 Android 15 或更高版本为目标平台的应用也是如此。不过,您可以通过调用 setShiftDrawingOffsetForStartOverhang 添加额外的内边距以防止剪裁。

以下示例展示了这些更改如何改进某些字体和语言的文本布局。

采用手写体字体的英语文本的标准布局。部分字母被截断。对应的 XML 如下:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
相同英语文本的布局,增加了宽度和内边距。以下是相应的 XML:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
泰语文本的标准布局。部分字母被截断。 以下是相应的 XML:

<TextView
    android:text="คอมพิวเตอร์" />
相同泰语文本的布局,增加了宽度和内边距。以下是相应的 XML:

<TextView
    android:text="คอมพิวเตอร์"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />

ارتفاع السطر التلقائي المتوافق مع اللغة المحلية في EditText

In previous versions of Android, the text layout stretched the height of the text to meet the line height of the font that matched the current locale. For example, if the content was in Japanese, because the line height of the Japanese font is slightly larger than the one of a Latin font, the height of the text became slightly larger. However, despite these differences in line heights, the EditText element was sized uniformly, regardless of the locale being used, as illustrated in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText is the same, even though these languages have different line heights from each other.

For apps targeting Android 15 (API level 35), a minimum line height is now reserved for EditText to match the reference font for the specified Locale, as shown in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText now includes space to accommodate the default line height for these languages' fonts.

If needed, your app can restore the previous behavior by specifying the useLocalePreferredLineHeightForMinimum attribute to false, and your app can set custom minimum vertical metrics using the setMinimumFontMetrics API in Kotlin and Java.

الكاميرا والوسائط

يُجري Android 15 التغييرات التالية على سلوك الكاميرا والوسائط في التطبيقات التي تستهدف الإصدار 15 من Android أو الإصدارات الأحدث.

القيود المفروضة على طلب التركيز على الصوت

يجب أن تكون التطبيقات التي تستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات) هي التطبيق الأهم أو أن تعمل بخدمة في المقدّمة من أجل طلب تركيز الصوت. إذا حاول أحد التطبيقات طلب التركيز عندما لا يستوفي أحد هذه المتطلبات، يعرض الإجراء AUDIOFOCUS_REQUEST_FAILED.

يمكنك الاطّلاع على مزيد من المعلومات حول ميزة "تركيز الصوت" في مقالة إدارة ميزة "تركيز الصوت".

تعديل القيود المفروضة على استخدام واجهات برمجة التطبيقات غير التابعة لحزمة SDK

يتضمّن نظام التشغيل Android 15 قوائم معدَّلة لواجهات برمجة التطبيقات غير التابعة لحزمة SDK والمقيّدة، وذلك استنادًا إلى التعاون مع مطوّري تطبيقات Android وأحدث الاختبارات الداخلية. نحرص دائمًا على توفير بدائل عامة قبل فرض قيود على الواجهات غير المتوفّرة في حزمة SDK.

إذا كان تطبيقك لا يستهدف الإصدار 15 من نظام التشغيل Android، قد لا تؤثّر بعض هذه التغييرات فيك على الفور. ومع ذلك، على الرغم من إمكانية وصول تطبيقك إلى بعض الواجهات غير التابعة لحزمة SDK استنادًا إلى مستوى واجهة برمجة التطبيقات المستهدَف في تطبيقك، فإنّ استخدام أي طريقة أو حقل غير تابع لحزمة SDK ينطوي دائمًا على خطر كبير بتعطُّل تطبيقك.

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

To learn more about the changes in this release of Android, see Updates to non-SDK interface restrictions in Android 15. To learn more about non-SDK interfaces generally, see Restrictions on non-SDK interfaces.