لماذا توقف تطبيقك فجأة عن العمل على Android 12؟
\n\nلقد قمت بتحديث targetSdkVersion إلى 31 أو أعلى، وقمت ببناء ملف APK، وعند تشغيله تحصل على تعطل مع رسالة غامضة:
android:exported needs to be explicitly specified for <activity>. \nApps targeting Android 12 and higher are required to specify \nan explicit value for `android:exported` when the corresponding \ncomponent has an intent filter defined.\n\nهل هذا مألوف؟ إذا كنت قد ألقيت نظرة على Stack Overflow، فسترى أن هذا السؤال حصد آلاف المشاهدات. المطورون في حالة ذعر، والمستخدمون في حيرة. لكن لا تتعجل في حذف مشروعك. في هذه المقالة، سنفهم ما هو هذا android:exported، ولماذا شددت Google المتطلبات، وكيفية إصلاح الخطأ بسرعة باستخدام Kotlin. هيا بنا!
ما هو android:exported ولماذا هو مطلوب؟
\n\nandroid:exported هو سمة في ملف AndroidManifest.xml تتحكم في ما إذا كانت التطبيقات الأخرى (أو النظام نفسه) يمكنها تشغيل المكون الخاص بك (Activity, Service, BroadcastReceiver أو ContentProvider). إذا تم تعيين السمة إلى true - يكون المكون متاحًا من الخارج. إذا كانت false - فقط داخل تطبيقك.
قبل Android 12 (API 31) كانت هذه السمة اختيارية. إذا لم تحددها، كان النظام يقرر بنفسه ما إذا كان المكون متاحًا:
\n- \n
- إذا كان النشاط (Activity) يحتوي على
<intent-filter>- كان النظام يعتبر أنexported = true. \n - إذا لم يكن هناك فلتر -
exported = false. \n
لكن هذا السلوك "التلقائي" هو ثغرة أمنية. تخيل: لقد كتبت نشاطًا لتسجيل الدخول إلى التطبيق وأضفت عن طريق الخطأ intent-filter. يمكن لأي تطبيق طرف ثالث استدعاء النشاط الخاص بك وسرقة الرموز المميزة (tokens). لذلك قررت Google: اعتبارًا من Android 12، يجب عليك تحديد android:exported بشكل صريح لأي مكون يحتوي على <intent-filter>.
كيف يبدو الخطأ عمليًا؟
\n\nلنفترض أن لديك بيانًا (manifest) بسيطًا:
\n\n<activity android:name=".MainActivity">\n <intent-filter>\n <action android:name="android.intent.action.MAIN" />\n <category android:name="android.intent.category.LAUNCHER" />\n </intent-filter>\n</activity>\n\nإذا كان targetSdkVersion = 31 أو أعلى، فستتلقى خطأ أثناء التجميع أو التشغيل. الحل هو إضافة android:exported:
<activity \n android:name=".MainActivity"\n android:exported="true">\n <intent-filter>\n <action android:name="android.intent.action.MAIN" />\n <category android:name="android.intent.category.LAUNCHER" />\n </intent-filter>\n</activity>\n\nبالنسبة للنشاط الرئيسي (MainActivity) الذي يتم تشغيله من الشاشة الرئيسية، يجب أن يكون exported بقيمة true - وإلا فلن يتمكن المشغل (launcher) من فتحه.
كيفية الإصلاح بشكل صحيح: دليل خطوة بخطوة
\n\nالخطوة 1. افتح ملف AndroidManifest.xml
\nابحث عن جميع <activity> و <service> و <receiver> و <provider> التي تحتوي على <intent-filter>. لكل منها، أضف android:exported.
الخطوة 2. حدد قيمة السمة
\nإليك قاعدة بسيطة:
\n- \n
- true - إذا كان المكون يجب أن يكون متاحًا من التطبيقات الأخرى (مثل نشاط تسجيل الدخول عبر Google، أو BroadcastReceiver للأحداث النظامية، أو النشاط الرئيسي مع فلتر LAUNCHER). \n
- false - إذا كان المكون يُستخدم فقط داخل تطبيقك (مثل نشاط يتم فتحه بالضغط على زر في الكود الخاص بك). \n
الخطوة 3. أمثلة لسيناريوهات مختلفة
\n\nالسيناريو 1: النشاط الرئيسي (Launcher)