كما هو الحال مع الإصدارات السابقة، يتضمّن Android 14 تغييرات في السلوك قد تؤثّر في تطبيقك. تنطبق تغييرات السلوك التالية حصريًا على التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث. إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android أو الإصدارات الأحدث، عليك تعديل تطبيقك ليتوافق مع هذه السلوكيات بشكل صحيح، حيثما ينطبق ذلك.
احرص أيضًا على مراجعة قائمة التغييرات في السلوك التي تؤثّر في جميع التطبيقات
التي تعمل على الإصدار 14 من نظام التشغيل Android بغض النظر عن
targetSdkVersion
للتطبيق.
الوظيفة الأساسية
أنواع الخدمات التي تعمل في المقدّمة مطلوبة
إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يجب تحديد نوع واحد على الأقل من الخدمات التي تعمل في المقدّمة لكل خدمة تعمل في المقدّمة ضمن تطبيقك. ويجب اختيار نوع خدمة تعمل في المقدّمة يمثّل حالة استخدام تطبيقك. يتوقّع النظام أن تستوفي الخدمات التي تعمل في المقدّمة والتي لها نوع معيّن حالة استخدام معيّنة.
إذا لم تكن حالة استخدام في تطبيقك مرتبطة بأيّ من هذه الأنواع، ننصح بشدة بنقل منطقك لاستخدام WorkManager أو مهام نقل البيانات التي يبدأها المستخدم.
فرض إذن BLUETOOTH_CONNECT في BluetoothAdapter
يفرض الإصدار 14 من نظام التشغيل Android إذن BLUETOOTH_CONNECT
عند استدعاء الأسلوب
BluetoothAdapter
getProfileConnectionState()
للتطبيقات التي تستهدف
Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث.
سبق أن تطلبت هذه الطريقة الحصول على إذن BLUETOOTH_CONNECT
،
ولكن لم يتم فرضها. تأكَّد من أنّ تطبيقك يعلن عن BLUETOOTH_CONNECT
في ملف
AndroidManifest.xml
الخاص به كما هو موضّح في المقتطف التالي، وتحقَّق من أنّه
منح المستخدم الإذن قبل استدعاء
getProfileConnectionState
.
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
تعديلات OpenJDK 17
يواصل نظام التشغيل Android 14 العمل على تحديث المكتبات الأساسية في Android لمواءمتها مع الميزات في أحدث إصدارات OpenJDK LTS، بما في ذلك تحديثات مكتبة ودعم لغة Java 17 لمطوّري التطبيقات والمنصات.
يمكن أن تؤثّر بعض هذه التغييرات في توافق التطبيق:
- تغييرات على التعبيرات العادية: تم الآن منع استخدام مراجع المجموعات غير الصالحة
لتتمكّن من اتّباع الدلالات في OpenJDK بشكلٍ أدق. قد تظهر لك
حالات جديدة يتم فيها طرح
IllegalArgumentException
من فئةjava.util.regex.Matcher
، لذا احرص على اختبار تطبيقك في المناطق التي تستخدم التعبيرات العادية. لتفعيل هذا التغيير أو إيقافه أثناء الاختبار، بدِّل العلامةDISALLOW_INVALID_GROUP_REFERENCE
باستخدام أدوات إطار العمل المعني بالتوافق. - معالجة معرّف UUID: تُجري طريقة
java.util.UUID.fromString()
الآن عمليات تحقّق أكثر صرامة عند التحقّق من مَعلمة الإدخال، لذا قد يظهر لكIllegalArgumentException
أثناء تحويل البيانات إلى نص. لتفعيل هذا التغيير أو إيقافه أثناء الاختبار، بدِّل العلامةENABLE_STRICT_VALIDATION
باستخدام أدوات إطار العمل المعني بالتوافق. - مشاكل ProGuard: في بعض الحالات، تؤدي إضافة فئة
java.lang.ClassValue
إلى حدوث مشكلة إذا حاولت تصغير تطبيقك وتشويشه وتحسينه باستخدام ProGuard. تنشأ المشكلة من مكتبة Kotlin التي تغيّر سلوك وقت التشغيل استنادًا إلى ما إذا كانتClass.forName("java.lang.ClassValue")
تُرجع فئة أم لا. إذا تم تطوير تطبيقك باستخدام إصدار قديم من وقت التشغيل بدون توفّر فئةjava.lang.ClassValue
، قد تؤدي عمليات التحسين هذه إلى إزالة طريقةcomputeValue
من الفئات المشتقة منjava.lang.ClassValue
.
تعزّز JobScheduler سلوك رد الاتصال والشبكة
自从引入后,JobScheduler 期望您的应用从
onStartJob
或 onStopJob
。在 Android 14 之前,如果作业运行时间过长,系统会停止作业并静默失败。如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,
超过在主线程上授予的时间,应用会触发 ANR
显示“没有响应 onStartJob
”错误消息或
“onStopJob
没有回复”。
此 ANR 可能是由以下 2 种情况造成的:
1.有工作阻塞主线程,阻止回调 onStartJob
或者onStopJob
在预期时间内执行并完成。
2. 开发者在 JobScheduler 中运行阻塞工作
回调 onStartJob
或 onStopJob
,阻止从
在预期的时限内完成
要解决第 1 个问题,您需要进一步调试阻塞主线程的因素
您可以使用以下代码
ApplicationExitInfo#getTraceInputStream()
,用于获取 Tombstone
ANR 发生时的跟踪信息如果您能够手动重现 ANR 问题
您可以录制系统轨迹,并使用
Android Studio 或 Perfetto,以便更好地了解应用上运行的
在发生 ANR 时调用主线程
请注意,直接使用 JobScheduler API 或使用 androidx 库 WorkManager 时可能会发生这种情况。
如需解决问题 2,请考虑迁移到 WorkManager,它支持将 onStartJob
或 onStopJob
中的任何处理封装在异步线程中。
JobScheduler
还引入了一项要求,即如果使用 setRequiredNetworkType
或 setRequiredNetwork
约束条件,则必须声明 ACCESS_NETWORK_STATE
权限。如果您的应用未声明
ACCESS_NETWORK_STATE
权限
Android 14 或更高版本,则会导致 SecurityException
。
Tiles launch API
بالنسبة إلى التطبيقات التي تستهدف 14 عامًا أو أعلى،
تم إيقاف TileService#startActivityAndCollapse(Intent)
نهائيًا وسيتم طرحه الآن.
استثناءً عند استدعائه. إذا كان تطبيقك يطلق الأنشطة من الشاشات، استخدِم
TileService#startActivityAndCollapse(PendingIntent)
بدلاً من ذلك.
الخصوصية
الوصول الجزئي إلى الصور والفيديوهات
يقدّم نظام التشغيل Android 14 ميزة "الوصول إلى الصور المحدّدة" التي تتيح للمستخدمين منح التطبيقات إذنًا بالوصول إلى صور وفيديوهات معيّنة في مكتبتهم، بدلاً من منح إذن بالوصول إلى كل الوسائط من نوع معيّن.
لا يتم تفعيل هذا التغيير إلا إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث. إذا لم تكن تستخدِم أداة اختيار الصور بعد، ننصحك بإضافتها إلى تطبيقك لتوفير تجربة متّسقة لاختيار الصور والفيديوهات، وذلك أيضًا لتعزيز خصوصية المستخدم بدون الحاجة إلى طلب أي أذونات تخزين.
إذا كنت تحافظ على أداة اختيار معرض الصور باستخدام أذونات التخزين وتحتاج إلى
الحفاظ على التحكّم الكامل في عملية التنفيذ، عليك تعديل عملية التنفيذ
لاستخدام الإذن الجديد READ_MEDIA_VISUAL_USER_SELECTED
. إذا كان تطبيقك
لا يستخدم الإذن الجديد، سيشغّل النظام تطبيقك في وضع ملف التوافق.
تجربة المستخدم
إشعارات آمنة بملء الشاشة
في الإصدار 11 من Android (المستوى 30 لواجهة برمجة التطبيقات)، كان بإمكان أي تطبيق استخدام Notification.Builder.setFullScreenIntent
لإرسال رسائل بهدف عرض المحتوى بملء الشاشة عندما يكون الهاتف مقفلاً. يمكنك منح هذا الإذن تلقائيًا عند تثبيت التطبيق من خلال
تحديد إذن USE_FULL_SCREEN_INTENT
فيملف
AndroidManifest.
تم تصميم إشعارات العرض بملء الشاشة لإرسال إشعارات مهمة للغاية
تتطلّب اهتمام المستخدم الفوري، مثل مكالمة هاتفية واردة أو إعدادات المنبّه التي ضبطها المستخدم. بالنسبة إلى التطبيقات التي تستهدف الإصدار
Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، تقتصر التطبيقات المسموح لها باستخدام
هذا الإذن على تلك التي توفّر وظائف الاتصال والمنبّهات فقط. يُلغي "متجر Google
Play" أذونات USE_FULL_SCREEN_INTENT
التلقائية لأي تطبيقات
لا تتوافق مع هذا الملف الشخصي. الموعد النهائي لتنفيذ هذه التغييرات في السياسة هو 31 أيار (مايو)
2024.
يظل هذا الإذن مفعّلاً للتطبيقات المثبَّتة على الهاتف قبل أن يصعّد المستخدم إلى الإصدار 14 من Android. يمكن للمستخدمين تفعيل هذا الإذن أو إيقافه.
يمكنك استخدام واجهة برمجة التطبيقات الجديدة
NotificationManager.canUseFullScreenIntent
للتحقّق مما إذا كان تطبيقك
يملك الإذن. وإذا لم يكن الأمر كذلك، يمكن لتطبيقك استخدام الطلب الجديد
ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
لفتح صفحة الإعدادات
التي يمكن للمستخدمين فيها منح الإذن.
الأمان
القيود المفروضة على النوايا الضمنية والمعلّقة
بالنسبة إلى التطبيقات التي تستهدف الإصدار Android 14 (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يحظِر Android التطبيقات من إرسال نوايا ضمنية إلى مكوّنات التطبيقات الداخلية بالطُرق التالية:
- لا يتم تسليم الأهداف الضمنية إلا إلى المكوّنات التي يتم تصديرها. يجب أن تطبّق التطبيقات نية واضحة للتسليم إلى مكوّنات غير مصدَّرة أو تضع علامة على المكوّنات تشير إلى أنّه تم تصديرها.
- إذا أنشأ تطبيق طلبًا مؤقتًا قابلاً للتغيير باستخدام طلب لا يحدد مكونات أو حزمة، يُنشئ النظام استثناءً.
تمنع هذه التغييرات التطبيقات الضارة من اعتراض الأهداف الضمنية المقصود استخدامها من قِبل المكوّنات الداخلية للتطبيق.
على سبيل المثال، في ما يلي فلتر أهداف يمكن تعريفه في ملف بيان تطبيقك:
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
إذا حاول تطبيقك تشغيل هذا النشاط باستخدام نية ضمنية، سيتم طرح استثناء
ActivityNotFoundException
:
Kotlin
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(Intent("com.example.action.APP_ACTION"))
Java
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
لبدء النشاط الذي لم يتم تصديره، يجب أن يستخدم تطبيقك نية صريحة بدلاً من ذلك:
Kotlin
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
Java
// This makes the intent explicit. Intent explicitIntent = new Intent("com.example.action.APP_ACTION") explicitIntent.setPackage(context.getPackageName()); context.startActivity(explicitIntent);
يجب أن تحدّد مستقبِلات البث المسجّلة في وقت التشغيل سلوك التصدير
على التطبيقات والخدمات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وتستخدم
مستقبلات مسجّلة في السياق تحديد علامة
للإشارة إلى ما إذا كان يجب تصدير المستقبل إلى جميع التطبيقات الأخرى على
الجهاز أم لا: إما RECEIVER_EXPORTED
أو RECEIVER_NOT_EXPORTED
، على التوالي.
يساعد هذا الشرط في حماية التطبيقات من الثغرات الأمنية من خلال الاستفادة من
ميزات هذه الأجهزة المستلِمة التي تم تقديمها في Android 13.
استثناء لأجهزة الاستقبال التي تتلقّى عمليات بث النظام فقط
إذا كان تطبيقك يسجِّل جهاز استقبال فقط لتلقّي
عمليات بث النظام من خلال Context#registerReceiver
طُرق، مثل Context#registerReceiver()
، يجب عدم تحديد علامة عند تسجيل جهاز الاستقبال.
تحميل الرموز الديناميكية بشكل أكثر أمانًا
إذا كان تطبيقك يستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث وكان يستخدم ميزة "تحميل الرمز الديناميكي" (DCL)، يجب وضع علامة "للقراءة فقط" على جميع الملفات المحمَّلة ديناميكيًا. بخلاف ذلك، يطرح النظام استثناءً. لذا نوصي التطبيقات بتجنّبها رمز يتم تحميله ديناميكيًا كلما أمكن، لأن القيام بذلك يزيد بشكل كبير من خطر تعرّض التطبيق عن طريق إدخال الرموز أو التلاعب بالرموز البرمجية.
إذا كان عليك تحميل الرمز ديناميكيًا، استخدِم النهج التالي لضبط الملف المحمَّل ديناميكيًا (مثل ملف DEX أو JAR أو APK) على وضع القراءة فقط بعد فتح الملف وقبل كتابة أي محتوى:
Kotlin
val jar = File("DYNAMICALLY_LOADED_FILE.jar") val os = FileOutputStream(jar) os.use { // Set the file to read-only first to prevent race conditions jar.setReadOnly() // Then write the actual file content } val cl = PathClassLoader(jar, parentClassLoader)
Java
File jar = new File("DYNAMICALLY_LOADED_FILE.jar"); try (FileOutputStream os = new FileOutputStream(jar)) { // Set the file to read-only first to prevent race conditions jar.setReadOnly(); // Then write the actual file content } catch (IOException e) { ... } PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);
التعامل مع الملفات الحالية التي يتم تحميلها ديناميكيًا
لمنع ظهور استثناءات في الملفات الحالية التي يتم تحميلها ديناميكيًا، ننصح بحذف الملفات وإعادة إنشائها قبل محاولة تحميلها مجددًا في تطبيقك ديناميكيًا. وعند إعادة إنشاء الملفات، اتّبِع التوجيهات السابقة لوضع علامة "للقراءة فقط" على الملفات في وقت الكتابة. بدلاً من ذلك، يمكنك إعادة تصنيف الملفات الحالية على أنها للقراءة فقط، ولكن في هذه الحالة، أنصحك بالتحقّق من سلامة الملفات أولاً (على سبيل المثال، من خلال التحقّق من توقيع الملف مقابل قيمة موثوق بها)، للمساعدة في حماية تطبيقك من الإجراءات الضارة.
قيود إضافية على بدء الأنشطة من الخلفية
بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يفرض النظام المزيد من القيود على الحالات التي يُسمح للتطبيقات فيها ببدء الأنشطة من الخلفية:
- عندما يُرسِل تطبيق طلبًا باستخدام
PendingIntent
PendingIntent#send()
أو طُرق مشابهة، يجب أن يوافق التطبيق على منح امتيازات بدء النشاط في الخلفية لبدء الطلب المعلّق. للموافقة على الميزة، يجب أن يُرسل التطبيقActivityOptions
حزمة تحتوي علىsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
. - عندما يربط تطبيق مرئي خدمة تطبيق آخر يعمل في الخلفية
باستخدام طريقة
bindService()
، يجب أن يوافق التطبيق المرئي الآن على ذلك إذا كان يريد منح امتيازات تشغيل النشاط في الخلفية الخاصة به للخدمة المرتبطة. للموافقة على الميزة، يجب أن يتضمّن التطبيق العلامةBIND_ALLOW_ACTIVITY_STARTS
عند استدعاء الأسلوبbindService()
.
تعمل هذه التغييرات على توسيع مجموعة القيود الحالية لحماية المستخدِمين من خلال منع التطبيقات الضارة من إساءة استخدام واجهات برمجة التطبيقات لبدء أنشطة مزعجة من الخلفية.
مسح مسار ملفات Zip
بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمنع Android ثغرة ملف ZIP
Path Traversal بالطريقة التالية: يعرض كل من
ZipFile(String)
و
ZipInputStream.getNextEntry()
خطأ
ZipException
إذا كانت أسماء إدخالات ملفات ZIP تحتوي على ".." أو تبدأ
بـ "/".
يمكن للتطبيقات إيقاف عملية التحقّق هذه من خلال الاتصال بالرقم
dalvik.system.ZipPathValidator.clearCallback()
.
يجب الحصول على موافقة المستخدم لكل جلسة التقاط MediaProjection
بالنسبة إلى التطبيقات التي تستهدف الإصدار 14 من نظام التشغيل Android (المستوى 34 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يتم طرح SecurityException
من قِبل MediaProjection#createVirtualDisplay
في أيّ من السيناريوهين التاليين:
- يخزِّن تطبيقك
Intent
الذي يتم إرجاعه منMediaProjectionManager#createScreenCaptureIntent
، ويمرره مرارًا وتكرارًا إلىMediaProjectionManager#getMediaProjection
. - يستدعي تطبيقك
MediaProjection#createVirtualDisplay
عدة مرات على مثيلMediaProjection
نفسه.
يجب أن يطلب تطبيقك من المستخدم تقديم موافقته قبل كل جلسة تسجيل. جلسة تسجيل واحدة هي طلب واحد على
MediaProjection#createVirtualDisplay
، ويجب استخدام كل مثيل منMediaProjection
مرّة واحدة فقط.
التعامل مع تغييرات الإعدادات
إذا كان تطبيقك بحاجة إلى استدعاء MediaProjection#createVirtualDisplay
لمعالجة
تغييرات الضبط (مثل تغيير اتجاه الشاشة أو حجمها)،
يمكنك اتّباع الخطوات التالية لتعديل VirtualDisplay
لمثيل
MediaProjection
الحالي:
- استخدِم
VirtualDisplay#resize
بالعرض والارتفاع الجديدَين. - قدِّم
Surface
جديدًا بالعرض والارتفاع الجديدَين لVirtualDisplay#setSurface
.
تسجيل طلب معاودة الاتصال
يجب أن يسجِّل تطبيقك طلب استدعاء للتعامل مع الحالات التي لا يمنح فيها المستخدم
الموافقة على مواصلة جلسة الالتقاط. لتنفيذ ذلك، عليك تنفيذ
Callback#onStop
وإصدار تطبيقك مع أي موارد ذات صلة (مثل
VirtualDisplay
وSurface
).
إذا لم يسجِّل تطبيقك هذه الوظيفة المرجعية،
يُرسِل MediaProjection#createVirtualDisplay
IllegalStateException
عند استدعائه من خلال تطبيقك.
تعديل القيود المفروضة على استخدام واجهات برمجة التطبيقات غير التابعة لحزمة SDK
يتضمّن نظام التشغيل Android 14 قوائم معدَّلة لواجهات غير SDK المحظورة استنادًا إلى التعاون مع مطوّري تطبيقات Android وأحدث الاختبارات الداخلية. نحرص دائمًا على توفير بدائل عامة قبل فرض قيود على الواجهات غير المتوفّرة في حزمة SDK.
إذا كان تطبيقك لا يستهدف الإصدار 14 من نظام التشغيل Android، قد لا تؤثّر بعض هذه التغييرات فيك على الفور. ومع ذلك، على الرغم من أنّه يمكنك حاليًا استخدام بعض الواجهات غير التابعة لحزمة SDK (حسب مستوى واجهة برمجة التطبيقات المستهدَف في تطبيقك)، فإنّ استخدام أي طريقة أو حقل غير تابع لحزمة SDK ينطوي دائمًا على خطر كبير بتعطُّل تطبيقك.
إذا لم تكن متأكدًا مما إذا كان تطبيقك يستخدم واجهات غير متوفرة في حزمة SDK، يمكنك اختبار تطبيقك لمعرفة ذلك. إذا كان تطبيقك يعتمد على واجهات غير متوفرة في حزمة SDK، عليك البدء في التخطيط لنقل البيانات إلى بدائل حزمة SDK. ومع ذلك، ندرك أنّ بعض التطبيقات لديها حالات استخدام صالحة لواجهات غير متوفرة في حزمة SDK. إذا لم تتمكّن من العثور على بديل لاستخدام واجهة غير متوفّرة في حزمة SDK لإحدى الميزات في تطبيقك، عليك طلب واجهة برمجة تطبيقات عامة جديدة.
لمزيد من المعلومات عن التغييرات في هذا الإصدار من Android، اطّلِع على تعديلات على القيود المفروضة على الواجهات غير المستندة إلى حزمة SDK في Android 14. للاطّلاع على مزيد من المعلومات حول الواجهات غير المتوفرة في حزمة SDK بشكل عام، اطّلِع على مقالة القيود المفروضة على الواجهات غير المتوفرة في حزمة SDK.