كما هو الحال في الإصدارات السابقة، يتضمّن Android 15 تغييرات في السلوك قد تؤثر في تطبيقك. تنطبق تغييرات السلوك التالية حصريًا على التطبيقات التي تستهدف Android 15 أو الإصدارات الأحدث. إذا كان تطبيقك يستهدف الإصدار 15 من نظام التشغيل Android أو الإصدارات الأحدث، عليك تعديل تطبيقك ليتوافق مع هذه السلوكيات بشكل سليم، حيثما ينطبق ذلك.
احرص أيضًا على مراجعة قائمة التغييرات في السلوك التي تؤثّر في جميع التطبيقات
التي تعمل على Android 15 بغض النظر عن targetSdkVersion لتطبيقك.
الوظيفة الأساسية
يعدّل نظام التشغيل Android 15 العديد من الإمكانات الأساسية لنظام Android أو يوسّع نطاقها.
التغييرات على الخدمات التي تعمل في المقدّمة
نحن بصدد إجراء التغييرات التالية على الخدمات التي تعمل في المقدّمة في Android 15.
- سلوك مهلة الخدمة التي تعمل في المقدّمة لمزامنة البيانات
- نوع الخدمة الجديدة التي تعمل في المقدّمة لمعالجة الوسائط
- القيود المفروضة على
BOOT_COMPLETEDمستقبلات البث التي تبدأ الخدمات التي تعمل في المقدّمة - قيود على بدء الخدمات التي تعمل في المقدّمة عندما يكون لدى التطبيق إذن
SYSTEM_ALERT_WINDOW
سلوك مهلة الخدمة التي تعمل في المقدّمة لمزامنة البيانات
对于以 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]"
为避免因行为变更而导致问题,您可以执行以下一项或多项操作:
- 让您的服务实现新的
Service.onTimeout(int, int)方法。当您的应用收到回调时,请务必在几秒钟内调用stopSelf()。(如果您不立即停止应用,系统会生成故障。) - 确保应用的
dataSync服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。 - 仅通过直接的用户互动来启动
dataSync前台服务;由于您的应用在服务启动时位于前台,因此服务会在应用进入后台后的 6 小时内完整运行。 - 请改用替代 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]"
لتجنُّب حدوث الاستثناء، يمكنك تنفيذ أحد الإجراءات التالية:
- اطلب من مقدّم الخدمة تنفيذ طريقة
Service.onTimeout(int, int)الجديدة. عندما يتلقّى تطبيقك معاودة الاتصال، احرص على الاتصال بـstopSelf()في غضون بضع ثوانٍ. (إذا لم توقف التطبيق على الفور، سيُنشئ النظام حالة تعطُّل.) - تأكَّد من أنّ خدمات
mediaProcessingفي تطبيقك لا تعمل لأكثر من إجمالي 6 ساعات في أي فترة 24 ساعة (ما لم يتفاعل المستخدم مع التطبيق، يؤدي ذلك إلى إعادة ضبط الموقّت). - لا تبدأ
mediaProcessingالخدمات التي تعمل في المقدّمة إلا نتيجةً لتفاعل مباشر من العميل، لأنّ تطبيقك يكون في المقدّمة عند بدء الخدمة، ويكون لدى خدمتك ست ساعات كاملة بعد انتقال التطبيق إلى الخلفية. - بدلاً من استخدام خدمة
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 بتشغيل
الأنواع التالية من الخدمات التي تعمل في المقدّمة:
dataSynccameramediaPlaybackphoneCallmediaProjectionmicrophone(تم فرض هذا التقييد علىmicrophoneمنذ بدء استخدام الإصدار 14 من نظام التشغيل Android)
إذا حاول مستقبل 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 的调用(setInterruptionFilter、setNotificationPolicy)会导致创建或更新隐式 AutomaticZenRule,该 AutomaticZenRule 会根据这些 API 调用的调用周期开启和关闭。
请注意,只有当应用调用 setInterruptionFilter(INTERRUPTION_FILTER_ALL) 并希望该调用停用之前由其所有者激活的 AutomaticZenRule 时,此更改才会影响可观察到的行为。
التغييرات في واجهة برمجة تطبيقات OpenJDK
يواصل نظام التشغيل Android 15 عملية تحديث المكتبات الأساسية في Android لتتوافق مع الميزات المتوفّرة في أحدث إصدارات OpenJDK LTS.
يمكن أن تؤثر بعض هذه التغييرات في توافق التطبيقات التي تستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات):
تغييرات على واجهات برمجة التطبيقات لتنسيق السلاسل: أصبح التحقّق من صحة فهرس الوسيطات والعلامات والعرض والدقة أكثر صرامة عند استخدام واجهتَي برمجة التطبيقات
String.format()وFormatter.format()التاليتَين:String.format(String, Object[])String.format(Locale, String, Object[])Formatter.format(String, Object[])Formatter.format(Locale, String, Object[])
على سبيل المثال، يتم طرح الاستثناء التالي عند استخدام فهرس وسيطة بقيمة 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، لن يتم بعد الآن تحويل رموز اللغات العبرية واليديشية والإندونيسية إلى أشكالها القديمة (العبرية: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 في توافق تطبيقك
بعد تعديل compileSdk في إعدادات الإصدار في تطبيقك لاستخدام
الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات):
تعارض مع دالتَي الإضافة
MutableList.removeFirst()وMutableList.removeLast()فيkotlin-stdlibيتم ربط النوع
Listفي Java بالنوعMutableListفي Kotlin. بما أنّه تم طرح واجهتَي برمجة التطبيقاتList.removeFirst()وList.removeLast()في نظام التشغيل Android 15 (المستوى 35 لواجهة برمجة التطبيقات)، يحلّل برنامج الترجمة البرمجية في Kotlin استدعاءات الدوال، مثلlist.removeFirst()، بشكل ثابت إلى واجهات برمجة التطبيقات الجديدةListبدلاً من دوال الإضافة فيkotlin-stdlib.إذا تمت إعادة تجميع تطبيق مع ضبط
compileSdkعلى35وضبطminSdkعلى34أو إصدار أقدم، ثم تم تشغيل التطبيق على الإصدار 14 من نظام التشغيل Android أو إصدار أقدم، سيتم عرض خطأ وقت التشغيل:java.lang.NoSuchMethodError: No virtual method removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;يمكن لخيار
NewApilint الحالي في "المكوّن الإضافي لنظام Gradle المتوافق مع Android" رصد حالات الاستخدام الجديدة لواجهة برمجة التطبيقات../gradlew lintMainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi] list.removeFirst()لحلّ خطأ وقت التشغيل وأخطاء Lint، يمكن استبدال استدعاءات الدالتَين
removeFirst()وremoveLast()بالدالتَينremoveAt(0)وremoveAt(list.lastIndex)على التوالي في Kotlin. إذا كنت تستخدم الإصدار 2024.1.3 من Android Studio Ladybug أو إصدارًا أحدث، سيتوفّر لك أيضًا خيار إصلاح سريع لهذه الأخطاء.ننصحك بإزالة
@SuppressLint("NewApi")وlintOptions { disable 'NewApi' }إذا تم إيقاف خيار Lint.تعارض مع طرق أخرى في Java
تمت إضافة طرق جديدة إلى الأنواع الحالية، مثل
ListوDeque. قد لا تكون هذه الطرق الجديدة متوافقة مع الطرق التي تحمل الاسم نفسه وأنواع الوسيطات في الواجهات والفئات الأخرى. في حال حدوث تعارض في توقيع الطريقة مع عدم التوافق، سيُخرج برنامج التجميعjavacخطأ في وقت الإصدار. على سبيل المثال:مثال على الخطأ 1:
javac MyList.javaMyList.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.javaMyList.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.javaMyList.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لإصلاح أخطاء الإصدار هذه، يجب أن تتجاوز الفئة التي تنفّذ هذه الواجهات الطريقة بنوع إرجاع متوافق. مثلاً:
@Override public Object getFirst() { return List.super.getFirst(); }
الأمان
يتضمّن Android 15 تغييرات تعزّز أمان النظام للمساعدة في حماية التطبيقات والمستخدمين من التطبيقات الضارة.
إصدارات TLS المحظورة
يفرض نظام التشغيل Android 15 قيودًا على استخدام الإصدارَين 1.0 و1.1 من بروتوكول أمان طبقة النقل. تم إيقاف هذه الإصدارات نهائيًا في Android، ولكن تم الآن حظر استخدامها في التطبيقات التي تستهدف الإصدار Android 15.
عمليات إطلاق الأنشطة الآمنة في الخلفية
يحمي Android 15 المستخدمين من التطبيقات الضارة ويمنحهم المزيد من التحكّم في أجهزتهم من خلال إضافة تغييرات تمنع التطبيقات الضارة التي تعمل في الخلفية من نقل تطبيقات أخرى إلى المقدّمة، وزيادة امتيازاتها، وإساءة استخدام تفاعل المستخدم. تم فرض قيود على عمليات تشغيل الأنشطة في الخلفية منذ الإصدار Android 10 (المستوى 29 من واجهة برمجة التطبيقات).
تغييرات أخرى
- تغيير إعدادات
PendingIntentلصنّاع المحتوى إلى حظر عمليات إطلاق نشاط الخلفية تلقائيًا ويساعد ذلك في منع التطبيقات من إنشاءPendingIntentعن طريق الخطأ، ما قد يؤدي إلى إساءة استخدامها من قِبل الجهات المسيئة. - لا تعرض تطبيقًا في المقدّمة ما لم يسمح بذلك مرسل
PendingIntent. يهدف هذا التغيير إلى منع التطبيقات الضارة من إساءة استخدام إمكانية بدء الأنشطة في الخلفية. بشكلٍ تلقائي، لا يُسمح للتطبيقات بنقل حزمة المهام إلى المقدّمة إلا إذا سمح المنشئ بأذونات تشغيل الأنشطة في الخلفية أو إذا كان لدى المرسِل أذونات تشغيل الأنشطة في الخلفية. - التحكّم في كيفية إنهاء النشاط الأعلى في حزمة المهام إذا أنهى النشاط العلوي مهمة، سيعود نظام التشغيل Android إلى المهمة التي كانت نشطة آخر مرة. بالإضافة إلى ذلك، إذا أنهى نشاط غير نشط في المقدّمة مهمته، سيعود نظام التشغيل Android إلى الشاشة الرئيسية، ولن يحظر إنهاء هذا النشاط.
- منع تشغيل أنشطة عشوائية من تطبيقات أخرى في مهمتك يمنع هذا التغيير التطبيقات الضارة من خداع المستخدمين من خلال إنشاء أنشطة تبدو وكأنّها من تطبيقات أخرى.
- حظر النوافذ غير المرئية من أن يتم أخذها في الاعتبار عند تشغيل التطبيقات في الخلفية يساعد ذلك في منع التطبيقات الضارة من إساءة استخدام عمليات تشغيل الأنشطة في الخلفية لعرض محتوى غير مرغوب فيه أو ضار للمستخدمين.
نوايا أكثر أمانًا
يقدّم نظام التشغيل Android 15 StrictMode للأهداف.
للاطّلاع على سجلّات تفصيلية حول انتهاكات استخدام Intent، استخدِم الطريقة التالية:
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 لواجهة برمجة التطبيقات).
هذا تغيير غير متوافق قد يؤثر سلبًا في واجهة مستخدم تطبيقك. تؤثّر التغييرات في مناطق واجهة المستخدم التالية:
- شريط التنقل باستخدام مقبض الإيماءات
- تكون شفافة تلقائيًا.
- يتم إيقاف الإزاحة السفلية كي يتم عرض المحتوى خلف شريط التنقّل في النظام ما لم يتم تطبيق هوامش داخلية.
- تم إيقاف
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 لواجهة برمجة التطبيقات)، وقبل وبعد تطبيق العناصر المضمّنة. هذا المثال ليس شاملاً، وقد يظهر بشكل مختلف على Android Auto.
ما يجب التحقّق منه إذا كان تطبيقك معروضًا من الحافة إلى الحافة
إذا كان تطبيقك يعرض المحتوى من الحافة إلى الحافة ويطبّق هوامش داخلية، لن تتأثر في معظم الحالات، باستثناء السيناريوهات التالية. ومع ذلك، حتى إذا كنت تعتقد أنّك لن تتأثر بهذا التغيير، ننصحك باختبار تطبيقك.
- لديك نافذة غير عائمة، مثل
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 للاطّلاع على اعتبارات إضافية بشأن تطبيق عمليات الإزاحة.
واجهات برمجة التطبيقات المتوقّفة نهائيًا
تم إيقاف واجهات برمجة التطبيقات التالية نهائيًا ولكن لم يتم إيقافها:
R.attr#enforceStatusBarContrastR.attr#navigationBarColor(للتنقّل باستخدام ثلاثة أزرار، مع قيمة ألفا تبلغ %80)Window#isStatusBarContrastEnforcedWindow#setNavigationBarColor(للتنقّل باستخدام ثلاثة أزرار، مع قيمة ألفا بنسبة% 80)Window#setStatusBarContrastEnforced
تم إيقاف واجهات برمجة التطبيقات التالية:
R.attr#navigationBarColor(للتنقّل بالإيماءات)R.attr#navigationBarDividerColorR.attr#statusBarColorWindow#setDecorFitsSystemWindowsWindow#getNavigationBarColorWindow#getNavigationBarDividerColorWindow#getStatusBarColorWindow#setNavigationBarColor(للتنقّل بالإيماءات)Window#setNavigationBarDividerColorWindow#setStatusBarColor
الإعدادات الثابتة
إذا كان تطبيقك يستهدف الإصدار 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
بالنسبة إلى التطبيقات التي تستهدف الإصدار 15 من نظام التشغيل Android (المستوى 35 لواجهة برمجة التطبيقات)، تصبح سمة
elegantTextHeight TextView
true تلقائيًا، ما يؤدي إلى استبدال الخط المكثّف المستخدَم تلقائيًا ببعض
النصوص البرمجية التي تحتوي على مقاييس عمودية كبيرة بخط يسهل قراءته.
تم طرح الخط المكثّف لمنع حدوث مشاكل في التنسيقات. يمنع نظام التشغيل Android 13 (المستوى 33 من واجهة برمجة التطبيقات) حدوث العديد من هذه المشاكل من خلال السماح لتنسيق النص بشدّ الارتفاع العمودي باستخدام السمة fallbackLineSpacing
.
في Android 15، سيظل الخط المكثّف متوفّرًا في النظام، لذا يمكن لتطبيقك ضبط
elegantTextHeight على false للحصول على السلوك نفسه كما في السابق، ولكن من المرجّح عدم توفّره في الإصدارات القادمة. لذلك، إذا كان تطبيقك متوافقًا مع
النصوص البرمجية التالية: العربية أو البورمية أو التايلاندية أو التاميل أو الغوجاراتية أو الكجراتية أو الماليالامية أو
الأوديا أو التيلوغوية أو اللاوية، يمكنك اختبار تطبيقك من خلال ضبط القيمة elegantTextHeight على true.
elegantTextHeight للتطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) والإصدارات الأقدم
elegantTextHeight للتطبيقات التي تستهدف الإصدار Android 15تغيير عرض TextView لأشكال الحروف المعقّدة
在以前的 Android 版本中,某些具有复杂形状的手写字体或语言可能会在上一个或下一个字符的区域绘制字母。在某些情况下,此类字母会在开头或结尾处被剪裁。从 Android 15 开始,TextView 会分配宽度,以便为此类字母绘制足够的空间,并允许应用请求向左额外添加内边距以防止剪裁。
由于此更改会影响 TextView 确定宽度的方式,因此如果应用以 Android 15(API 级别 35)或更高版本为目标平台,TextView 会默认分配更多宽度。您可以通过对 TextView 调用 setUseBoundsForWidth API 来启用或停用此行为。
由于添加左内边距可能会导致现有布局未对齐,因此默认情况下不会添加内边距,即使以 Android 15 或更高版本为目标平台的应用也是如此。不过,您可以通过调用 setShiftDrawingOffsetForStartOverhang 添加额外的内边距以防止剪裁。
以下示例展示了这些更改如何改进某些字体和语言的文本布局。
<TextView android:fontFamily="cursive" android:text="java" />
<TextView android:fontFamily="cursive" android:text="java" android:useBoundsForWidth="true" android:shiftDrawingOffsetForStartOverhang="true" />
<TextView android:text="คอมพิวเตอร์" />
<TextView android:text="คอมพิวเตอร์" android:useBoundsForWidth="true" android:shiftDrawingOffsetForStartOverhang="true" />
ارتفاع السطر التلقائي المتوافق مع اللغة المحلية في EditText
في الإصدارات السابقة من Android، كان تنسيق النص يمدّد ارتفاع
النص ليتناسب مع ارتفاع سطر الخط الذي يتطابق مع اللغة الحالية. على سبيل المثال، إذا كان المحتوى باللغة اليابانية، يصبح ارتفاع النص أكبر قليلاً لأنّ ارتفاع السطر للخط الياباني
أكبر قليلاً من ارتفاع السطر للخط اللاتيني. ومع ذلك، على الرغم من هذه الاختلافات في ارتفاعات السطور، تم ضبط حجم العنصر
EditText بشكلٍ موحّد، بغض النظر عن
اللغة المستخدَمة، كما هو موضّح في الصورة التالية:
EditText عنصرًا يمكن أن يحتوي
على نص باللغة الإنجليزية (en) واليابانية (ja) والبورمية (my). يكون
ارتفاع الرمز EditText متطابقًا، على الرغم من أنّ هذه اللغات
لها ارتفاعات سطور مختلفة عن بعضها.بالنسبة إلى التطبيقات التي تستهدف الإصدار 15 من Android (المستوى 35 لواجهة برمجة التطبيقات)، تم الآن تخصيص الحد الأدنى لارتفاع السطر
لـ EditText لمطابقة الخط المرجعي للّغة المحدّدة، كما هو موضح
في الصورة التالية:
EditText عنصرًا يمكن أن يحتوي
على نص باللغة الإنجليزية (en) واليابانية (ja) والبورمية (my). يتضمّن الآن
ارتفاع الرمز EditText مساحة لاستيعاب
ارتفاع السطر التلقائي لخطوط هذه اللغات.يمكن لتطبيقك استعادة السلوك السابق إذا لزم الأمر من خلال تحديد سمة
useLocalePreferredLineHeightForMinimum
على false، ويمكن لتطبيقك ضبط الحد الأدنى المخصّص للمقاييس العمودية باستخدام واجهة برمجة التطبيقات
setMinimumFontMetrics في Kotlin و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 لإحدى الميزات في تطبيقك، عليك طلب واجهة برمجة تطبيقات عامة جديدة.
لمزيد من المعلومات عن التغييرات في هذا الإصدار من Android، اطّلِع على التعديلات على قيود واجهات غير حزمة SDK في Android 15. للاطّلاع على مزيد من المعلومات حول الواجهات غير المتوفّرة في حزمة SDK بشكل عام، اطّلِع على مقالة القيود المفروضة على الواجهات غير المتوفّرة في حزمة SDK.