Intent هي عنصر مراسلة يمكنك استخدامه لطلب تنفيذ إجراء
من مكوّن تطبيق آخر.
على الرغم من أنّ رسائل Intent تسهّل التواصل بين المكوّنات بعدة طرق، هناك ثلاث حالات استخدام أساسية:
- بدء نشاط
يمثّل
Activityشاشة واحدة في تطبيق. يمكنك بدء مثيل جديد منActivityمن خلال تمريرIntentإلىstartActivity(). تصف السمةIntentالنشاط الذي سيتم بدءه وتحمل أي بيانات ضرورية.إذا أردت تلقّي نتيجة من النشاط عند انتهائه، اتّصِل بالرقم
startActivityForResult(). يتلقّى نشاطك النتيجة كعنصرIntentمنفصل في معاودة الاتصالonActivityResult()الخاصة بنشاطك. لمزيد من المعلومات، يُرجى الاطّلاع على دليل الأنشطة. - بدء خدمة
Serviceهو مكوّن ينفّذ عمليات في الخلفية بدون واجهة مستخدم. في الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يمكنك بدء خدمة باستخدامJobScheduler. لمزيد من المعلومات حولJobScheduler، يُرجى الاطّلاع علىAPI-reference documentation.بالنسبة إلى الإصدارات الأقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، يمكنك بدء خدمة باستخدام طرق الفئة
Service. يمكنك بدء خدمة لتنفيذ عملية لمرة واحدة (مثل تنزيل ملف) من خلال تمريرIntentإلىstartService(). توضّحIntentالخدمة التي سيتم تشغيلها وتحمل أي بيانات ضرورية.إذا كانت الخدمة مصمَّمة بواجهة بين العميل والخادم، يمكنك الربط بالخدمة من مكوّن آخر من خلال تمرير
IntentإلىbindService(). لمزيد من المعلومات، راجِع دليل الخدمات. - عرض بث
الإعلان هو رسالة يمكن لأي تطبيق تلقّيها. يقدّم النظام عمليات بث مختلفة لأحداث النظام، مثل وقت بدء تشغيل النظام أو بدء شحن الجهاز. يمكنك إرسال بث إلى تطبيقات أخرى عن طريق تمرير
IntentإلىsendBroadcast()أوsendOrderedBroadcast().
توضّح بقية هذه الصفحة طريقة عمل النوايا وكيفية استخدامها. للحصول على معلومات ذات صلة، يُرجى الاطّلاع على التفاعل مع تطبيقات أخرى ومشاركة المحتوى.
أنواع النوايا
هناك نوعان من الأهداف:
- تحدّد الأهداف الصريحة المكوِّن الذي سيحقّق الهدف من التطبيق، وذلك من خلال تحديد
ComponentNameكامل. ستستخدم عادةً Intent صريحًا لبدء مكوّن في تطبيقك، لأنّك تعرف اسم فئة النشاط أو الخدمة التي تريد بدءها. على سبيل المثال، يمكنك بدء نشاط جديد داخل تطبيقك استجابةً لإجراء اتّخذه المستخدم، أو بدء خدمة لتنزيل ملف في الخلفية. - لا تحدّد الأهداف الضمنية اسم مكوّن معيّن، بل تحدّد إجراءً عامًا يجب تنفيذه، ما يسمح لمكوّن من تطبيق آخر بمعالجة هذا الإجراء. على سبيل المثال، إذا أردت عرض موقع جغرافي للمستخدم على خريطة، يمكنك استخدام implicit intent لطلب أن يعرض تطبيق آخر قادر على ذلك الموقع الجغرافي المحدّد على خريطة.
يوضّح الشكل 1 كيفية استخدام الغرض عند بدء نشاط. عندما يحدّد العنصر
Intent أحد مكوّنات النشاط بشكل صريح، يبدأ النظام
هذا المكوّن على الفور.
الشكل 1: كيف يتم تسليم implicit intent من خلال النظام لبدء نشاط آخر: [1] ينشئ النشاط (أ) Intent مع وصف إجراء ويمرّره إلى startActivity(). [2] يبحث نظام Android عن intent filter يطابق intent في جميع التطبيقات. عند العثور على تطابق، [3] يبدأ النظام نشاط المطابقة (النشاط B) من خلال استدعاء الطريقة onCreate() وتمرير Intent إليها.
عند استخدام implicit intent، يعثر نظام Android على المكوّن المناسب لبدء التشغيل من خلال مقارنة محتوى intent بـ intent filters المحدّدة في ملف البيان الخاص بالتطبيقات الأخرى على الأجهزة. إذا كان intent يتطابق مع intent filter، يبدأ النظام هذا المكوِّن ويسلّمه الكائن Intent. إذا كانت فلاتر الأهداف المتعددة متوافقة، يعرض النظام مربّع حوار ليتمكّن المستخدم من اختيار التطبيق الذي يريد استخدامه.
فلتر Intent هو تعبير في ملف البيان الخاص بالتطبيق يحدّد نوع الأهداف التي يريد المكوّن تلقّيها. على سبيل المثال، من خلال تعريف intent filter لنشاط معيّن، يمكنك السماح للتطبيقات الأخرى ببدء نشاطك مباشرةً بنوع معيّن من intent. وبالمثل، إذا لم تحدّد أي فلاتر أهداف لنشاط معيّن، لا يمكن بدء هذا النشاط إلا باستخدام هدف ضمني.
تنبيه: لضمان أمان تطبيقك، احرص دائمًا على استخدام
intent ضمني
عند بدء Service، ولا
تحدّد فلاتر intent لخدماتك. يُعد استخدام implicit intent لبدء خدمة خطرًا أمنيًا لأنّه لا يمكنك التأكّد من الخدمة التي ستستجيب للـ intent، ولا يمكن للمستخدم معرفة الخدمة التي تبدأ. اعتبارًا من الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات)، يعرض النظام استثناءً إذا طلبت bindService() باستخدام غرض ضمني.
إنشاء نية
يحمل العنصر Intent معلومات يستخدمها نظام التشغيل Android لتحديد المكوّن الذي سيتم تشغيله (مثل اسم المكوّن أو فئة المكوّن التي يجب أن تتلقّى الغرض)، بالإضافة إلى معلومات يستخدمها المكوّن المتلقّي لتنفيذ الإجراء بشكل صحيح (مثل الإجراء الذي سيتم اتخاذه والبيانات التي سيتم العمل عليها).
تتضمّن Intent المعلومات الأساسية التالية:
- اسم المكوِّن
- اسم المكوّن الذي سيتم تشغيله.
هذا الحقل اختياري، ولكنّه يمثّل جزءًا مهمًا من المعلومات التي تجعل intent صريحًا، ما يعني أنّه يجب تسليم intent إلى مكوّن التطبيق المحدّد باسم المكوّن فقط. بدون اسم مكوّن، يكون الغرض ضمنيًا ويحدّد النظام المكوّن الذي يجب أن يتلقّى الغرض استنادًا إلى معلومات الغرض الأخرى (مثل الإجراء والبيانات والفئة الموضّحة أدناه). إذا كنت بحاجة إلى بدء تشغيل مكوّن معيّن في تطبيقك، عليك تحديد اسم المكوّن.
ملاحظة: عند بدء
Service، يجب دائمًا تحديد اسم المكوّن. بخلاف ذلك، لا يمكنك التأكّد من الخدمة التي ستستجيب للغرض، ولن يتمكّن المستخدم من معرفة الخدمة التي ستبدأ.هذا الحقل الخاص بالسمة
Intentهو عنصرComponentName، ويمكنك تحديده باستخدام اسم فئة مؤهَّل بالكامل للمكوّن المستهدَف، بما في ذلك اسم حزمة التطبيق، مثلcom.example.ExampleActivity. يمكنك ضبط اسم المكوّن باستخدامsetComponent()أوsetClass()أوsetClassName()أو باستخدام الدالة الإنشائيةIntent. - الإجراء
- سلسلة تحدّد الإجراء العام المطلوب تنفيذه (مثل عرض أو اختيار).
في حالة intent البث، هذا هو الإجراء الذي تم تنفيذه ويتم الإبلاغ عنه. يحدّد الإجراء إلى حد كبير كيفية تنظيم بقية الغرض، لا سيما المعلومات الواردة في البيانات والإضافات.
يمكنك تحديد إجراءاتك الخاصة لاستخدامها من خلال الأهداف داخل تطبيقك (أو لاستخدامها من خلال تطبيقات أخرى لاستدعاء المكوّنات في تطبيقك)، ولكنك عادةً ما تحدّد ثوابت الإجراءات المحدّدة بواسطة الفئة
Intentأو فئات إطار العمل الأخرى. في ما يلي بعض الإجراءات الشائعة لبدء نشاط:ACTION_VIEW- استخدِم هذا الإجراء في هدف مع
startActivity()عندما تتوفّر لديك بعض المعلومات التي يمكن أن يعرضها نشاط للمستخدم، مثل صورة لعرضها في تطبيق معرض الصور، أو عنوان لعرضه في تطبيق خرائط. ACTION_SEND- يُعرف هذا الإجراء أيضًا باسم الغرض share، ويجب استخدامه في الغرض مع
startActivity()عندما يكون لديك بعض البيانات التي يمكن للمستخدم مشاركتها من خلال تطبيق آخر، مثل تطبيق البريد الإلكتروني أو تطبيق المشاركة على وسائل التواصل الاجتماعي.
راجِع مرجع الفئة
Intentلمعرفة المزيد من الثوابت التي تحدّد الإجراءات العامة. يتم تحديد إجراءات أخرى في أماكن أخرى ضمن إطار عمل Android، مثلSettingsللإجراءات التي تفتح شاشات معيّنة في تطبيق "الإعدادات" على النظام.يمكنك تحديد الإجراء الخاص بغرض باستخدام
setAction()أو باستخدام أداة إنشاءIntent.إذا كنت تحدّد إجراءاتك الخاصة، احرص على تضمين اسم حزمة تطبيقك كبادئة، كما هو موضّح في المثال التالي:
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- البيانات
- معرّف الموارد المنتظم (URI) (كائن
Uri) الذي يشير إلى البيانات المطلوب اتخاذ إجراء بشأنها و/أو نوع MIME الخاص بهذه البيانات. يتم تحديد نوع البيانات المقدَّمة بشكل عام من خلال إجراء الهدف. على سبيل المثال، إذا كان الإجراءACTION_EDIT، يجب أن تحتوي البيانات على معرّف الموارد الموحّد (URI) للمستند المطلوب تعديله.عند إنشاء هدف، من المهم غالبًا تحديد نوع البيانات (نوع MIME) بالإضافة إلى معرّف الموارد المنتظم (URI). على سبيل المثال، قد لا يتمكّن نشاط يمكنه عرض الصور من تشغيل ملف صوتي، حتى إذا كانت تنسيقات معرّف الموارد المنتظم متشابهة. يساعد تحديد نوع MIME للبيانات نظام Android في العثور على أفضل مكوّن لتلقّي الغرض. ومع ذلك، يمكن في بعض الأحيان استنتاج نوع MIME من معرّف الموارد المنتظم (URI)، خاصةً عندما تكون البيانات معرّف موارد منتظم (URI) من النوع
content:. يشير معرّف الموارد الموحّدcontent:إلى أنّ البيانات مخزّنة على الجهاز ويتحكّم فيهاContentProvider، ما يتيح للنظام الاطّلاع على نوع MIME الخاص بالبيانات.لضبط عنوان URI للبيانات فقط، استخدِم
setData(). لضبط نوع MIME فقط، استخدِمsetType(). إذا لزم الأمر، يمكنك ضبط كليهما بشكل صريح باستخدامsetDataAndType().تنبيه: إذا كنت تريد ضبط كلّ من معرّف الموارد المنتظم (URI) ونوع MIME، لا تستخدِم
setData()وsetType()لأنّ كلّ منهما يبطل قيمة الآخر. استخدِمsetDataAndType()دائمًا لضبط كل من معرّف الموارد الموحّد ونوع MIME. - Category (الفئة)
- سلسلة تحتوي على معلومات إضافية حول نوع المكوّن الذي يجب أن يتعامل مع الغرض. يمكن وضع أي عدد من أوصاف الفئات في هدف، ولكن معظم الأهداف لا تتطلّب فئة.
في ما يلي بعض الفئات الشائعة:
CATEGORY_BROWSABLE- يسمح النشاط المستهدف بتشغيله من خلال متصفّح ويب لعرض البيانات التي يشير إليها الرابط، مثل صورة أو رسالة إلكترونية.
CATEGORY_LAUNCHER- النشاط هو النشاط الأوّلي لمهمة ويتم إدراجه في مشغّل التطبيقات بالنظام.
اطّلِع على وصف الفئة
Intentللحصول على القائمة الكاملة للفئات.يمكنك تحديد فئة باستخدام
addCategory().
تمثّل الخصائص المذكورة أعلاه (اسم المكوّن والإجراء والبيانات والفئة) الخصائص المحدّدة للغرض. ومن خلال قراءة هذه الخصائص، يمكن لنظام Android تحديد مكوّن التطبيق الذي يجب تشغيله. ومع ذلك، يمكن أن يتضمّن الغرض معلومات إضافية لا تؤثّر في كيفية تحويله إلى أحد مكونات التطبيق. يمكن أن يوفّر الغرض أيضًا المعلومات التالية:
- الميزات الإضافية
- أزواج المفاتيح والقيم التي تتضمّن معلومات إضافية مطلوبة لتنفيذ الإجراء المطلوب.
وكما تستخدم بعض الإجراءات أنواعًا معيّنة من معرّفات الموارد الموحّدة للبيانات، تستخدم بعض الإجراءات أيضًا عناصر إضافية معيّنة.
يمكنك إضافة بيانات إضافية باستخدام طرق
putExtra()مختلفة، يقبل كل منها مَعلمتَين: اسم المفتاح والقيمة. يمكنك أيضًا إنشاء عنصرBundleيتضمّن جميع البيانات الإضافية، ثم إدراجBundleفيIntentباستخدامputExtras().على سبيل المثال، عند إنشاء هدف لإرسال رسالة إلكترونية باستخدام
ACTION_SEND، يمكنك تحديد المستلِم إلى باستخدام المفتاحEXTRA_EMAIL، وتحديد الموضوع باستخدام المفتاحEXTRA_SUBJECT.تحدّد الفئة
Intentالعديد من الثوابتEXTRA_*لأنواع البيانات الموحّدة. إذا كنت بحاجة إلى تعريف مفاتيح إضافية خاصة بك (لأغراض يتلقّاها تطبيقك)، احرص على تضمين اسم حزمة تطبيقك كبادئة، كما هو موضّح في المثال التالي:Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
تنبيه: لا تستخدِم بيانات
ParcelableأوSerializableعند إرسال غرض تتوقّع أن يتلقّاه تطبيق آخر. إذا حاول تطبيق الوصول إلى البيانات في عنصرBundleولكن لم يكن لديه إذن الوصول إلى الفئة التي تم تجميعها أو تسلسلها، سيُصدر النظامRuntimeException. - الأعلام
- يتم تحديد العلامات في الفئة
Intentالتي تعمل كبيانات وصفية للغرض. قد توجّه العلامات نظام Android بشأن كيفية تشغيل نشاط معيّن (على سبيل المثال، المهمة التي يجب أن ينتمي إليها النشاط) وكيفية التعامل معه بعد تشغيله (على سبيل المثال، ما إذا كان ينتمي إلى قائمة الأنشطة الأخيرة).لمزيد من المعلومات، اطّلِع على طريقة
setFlags().
مثال على explicit intent
الهدف من استخدام explicit intent هو تشغيل مكوّن معيّن في التطبيق، مثل Activity أو خدمة معيّنة في تطبيقك. لإنشاء explicit intent، حدِّد اسم المكوّن لكائن Intent، وجميع خصائص intent الأخرى اختيارية.
على سبيل المثال، إذا أنشأت خدمة في تطبيقك باسم DownloadService،
مصمَّمة لتنزيل ملف من الويب، يمكنك بدء تشغيلها باستخدام الرمز التالي:
Kotlin
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" val downloadIntent = Intent(this, DownloadService::class.java).apply { data =Uri.parse(fileUrl) } startService(downloadIntent)
Java
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" Intent downloadIntent = new Intent(this, DownloadService.class); downloadIntent.setData(Uri.parse(fileUrl)); startService(downloadIntent);
يوفّر المنشئ Intent(Context, Class) التطبيق Context والمكوِّن كائن Class. وبالتالي، يبدأ هذا الغرض بشكل صريح الفئة DownloadService في التطبيق.
لمزيد من المعلومات حول إنشاء خدمة وبدء استخدامها، يُرجى الاطّلاع على دليل الخدمات.
مثال على implicit intent
تحدّد رسالة Intent الضمنية إجراءً يمكنه استدعاء أي تطبيق على الجهاز قادر على تنفيذ هذا الإجراء. يكون استخدام الغرض الضمني مفيدًا عندما لا يستطيع تطبيقك تنفيذ الإجراء، ولكن من المحتمل أن تستطيع تطبيقات أخرى تنفيذه، وتريد أن يختار المستخدم التطبيق الذي يريد استخدامه.
على سبيل المثال، إذا كان لديك محتوى تريد أن يشاركه المستخدم مع أشخاص آخرين، أنشئ هدفًا باستخدام الإجراء ACTION_SEND وأضِف عناصر إضافية تحدّد المحتوى المطلوب مشاركته. عند استدعاء
startActivity() باستخدام هذه النية، يمكن للمستخدم
اختيار تطبيق لمشاركة المحتوى من خلاله.
Kotlin
// Create the text message with a string. val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, textMessage) type = "text/plain" } // Try to invoke the intent. try { startActivity(sendIntent) } catch (e: ActivityNotFoundException) { // Define what your app should do if no activity can handle the intent. }
Java
// Create the text message with a string. Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType("text/plain"); // Try to invoke the intent. try { startActivity(sendIntent); } catch (ActivityNotFoundException e) { // Define what your app should do if no activity can handle the intent. }
عند استدعاء startActivity()، يفحص النظام جميع التطبيقات المثبَّتة لتحديد التطبيقات التي يمكنها التعامل مع هذا النوع من الأهداف (هدف يتضمّن الإجراء ACTION_SEND ويحمل بيانات "text/plain"). إذا كان هناك تطبيق واحد فقط يمكنه التعامل معها، سيتم فتح هذا التطبيق على الفور وسيتم منحه الغرض. إذا لم تتمكّن أي تطبيقات أخرى من التعامل معها، يمكن لتطبيقك رصد
ActivityNotFoundException
الذي يحدث. إذا كانت أنشطة متعددة تقبل الغرض، يعرض النظام مربع حوار مثل الموضّح في الشكل 2، ليتمكّن المستخدم من اختيار التطبيق الذي يريد استخدامه.
تتضمّن الدليل أيضًا معلومات إضافية حول تشغيل تطبيقات أخرى في القسم الخاص بإرسال المستخدم إلى تطبيق آخر.
الشكل 2: مربّع حوار أداة اختيار
فرض استخدام أداة اختيار التطبيقات
عندما يكون هناك أكثر من تطبيق يستجيب لـ implicit intent، يمكن للمستخدم اختيار التطبيق الذي يريد استخدامه وتعيينه كخيار تلقائي لتنفيذ الإجراء. تكون إمكانية اختيار تطبيق تلقائي مفيدة عند تنفيذ إجراء من المحتمل أن يريد المستخدم استخدام التطبيق نفسه في كل مرة، مثل فتح صفحة ويب (يفضّل المستخدمون غالبًا متصفّح ويب واحدًا فقط).
ومع ذلك، إذا كان بإمكان تطبيقات متعددة الاستجابة للغرض، وكان المستخدم يريد استخدام تطبيق مختلف في كل مرة، عليك عرض مربّع حوار اختيار بشكل صريح. يطلب مربّع حوار أداة الاختيار من المستخدم تحديد التطبيق الذي يريد استخدامه لتنفيذ الإجراء (لا يمكن للمستخدم تحديد تطبيق تلقائي لتنفيذ الإجراء). على سبيل المثال، عندما ينفّذ تطبيقك الإجراء "مشاركة" باستخدام ACTION_SEND، قد يريد المستخدمون المشاركة باستخدام تطبيق مختلف حسب حالتهم الحالية، لذا عليك دائمًا استخدام مربّع حوار أداة الاختيار، كما هو موضّح في الشكل 2.
لعرض أداة الاختيار، أنشئ Intent باستخدام createChooser() ومرِّرها إلى startActivity()، كما هو موضّح في المثال التالي.
يعرض هذا المثال مربّع حوار يتضمّن قائمة بالتطبيقات التي تستجيب للغرض الذي تم تمريره إلى الطريقة createChooser()، ويستخدم النص المقدَّم كعنوان لمربّع الحوار.
Kotlin
val sendIntent = Intent(Intent.ACTION_SEND) ... // Always use string resources for UI text. // This says something like "Share this photo with" val title: String = resources.getString(R.string.chooser_title) // Create intent to show the chooser dialog val chooser: Intent = Intent.createChooser(sendIntent, title) // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(packageManager) != null) { startActivity(chooser) }
Java
Intent sendIntent = new Intent(Intent.ACTION_SEND); ... // Always use string resources for UI text. // This says something like "Share this photo with" String title = getResources().getString(R.string.chooser_title); // Create intent to show the chooser dialog Intent chooser = Intent.createChooser(sendIntent, title); // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
رصد عمليات تشغيل الأهداف غير الآمنة
قد يطلق تطبيقك أهدافًا للتنقّل بين المكوّنات داخل تطبيقك، أو لتنفيذ إجراء نيابةً عن تطبيق آخر. ولتحسين أمان النظام الأساسي، يوفّر الإصدار 12 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 31) والإصدارات الأحدث ميزة لتصحيح الأخطاء تحذّرك إذا كان تطبيقك يطلق هدفًا بطريقة غير آمنة. على سبيل المثال، قد ينفّذ تطبيقك عملية تشغيل غير آمنة لرسالة intent متداخلة، وهي رسالة intent يتم تمريرها كبيانات إضافية في رسالة intent أخرى.
إذا كان تطبيقك ينفّذ الإجراءَين التاليَين، سيرصد النظام عملية تشغيل غير آمنة لغرض ، وسيحدث انتهاك StrictMode:
- يفكّ تطبيقك حزمة هدف متداخل من الإضافات الخاصة بهدف تم تسليمه.
- يبدأ تطبيقك على الفور أحد مكونات التطبيق باستخدام الغرض المتداخل، مثل تمرير الغرض إلى
startActivity()أوstartService()أوbindService().
لمزيد من التفاصيل حول كيفية تحديد هذه الحالة وإجراء تغييرات على تطبيقك، يُرجى قراءة مشاركة المدوّنة حول تداخل النوايا في Android على Medium.
التحقّق من عمليات إطلاق النية غير الآمنة
للتحقّق من عمليات تشغيل النوايا غير الآمنة في تطبيقك، استدعِ الدالة
detectUnsafeIntentLaunch()
عند إعداد VmPolicy، كما هو موضّح في مقتطف الرمز البرمجي التالي. إذا رصد تطبيقك مخالفة لسياسة StrictMode، قد تحتاج إلى إيقاف تنفيذ التطبيق لحماية المعلومات التي قد تكون حساسة.
Kotlin
fun onCreate() { StrictMode.setVmPolicy(VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()) }
Java
protected void onCreate() { StrictMode.setVmPolicy(new VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()); }
استخدام النوايا بشكل أكثر مسؤولية
لتقليل فرصة إطلاق نية غير آمنة وحدوث انتهاك StrictMode، اتّبِع أفضل الممارسات التالية.
انسخ الإضافات الأساسية فقط ضمن الأهداف، ونفِّذ أي عمليات تنظيف وتحقّق ضرورية. قد ينسخ تطبيقك البيانات الإضافية من غرض إلى غرض آخر يُستخدَم لتشغيل مكوّن جديد. يحدث ذلك عندما يطلب تطبيقك
putExtras(Intent)
أو
putExtras(Bundle).
إذا كان تطبيقك ينفّذ إحدى هذه العمليات، انسخ فقط البيانات الإضافية التي يتوقّعها المكوّن المستلِم. إذا كان الغرض الآخر (الذي يتلقّى النسخة) يشغّل مكونًا غير مصدَّر، يجب تنظيف البيانات الإضافية والتحقّق من صحتها قبل نسخها إلى الغرض الذي يشغّل المكوّن.
لا تصدِّر مكوّنات تطبيقك بدون داعٍ. على سبيل المثال، إذا كنت تنوي تشغيل أحد مكوّنات التطبيق باستخدام هدف متداخل داخلي، اضبط السمة android:exported لهذا المكوّن على false.
استخدِم PendingIntent بدلاً من
نية متداخلة. بهذه الطريقة، عندما يفك تطبيق آخر حزمة PendingIntent التي تتضمّن Intent، يمكن للتطبيق الآخر تشغيل PendingIntent باستخدام هوية تطبيقك. يتيح هذا الإعداد للتطبيق الآخر تشغيل أي مكوّن بأمان، بما في ذلك مكوّن غير مُصدَّر، في تطبيقك.
يوضّح المخطّط في الشكل 2 كيف ينقل النظام التحكّم من تطبيقك (العميل) إلى تطبيق آخر (الخدمة)، ثم يعيده إلى تطبيقك:
- ينشئ تطبيقك رسالة intent تستدعي نشاطًا في تطبيق آخر. وضمن رسالة intent هذه، يمكنك إضافة عنصر
PendingIntentكبيانات إضافية. تستدعي intent مؤجّلة هذه أحد المكوّنات في تطبيقك، ولكن لم يتم تصدير هذا المكوّن. - عند تلقّي هدف تطبيقك، يستخرج التطبيق الآخر العنصر
PendingIntentالمتداخل. - يستدعي التطبيق الآخر الطريقة
send()على العنصرPendingIntent. - بعد إعادة عنصر التحكّم إلى تطبيقك، يستدعي النظام الغرض المعلّق باستخدام سياق تطبيقك.
الشكل 2: مخطّط بياني يوضّح عملية التواصل بين التطبيقات عند استخدام نية معلّقة متداخلة
تلقّي intent ضمني
للإعلان عن الأهداف الضمنية التي يمكن أن يتلقّاها تطبيقك، عليك تحديد فلتر هدف واحد أو أكثر لكل مكوّن من مكوّنات تطبيقك باستخدام عنصر <intent-filter> في ملف البيان.
يحدّد كل intent filter نوع الأهداف التي يقبلها استنادًا إلى إجراء الهدف وبياناته وفئته. لا يرسل النظام هدفًا ضمنيًا إلى أحد مكونات تطبيقك إلا إذا كان بإمكان الهدف المرور عبر أحد فلاتر الأهداف.
ملاحظة: يتم دائمًا تسليم explicit intent إلى وجهته، بغض النظر عن أي intent filter يعرّفه المكوِّن.
يجب أن يحدّد أحد مكوّنات التطبيق فلاتر منفصلة لكل مهمة فريدة يمكنه تنفيذها.
على سبيل المثال، قد يتضمّن أحد الأنشطة في تطبيق معرض الصور فلترَين: فلتر
لعرض صورة، وفلتر آخر لتعديل صورة. عندما يبدأ النشاط، يفحص Intent ويقرّر كيفية التصرف استنادًا إلى المعلومات الواردة في Intent (مثل عرض عناصر تحكّم المحرّر أو عدم عرضها).
يتم تحديد كل intent filter من خلال عنصر <intent-filter> في ملف بيان التطبيق، ويكون هذا العنصر مضمّنًا في مكوّن التطبيق ذي الصلة (مثل عنصر <activity>).
في كل مكوّن تطبيق يتضمّن عنصر <intent-filter>، اضبط قيمة android:exported بشكل صريح.
توضّح هذه السمة ما إذا كان بإمكان التطبيقات الأخرى الوصول إلى مكوّن التطبيق. في بعض الحالات، مثل الأنشطة التي تتضمّن فلاتر الأهداف الخاصة بها الفئة LAUNCHER، يكون من المفيد ضبط هذه السمة على true. وفي ما عدا ذلك،
من الأفضل ضبط هذه السمة على false.
تحذير: إذا كان أحد الأنشطة أو الخدمات أو مستقبِلات البث في تطبيقك يستخدم فلاتر الأهداف ولم يضبط قيمة android:exported بشكل صريح، لن يكون بإمكانك تثبيت تطبيقك على جهاز يعمل بالإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث.
داخل <intent-filter>،
يمكنك تحديد نوع النوايا التي تريد قبولها باستخدام عنصر واحد أو أكثر
من العناصر الثلاثة التالية:
<action>- تُعلن عن قبول إجراء الهدف، وذلك في السمة
name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية لإجراء، وليس الثابت الخاص بالفئة. <data>- تحدّد هذه السمة نوع البيانات المقبولة، وذلك باستخدام سمة واحدة أو أكثر تحدّد جوانب مختلفة من معرّف URI الخاص بالبيانات (
schemeوhostوportوpath) ونوع MIME. <category>- تُعلن عن فئة الغرض المقبولة في السمة
name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية للإجراء، وليس الثابت الخاص بالفئة.ملاحظة: لتلقّي implicit intents، يجب تضمين الفئة
CATEGORY_DEFAULTفي intent filter. تتعامل الطريقتانstartActivity()وstartActivityForResult()مع جميع الأهداف على أنّها تعرّف الفئةCATEGORY_DEFAULT. إذا لم تحدّد هذه الفئة في فلتر الأهداف، لن يتم حل أي أهداف ضمنية في نشاطك.
على سبيل المثال، إليك بيان نشاط يتضمّن فلتر أهداف لتلقّي هدف ACTION_SEND عندما يكون نوع البيانات نصًا:
<activity android:name="ShareActivity" android:exported="false"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
يمكنك إنشاء فلتر يتضمّن أكثر من مثيل واحد من
<action> أو
<data> أو
<category>.
وفي حال فعلت ذلك، عليك التأكّد من أنّ المكوّن يمكنه التعامل مع أي مجموعة من عناصر الفلتر هذه.
عندما تريد التعامل مع أنواع متعددة من الأهداف، ولكن فقط في مجموعات محددة من نوع الإجراء والبيانات والفئة، عليك إنشاء فلاتر أهداف متعددة.
يتم اختبار النية الضمنية مقابل فلتر من خلال مقارنة النية بكل عنصر من العناصر الثلاثة. ولكي يتم تسليم الغرض إلى المكوّن، يجب أن يجتاز الغرض جميع الاختبارات الثلاثة. وإذا لم يتمكّن من مطابقة أحدها، لن يرسل نظام Android الغرض إلى المكوّن. ومع ذلك، بما أنّ المكوّن قد يتضمّن فلاتر متعددة للأهداف، فإنّ الهدف الذي لا يمر عبر أحد فلاتر المكوّن قد يمر عبر فلتر آخر. يمكنك الاطّلاع على مزيد من المعلومات حول كيفية حلّ النظام للأهداف في القسم أدناه حول حلّ الأهداف.
تنبيه: لا يُعد استخدام intent filter طريقة آمنة لمنع التطبيقات الأخرى من بدء مكوناتك. على الرغم من أنّ فلاتر الأهداف تحصر استجابة أحد المكوّنات بأنواع معيّنة فقط من الأهداف الضمنية، يمكن لتطبيق آخر بدء تشغيل مكوّن تطبيقك باستخدام هدف صريح إذا حدّد المطوّر أسماء المكوّنات.
إذا كان من المهم أن يتمكّن تطبيقك فقط من بدء أحد مكوّناتك، لا تعرِّف فلاتر الأهداف في ملف البيان. بدلاً من ذلك، اضبط السمة
exported على "false" لهذا المكوّن.
وبالمثل، لتجنُّب تشغيل Service لتطبيق آخر عن غير قصد، استخدِم دائمًا غرضًا ضمنيًا لبدء خدمتك.
ملاحظة:
بالنسبة إلى جميع الأنشطة، يجب الإفصاح عن فلاتر الأهداف في ملف البيان.
ومع ذلك، يمكن تسجيل فلاتر لأجهزة استقبال البث بشكل ديناميكي من خلال استدعاء registerReceiver(). يمكنك بعد ذلك إلغاء تسجيل جهاز الاستقبال من خلال التواصل مع unregisterReceiver(). ويتيح ذلك لتطبيقك الاستماع إلى عمليات بث معيّنة خلال فترة زمنية محدّدة فقط أثناء تشغيل تطبيقك.
أمثلة على الفلاتر
لتوضيح بعض سلوكيات intent filter، إليك مثال من ملف البيان لتطبيق مشاركة على وسائل التواصل الاجتماعي:
<activity android:name="MainActivity" android:exported="true"> <!-- This activity is the main entry, should appear in app launcher --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="ShareActivity" android:exported="false"> <!-- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
النشاط الأول، MainActivity، هو نقطة الدخول الرئيسية للتطبيق، أي النشاط الذي يتم فتحه عندما يشغّل المستخدم التطبيق لأول مرة باستخدام رمز مشغّل التطبيقات:
- يشير الإجراء
ACTION_MAINإلى أنّ هذا هو نقطة الدخول الرئيسية ولا يتوقّع أي بيانات Intent. - تشير الفئة
CATEGORY_LAUNCHERإلى أنّه يجب وضع رمز هذا النشاط في مشغّل التطبيقات بالنظام. إذا لم يحدّد العنصر<activity>رمزًا باستخدامicon، سيستخدم النظام الرمز من العنصر<application>.
يجب ربط هذين العنصرين معًا لكي يظهر النشاط في مشغّل التطبيقات.
يهدف النشاط الثاني، ShareActivity، إلى تسهيل مشاركة النصوص ومحتوى الوسائط. على الرغم من أنّ المستخدمين قد يصلون إلى هذا النشاط من خلال الانتقال إليه من MainActivity،
يمكنهم أيضًا الوصول إلى ShareActivity مباشرةً من تطبيق آخر يرسل هدفًا ضمنيًا
يتطابق مع أحد فلترَي الأهداف.
ملاحظة: نوع MIME،
application/vnd.google.panorama360+jpg، هو نوع بيانات خاص يحدّد
الصور البانورامية التي يمكنك التعامل معها باستخدام واجهات برمجة التطبيقات Google
panorama.
مطابقة الأهداف مع فلاتر الأهداف في التطبيقات الأخرى
إذا كان تطبيق آخر يستهدف الإصدار 13 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 33) أو الإصدارات الأحدث، يمكنه معالجة الغرض من تطبيقك فقط إذا كان الغرض يتطابق مع الإجراءات والفئات الخاصة بعنصر <intent-filter> في هذا التطبيق الآخر. وإذا لم يعثر النظام على تطابق، سيُظهر الخطأ ActivityNotFoundException.
يجب أن يتعامل تطبيق الإرسال مع هذا الاستثناء.
وبالمثل، إذا عدّلت تطبيقك ليصبح يستهدف الإصدار 13 من نظام التشغيل Android أو الإصدارات الأحدث، لن يتم تسليم أي أغراض مصدرها تطبيقات خارجية إلى أي مكوّن تم تصديره من تطبيقك إلا إذا كان الغرض يتطابق مع الإجراءات والفئات الخاصة بعنصر <intent-filter> الذي يحدّده تطبيقك. ويحدث هذا السلوك بغض النظر عن إصدار حزمة SDK المستهدَف للتطبيق المُرسِل.
في الحالات التالية، لا يتم فرض مطابقة النية:
- الأهداف التي يتم تسليمها إلى المكوّنات التي لا تحدّد أي فلاتر أهداف
- طلبات Intent التي تنشأ من داخل التطبيق نفسه
- الأهداف الصادرة من النظام، أي الأهداف التي يتم إرسالها من "معرّف المستخدم الفريد للنظام" (uid=1000). تشمل تطبيقات النظام
system_serverوالتطبيقات التي تم ضبطandroid:sharedUserIdفيها علىandroid.uid.system. - الأهداف التي تنشأ من الجذر
مزيد من المعلومات عن "مطابقة نية المستخدم"
استخدام intent مؤجّلة
عنصر PendingIntent هو برنامج تضمين لعنصر Intent. الغرض الأساسي من PendingIntent
هو منح إذن لتطبيق خارجي
باستخدام Intent المضمّن كما لو كان يتم تنفيذه من
عملية التطبيق نفسها.
تشمل حالات الاستخدام الرئيسية لـ intent مؤجّلة ما يلي:
- يتم الإعلان عن هدف سيتم تنفيذه عندما يتّخذ المستخدم إجراءً باستخدام الإشعار
(ينفّذ نظام التشغيل Android
NotificationManagerIntent). - تحديد هدف سيتم تنفيذه عندما ينفِّذ المستخدم إجراءً باستخدام أداة التطبيق (ينفِّذ تطبيق الشاشة الرئيسية
Intent). - تحديد هدف سيتم تنفيذه في وقت مستقبلي محدّد (ينفّذ نظام التشغيل Android
AlarmManagerIntent).
وكما أنّ كل عنصر Intent مصمّم ليتم التعامل معه من خلال نوع معيّن من مكوّنات التطبيق (إما Activity أو Service أو BroadcastReceiver)، يجب أيضًا إنشاء PendingIntent مع مراعاة الأمر نفسه. عند استخدام intent مؤجّلة، لا ينفّذ تطبيقك الغرض من خلال طلب مثل startActivity(). بدلاً من ذلك، يجب الإفصاح عن نوع المكوّن المقصود عند إنشاء PendingIntent من خلال استدعاء طريقة الإنشاء المعنية:
PendingIntent.getActivity()لـIntentالذي يبدأActivity-
PendingIntent.getService()مقابلIntentالذي يبدأService -
PendingIntent.getBroadcast()مقابلIntentالذي يبدأBroadcastReceiver
ما لم يكن تطبيقك يتلقّى طلبات intents معلّقة من تطبيقات أخرى،
من المحتمل أنّ تكون الطرق المذكورة أعلاه لإنشاء PendingIntent هي الطرق الوحيدة
PendingIntent التي ستحتاج إليها.
يأخذ كلّ من الطريقتين التطبيق الحالي Context وIntent الذي تريد تضمينه، وعلامة واحدة أو أكثر تحدّد كيفية استخدام الغرض (مثل ما إذا كان يمكن استخدام الغرض أكثر من مرة).
لمزيد من المعلومات حول استخدام النوايا المعلّقة، راجِع مستندات كل حالة من حالات الاستخدام المعنية، مثل الإشعارات وأدلة واجهة برمجة التطبيقات التطبيقات المصغّرة.
تحديد قابلية التغيير
إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث، عليك تحديد قابلية التغيّر لكل عنصر في دالة PendingIntent ينشئه تطبيقك. للإشارة إلى أنّ كائن PendingIntent معيّن قابل للتغيير أو غير قابل للتغيير، استخدِم العلامة PendingIntent.FLAG_MUTABLE أو PendingIntent.FLAG_IMMUTABLE على التوالي.
إذا حاول تطبيقك إنشاء عنصر PendingIntent بدون ضبط أي من علامتَي قابلية التغيير، سيُصدر النظام الخطأ IllegalArgumentException، وستظهر الرسالة التالية في Logcat:
PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
إنشاء أهداف غير قابلة للتغيير معلّقة كلما أمكن ذلك
في معظم الحالات، يجب أن ينشئ تطبيقك عناصر PendingIntent غير قابلة للتغيير، كما هو موضّح في مقتطف الرمز التالي. إذا كان العنصر PendingIntent غير قابل للتغيير،
فلا يمكن للتطبيقات الأخرى تعديل الغرض لتغيير نتيجة استدعاء الغرض.
Kotlin
val pendingIntent = PendingIntent.getActivity(applicationContext, REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE);
ومع ذلك، تتطلّب بعض حالات الاستخدام كائنات PendingIntent قابلة للتغيير بدلاً من ذلك:
- إتاحة إجراءات الردّ المباشر في الإشعارات تتطلّب الردود المباشرة إجراء تغيير على بيانات المقطع في عنصر PendingIntent المرتبط بالرد. عادةً، يمكنك طلب إجراء هذا التغيير عن طريق تمرير
FILL_IN_CLIP_DATAكعلامة إلى الطريقةfillIn(). - ربط الإشعارات بإطار عمل Android Auto باستخدام مثيلات
CarAppExtender - وضع المحادثات في فقاعات باستخدام مثيلات
PendingIntentيسمح عنصرPendingIntentقابل للتغيير للنظام بتطبيق العلامات الصحيحة، مثلFLAG_ACTIVITY_MULTIPLE_TASKوFLAG_ACTIVITY_NEW_DOCUMENT. - طلب معلومات الموقع الجغرافي للجهاز من خلال استدعاء
requestLocationUpdates()أو واجهات برمجة تطبيقات مشابهة يسمح العنصرPendingIntentالقابل للتغيير للنظام بإضافة بيانات إضافية خاصة بالأهداف تمثّل أحداث مراحل النشاط المتعلقة بالموقع الجغرافي. تشمل هذه الأحداث تغييرًا في الموقع الجغرافي وتوفّر مقدّم خدمة. - جدولة المنبّهات باستخدام
AlarmManagerيسمح عنصرPendingIntentالقابل للتغيير للنظام بإضافةEXTRA_ALARM_COUNTإلى بيانات Intent الإضافية. يمثّل هذا العنصر الإضافي عدد المرات التي تم فيها تشغيل منبّه متكرّر. من خلال تضمين هذا العنصر الإضافي، يمكن أن يرسل الغرض إشعارًا دقيقًا إلى أحد التطبيقات بشأن ما إذا تم تشغيل تنبيه متكرر عدة مرات، مثلاً عندما كان الجهاز في وضع السكون.
إذا كان تطبيقك ينشئ عنصر PendingIntent قابلاً للتغيير، ننصحك بشدة باستخدام explicit intent وملء ComponentName. بهذه الطريقة، عندما يستدعي تطبيق آخر PendingIntent ويعيد التحكّم إلى تطبيقك، يبدأ دائمًا المكوّن نفسه في تطبيقك.
استخدام الأهداف الواضحة ضمن الأهداف المعلّقة
لتحديد كيفية استخدام التطبيقات الأخرى لـ intent مؤجّلة في تطبيقك بشكل أفضل، عليك دائمًا تضمين intent مؤجّلة في explicit intent. للمساعدة في اتّباع أفضل الممارسات هذه، يُرجى اتّباع الخطوات التالية:
- تأكَّد من إدخال قيمة في حقول الإجراء والحزمة والمكوِّن في رسالة Intent الأساسية.
-
استخدِم
FLAG_IMMUTABLE، التي تمت إضافتها في Android 6.0 (المستوى 23 من واجهة برمجة التطبيقات)، لإنشاء أنشطة في انتظار المراجعة. يمنع هذا العلامة التطبيقات التي تتلقّىPendingIntentمن ملء الخصائص الفارغة. إذا كانminSdkVersionلتطبيقك هو22أو إصدار أقدم، يمكنك توفير الأمان والتوافق معًا باستخدام الرمز التالي:if (Build.VERSION.SDK_INT >= 23) { // Create a PendingIntent using FLAG_IMMUTABLE. } else { // Existing code that creates a PendingIntent. }
حلّ النية
عندما يتلقّى النظام implicit intent لبدء نشاط، يبحث عن أفضل نشاط مناسب لهذا الـ intent من خلال مقارنته بـ intent filters استنادًا إلى ثلاثة جوانب:
- الإجراء
- البيانات (المعرّف الموحّد للموارد ونوع البيانات)
- الفئة.
توضّح الأقسام التالية كيفية مطابقة intents مع المكوّنات المناسبة وفقًا لتعريف intent filter في ملف البيان الخاص بالتطبيق.
اختبار الإجراء
لتحديد intent action المقبولة، يمكن لـ intent filter تعريف صفر أو أكثر من عناصر
<action>، كما هو موضّح في المثال التالي:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>
لاجتياز هذا الفلتر، يجب أن يتطابق الإجراء المحدّد في Intent
مع أحد الإجراءات المدرَجة في الفلتر.
إذا لم يدرج الفلتر أي إجراءات، لن يكون هناك أي شيء يتطابق معه الغرض، وبالتالي ستفشل جميع الأغراض في الاختبار. ومع ذلك، إذا لم يحدّد Intent إجراءً، سيجتاز الاختبار طالما أنّ الفلتر يتضمّن إجراءً واحدًا على الأقل.
اختبار الفئة
لتحديد فئات الأهداف المقبولة، يمكن لفلتر الأهداف تعريف صفر أو أكثر من عناصر
<category>، كما هو موضّح في المثال التالي:
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter>
لكي يجتاز أحد الأهداف اختبار الفئة، يجب أن تتطابق كل فئة في Intent
مع فئة في الفلتر. وليس من الضروري أن يكون العكس صحيحًا، أي يمكن أن يحدّد intent filter فئات أكثر من تلك المحدّدة في Intent، وسيظل Intent صالحًا. لذلك، فإنّ أي intent بدون فئات
يجتاز هذا الاختبار دائمًا، بغض النظر عن الفئات المحدّدة في الفلتر.
ملاحظة:
يُطبِّق نظام التشغيل Android تلقائيًا الفئة CATEGORY_DEFAULT
على جميع النوايا الضمنية التي يتم تمريرها إلى startActivity() وstartActivityForResult().
إذا كنت تريد أن يتلقّى نشاطك أهدافًا ضمنية، يجب أن يتضمّن فئة "android.intent.category.DEFAULT" في فلاتر الأهداف، كما هو موضّح في المثال <intent-filter> السابق.
اختبار البيانات
لتحديد بيانات intent المقبولة، يمكن أن يحدّد intent filter صفرًا أو أكثر من عناصر
<data>، كما هو موضّح في المثال التالي:
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ... </intent-filter>
يمكن لكل عنصر <data>
تحديد بنية URI ونوع بيانات (نوع وسائط MIME).
كل جزء من معرّف الموارد المنتظم (URI) هو سمة منفصلة: scheme وhost وport وpath:
<scheme>://<host>:<port>/<path>
يعرض المثال التالي القيم المحتملة لهذه السمات:
content://com.example.project:200/folder/subfolder/etc
في معرّف الموارد المنتظم هذا، المخطّط هو content، والمضيف هو com.example.project، والمنفذ هو 200، والمسار هو folder/subfolder/etc.
كلّ من هذه السمات اختياري في العنصر <data>،
ولكن هناك تبعيات خطية:
- إذا لم يتم تحديد مخطط، سيتم تجاهل المضيف.
- إذا لم يتم تحديد مضيف، سيتم تجاهل المنفذ.
- إذا لم يتم تحديد كل من المخطط والمضيف، سيتم تجاهل المسار.
عند مقارنة معرّف الموارد المنتظم (URI) في هدف بمواصفات معرّف الموارد المنتظم في فلتر، تتم مقارنته فقط بأجزاء معرّف الموارد المنتظم المضمّنة في الفلتر. على سبيل المثال:
- إذا كان الفلتر يحدّد المخطط فقط، ستتطابق جميع معرّفات الموارد الموحّدة التي تستخدم هذا المخطط مع الفلتر.
- إذا حدّد فلتر مخططًا وسلطة بدون مسار، ستجتاز جميع معرّفات الموارد الموحّدة (URI) التي تتضمّن المخطط والسلطة نفسها الفلتر، بغض النظر عن مساراتها.
- إذا حدّد الفلتر مخططًا ومصدرًا ومسارًا، لن يتم اجتياز الفلتر إلا من خلال معرّفات الموارد الموحّدة التي تتضمّن المخطط والمصدر والمسار نفسهما.
ملاحظة: يمكن أن يحتوي تحديد المسار على علامة نجمة بدل (*) لطلب مطابقة جزئية فقط لاسم المسار.
يقارن اختبار البيانات كلاً من معرّف الموارد المنتظم (URI) ونوع MIME في الغرض بمعرّف موارد منتظم ونوع MIME محدّدين في الفلتر. في ما يلي القواعد:
- لا يجتاز الغرض الذي لا يحتوي على معرّف موارد منتظم (URI) أو نوع MIME الاختبار إلا إذا كان الفلتر لا يحدّد أي معرّفات موارد منتظمة أو أنواع MIME.
- لا يجتاز الاختبار أي هدف يتضمّن معرّف موارد منتظم (URI) ولكن لا يتضمّن نوع MIME (سواء كان صريحًا أو يمكن استنتاجه من معرّف الموارد المنتظم (URI)) إلا إذا كان معرّف الموارد المنتظم (URI) الخاص به يطابق تنسيق معرّف الموارد المنتظم (URI) الخاص بالفلتر، ولا يحدّد الفلتر أيضًا نوع MIME.
- تجتاز النية التي تحتوي على نوع MIME ولكن ليس على معرّف موارد منتظم (URI) الاختبار فقط إذا كانت قائمة الفلاتر تتضمّن نوع MIME نفسه ولا تحدّد تنسيق معرّف موارد منتظم (URI).
- لا يجتاز جزء نوع MIME من الاختبار إلا إذا كان هذا النوع يطابق نوعًا مدرَجًا في الفلتر. ويجتاز الجزء الخاص بمعرّف الموارد المنتظم (URI) الاختبار
إما إذا كان معرّف الموارد المنتظم الخاص به يتطابق مع معرّف موارد منتظم في الفلتر أو إذا كان يتضمّن معرّف موارد منتظم
content:أوfile:ولم يحدّد الفلتر معرّف موارد منتظم. بعبارة أخرى، يُفترض أنّ أحد المكوّنات يتوافق مع بياناتcontent:وfile:إذا كانت قوائم الفلاتر الخاصة به تتضمّن فقط نوع MIME.
ملاحظة: إذا كان الغرض يحدّد معرّف موارد منتظم (URI) أو نوع MIME، سيفشل اختبار البيانات إذا لم تتضمّن <intent-filter> أي عناصر <data>.
تعكس القاعدة الأخيرة، أي القاعدة (د)، التوقّع بأنّ المكوّنات يمكنها الحصول على بيانات محلية من ملف أو موفّر محتوى.
لذلك، يمكن أن تتضمّن فلاترها نوع بيانات فقط، ولا تحتاج إلى تسمية المخططَين content: وfile: بشكل صريح.
يوضّح المثال التالي حالة نموذجية يخبر فيها العنصر <data> نظام Android بأنّ المكوّن يمكنه الحصول على بيانات الصور من موفّر محتوى وعرضها:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
ربما تكون الفلاتر التي تحدّد نوع بيانات ولكن ليس معرّف URI هي الأكثر شيوعًا لأنّ معظم البيانات المتاحة يتم توزيعها من خلال مقدّمي المحتوى.
هناك إعداد شائع آخر وهو عامل تصفية يتضمّن مخططًا ونوع بيانات. على سبيل المثال، يوضّح العنصر <data> التالي لنظام Android أنّ المكوّن يمكنه استرداد بيانات الفيديو من الشبكة لتنفيذ الإجراء:
<intent-filter> <data android:scheme="http" android:mimeType="video/*" /> ... </intent-filter>
مطابقة النية
تتم مطابقة الأهداف مع فلاتر الأهداف ليس فقط لاكتشاف مكوِّن مستهدف لتفعيله، ولكن أيضًا لاكتشاف معلومات حول مجموعة المكوِّنات على الجهاز. على سبيل المثال، يملأ تطبيق Home مشغّل التطبيقات
من خلال العثور على جميع الأنشطة التي تتضمّن فلاتر أهداف تحدّد الإجراء
ACTION_MAIN والفئة
CATEGORY_LAUNCHER.
لا يكون التطابق ناجحًا إلا إذا كانت الإجراءات والفئات في Intent تتطابق مع الفلتر، كما هو موضّح في مستندات فئة IntentFilter.
يمكن لتطبيقك استخدام ميزة مطابقة الأهداف بطريقة مشابهة لما يفعله تطبيق Home.
يحتوي PackageManager على مجموعة من الطرق query...()
التي تعرض جميع المكوّنات التي يمكنها قبول غرض معيّن،
وسلسلة مشابهة من طرق resolve...() التي تحدّد أفضل
مكوّن للرد على غرض. على سبيل المثال، يعرض queryIntentActivities() قائمة بجميع الأنشطة التي يمكنها تنفيذ الغرض الذي تم تمريره كوسيطة، ويعرض queryIntentServices() قائمة مشابهة بالخدمات.
ولا تؤدي أي من الطريقتين إلى تفعيل المكوّنات، بل تعرضان فقط المكوّنات التي يمكنها الاستجابة. تتوفّر طريقة مشابهة،
queryBroadcastReceivers()، لمستقبِلات البث.