فلاتر الأهداف والنية

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 كامل. ستستخدم عادةً explicit 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 filter، يبدأ النظام هذا المكوّن ويسلّمه الكائن Intent. إذا كانت هناك عدة فلاتر أهداف متوافقة، يعرض النظام مربّع حوار ليتمكّن المستخدم من اختيار التطبيق الذي يريد استخدامه.

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

تنبيه: لضمان أمان تطبيقك، احرص دائمًا على استخدام explicit intent عند بدء Service، ولا تحدّد intent filters لخدماتك. يُعد استخدام intent ضمني لبدء خدمة من المخاطر الأمنية لأنّه لا يمكنك التأكّد من الخدمة التي ستستجيب للـ intent، ولا يمكن للمستخدم معرفة الخدمة التي يتم تشغيلها. اعتبارًا من الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات)، يطرح النظام استثناءً إذا طلبت bindService() باستخدام implicit intent.

إنشاء نية

يحمل العنصر Intent معلومات يستخدمها نظام Android لتحديد المكوِّن الذي سيتم تشغيله (مثل اسم المكوِّن أو فئة المكوِّن التي يجب أن تتلقّى الـ 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 (الفئة)
سلسلة تحتوي على معلومات إضافية حول نوع المكوّن الذي يجب أن يتعامل مع الغرض. يمكن وضع أي عدد من أوصاف الفئات في intent، ولكن معظم intents لا تتطلّب فئة. في ما يلي بعض الفئات الشائعة:
CATEGORY_BROWSABLE
يسمح النشاط المستهدف بتشغيله من خلال متصفّح ويب لعرض البيانات التي يشير إليها الرابط، مثل صورة أو رسالة إلكترونية.
CATEGORY_LAUNCHER
النشاط هو النشاط الأوّلي لمهمة ويتم إدراجه في مشغّل تطبيقات النظام.

اطّلِع على وصف فئة Intent للحصول على القائمة الكاملة للفئات.

يمكنك تحديد فئة باستخدام addCategory().

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

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

يمكنك إضافة بيانات إضافية باستخدام طرق putExtra() مختلفة، يقبل كل منها مَعلمتَين: اسم المفتاح والقيمة. يمكنك أيضًا إنشاء عنصر Bundle يتضمّن كل البيانات الإضافية، ثم إدراج Bundle في Intent باستخدام putExtras().

على سبيل المثال، عند إنشاء هدف لإرسال رسالة إلكترونية باستخدام ACTION_SEND، يمكنك تحديد المستلِم to باستخدام المفتاح 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

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

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

Kotlin

// Executed in an Activity, so 'this' is the Context
// 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 the Context
// 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:

  1. يفكّ تطبيقك حزمة هدف متداخل من الإضافات الخاصة بهدف تم تسليمه.
  2. يبدأ تطبيقك على الفور أحد مكونات التطبيق باستخدام هذا الغرض المتداخل، مثل تمرير الغرض إلى 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 كيف ينقل النظام التحكّم من تطبيقك (العميل) إلى تطبيق آخر (الخدمة)، ثم يعيده إلى تطبيقك:

  1. ينشئ تطبيقك رسالة intent تستدعي نشاطًا في تطبيق آخر. وضمن رسالة intent هذه، يمكنك إضافة عنصر PendingIntent كبيانات إضافية. يستدعي رمز PendingIntent هذا أحد المكوّنات في تطبيقك، ولكن لم يتم تصدير هذا المكوّن.
  2. عند تلقّي هدف تطبيقك، يستخرج التطبيق الآخر العنصر PendingIntent المضمّن.
  3. يستدعي التطبيق الآخر الإجراء send() على العنصر PendingIntent.
  4. بعد إعادة التحكّم إلى تطبيقك، يستدعي النظام الغرض المعلّق باستخدام سياق تطبيقك.

الشكل 2: مخطّط بياني يوضّح عملية التواصل بين التطبيقات عند استخدام نية معلّقة متداخلة

تلقّي intent ضمني

للإعلان عن الأهداف الضمنية التي يمكن أن يتلقّاها تطبيقك، عليك تحديد فلتر هدف واحد أو أكثر لكل مكوّن من مكوّنات تطبيقك باستخدام عنصر <intent-filter> في ملف البيان. يحدّد كل intent filter نوع intents التي يقبلها استنادًا إلى إجراء intent وبياناته وفئته. لا يرسل النظام هدفًا ضمنيًا إلى أحد مكونات تطبيقك إلا إذا كان بإمكان الهدف المرور عبر أحد فلاتر الأهداف.

ملاحظة: يتم دائمًا تسليم 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. يجب أن تكون القيمة هي قيمة السلسلة الحرفية للإجراء، وليس الثابت الخاص بالفئة.

ملاحظة: لتلقّي الأهداف الضمنية، يجب تضمين الفئة CATEGORY_DEFAULT في فلتر الأهداف. تتعامل الطريقتان startActivity() وstartActivityForResult() مع جميع الأهداف على أنّها تعرّف الفئة CATEGORY_DEFAULT. إذا لم تحدّد هذه الفئة في intent filter، لن يتم توجيه أي implicit intent إلى نشاطك.

على سبيل المثال، إليك بيان نشاط يتضمّن فلتر intent لتلقّي intent 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 طريقة آمنة لمنع التطبيقات الأخرى من بدء مكوناتك. على الرغم من أنّ فلاتر الأهداف تحصر استجابة المكوّن بأنواع معيّنة فقط من الأهداف الضمنية، يمكن لتطبيق آخر بدء تشغيل مكوّن تطبيقك باستخدام explicit intent إذا حدّد المطوّر أسماء المكوّنات. إذا كان من المهم أن يتمكّن تطبيقك فقط من بدء أحد مكوناتك، لا تعرِّف فلاتر الأهداف في ملف البيان. بدلاً من ذلك، اضبط السمة 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.
  • الأهداف التي تنشأ من الجذر

مزيد من المعلومات عن ميزة "مطابقة الأهداف"

استخدام طلب معلّق

عنصر PendingIntent هو برنامج تضمين لعنصر Intent. الغرض الأساسي من PendingIntent هو منح إذن لتطبيق خارجي باستخدام Intent المضمّن كما لو كان يتم تنفيذه من عملية تطبيقك.

تشمل حالات الاستخدام الرئيسية لـ PendingIntent ما يلي:

  • يتم الإعلان عن هدف سيتم تنفيذه عندما يتّخذ المستخدم إجراءً باستخدام الإشعار (ينفّذ نظام التشغيل Android NotificationManager Intent).
  • يتم الإعلان عن هدف سيتم تنفيذه عندما ينفِّذ المستخدم إجراءً باستخدام أداة التطبيق (ينفِّذ تطبيق الشاشة الرئيسية Intent).
  • تحديد هدف سيتم تنفيذه في وقت مستقبلي محدّد (ينفّذ نظام التشغيل Android AlarmManager Intent).

وكما أنّ كل عنصر Intent مصمّم ليتم التعامل معه بواسطة نوع معيّن من مكوّنات التطبيق (إما Activity أو Service أو BroadcastReceiver)، يجب أيضًا إنشاء PendingIntent مع مراعاة ذلك. عند استخدام PendingIntent، لا ينفّذ تطبيقك الغرض من خلال طلب مثل startActivity(). بدلاً من ذلك، يجب تحديد نوع المكوّن المقصود عند إنشاء PendingIntent من خلال استدعاء طريقة الإنشاء المعنية:

ما لم يكن تطبيقك يتلقّى طلبات 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 القابل للتغيير للنظام بإضافة إضافات intent تمثّل أحداث مراحل النشاط المتعلقة بالموقع الجغرافي. وتشمل هذه الأحداث تغييرًا في الموقع الجغرافي وتوفُّر مقدّم خدمة.
  • جدولة المنبّهات باستخدام AlarmManager يسمح العنصر القابل للتغيير PendingIntent للنظام بإضافة EXTRA_ALARM_COUNT إلى بيانات Intent الإضافية. يمثّل هذا العنصر الإضافي عدد المرات التي تم فيها تشغيل منبّه متكرّر. من خلال تضمين هذا العنصر الإضافي، يمكن أن يرسل الغرض إشعارًا دقيقًا إلى التطبيق بشأن ما إذا تم تشغيل تنبيه متكرر عدة مرات، مثلاً عندما كان الجهاز في وضع السكون.

إذا كان تطبيقك ينشئ عنصر PendingIntent قابلاً للتغيير، ننصحك بشدة باستخدام explicit intent وتعبئة ComponentName. بهذه الطريقة، عندما يستدعي تطبيق آخر PendingIntent ويعيد التحكّم إلى تطبيقك، يبدأ دائمًا المكوّن نفسه في تطبيقك.

استخدام الأهداف الضمنية في الأهداف المعلّقة

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

  1. تأكَّد من إدخال قيمة في حقول الإجراء والحزمة والمكوِّن في رسالة Intent الأساسية.
  2. استخدِم طريقة 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 إجراءً، سيجتاز الاختبار طالما أنّ الفلتر يحتوي على إجراء واحد على الأقل.

اختبار الفئة

لتحديد فئات 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) في هدف (Intent) بمواصفات معرّف الموارد المنتظم في فلتر، تتم مقارنته فقط بأجزاء معرّف الموارد المنتظم المضمّنة في الفلتر. على سبيل المثال:

  • إذا كان الفلتر يحدّد المخطط فقط، ستتطابق جميع معرّفات URI التي تستخدم هذا المخطط مع الفلتر.
  • إذا حدّد فلتر مخططًا وسلطة بدون مسار، ستجتاز جميع معرّفات الموارد الموحّدة (URI) التي تتضمّن المخطط والسلطة نفسها الفلتر، بغض النظر عن مساراتها.
  • إذا حدّد الفلتر مخططًا ومصدرًا ومسارًا، لن تجتاز الفلتر سوى معرّفات الموارد الموحّدة التي تتضمّن المخطط والمصدر والمسار نفسهما.

ملاحظة: يمكن أن يحتوي تحديد المسار على علامة نجمة (*) كحرف بدل لطلب مطابقة جزئية فقط لاسم المسار.

يقارن اختبار البيانات كلاً من معرّف الموارد المنتظم (URI) ونوع MIME في الغرض بمعرّف موارد منتظم (URI) ونوع MIME محدّدين في الفلتر. القواعد هي كما يلي:

  1. لا يجتاز الغرض الذي لا يحتوي على معرّف موارد منتظم (URI) أو نوع MIME الاختبار إلا إذا كان الفلتر لا يحدّد أي معرّفات موارد منتظمة أو أنواع MIME.
  2. لا يجتاز الاختبار أي غرض يحتوي على معرّف موارد منتظم (URI) ولكن لا يحتوي على نوع MIME (لا صريح ولا يمكن استنتاجه من معرّف الموارد المنتظم (URI)) إلا إذا كان معرّف الموارد المنتظم (URI) الخاص به يطابق تنسيق معرّف الموارد المنتظم (URI) الخاص بالفلتر، وبالمثل إذا لم يحدّد الفلتر نوع MIME.
  3. يجتاز الغرض الذي يحتوي على نوع MIME ولكن ليس على معرّف الموارد المنتظم (URI) الاختبار فقط إذا كانت قائمة الفلاتر تتضمّن نوع MIME نفسه ولا تحدّد تنسيق معرّف الموارد المنتظم (URI).
  4. تجتاز النية التي تحتوي على معرّف موارد منتظم (URI) ونوع MIME (إما صريح أو يمكن استنتاجه من معرّف الموارد المنتظم) جزء نوع MIME من الاختبار فقط إذا كان هذا النوع يطابق نوعًا مدرجًا في الفلتر. ويجتاز الجزء الخاص بمعرّف الموارد المنتظم (URI) الاختبار إما إذا كان معرّف الموارد المنتظم (URI) الخاص به يتطابق مع معرّف موارد منتظم (URI) في الفلتر أو إذا كان يتضمّن معرّف موارد منتظم (URI) من النوع content: أو file: ولم يحدّد الفلتر معرّف موارد منتظم (URI). بعبارة أخرى، يُفترض أنّ أحد المكوّنات يتوافق مع بيانات content: وfile: إذا كانت قوائم الفلاتر الخاصة به تتضمّن فقط نوع MIME.

ملاحظة: إذا كان الغرض يحدّد معرّف موارد منتظم (URI) أو نوع MIME، سيفشل اختبار البيانات إذا لم تتضمّن <intent-filter> أي عناصر <data>.

تعكس القاعدة الأخيرة، أي القاعدة (د)، التوقّع بأنّ المكوّنات يمكنها الحصول على بيانات محلية من ملف أو موفّر محتوى. لذلك، يمكن أن تتضمّن فلاترها نوع بيانات فقط، ولا تحتاج إلى تسمية المخططَين content: وfile: بشكل صريح. يوضّح المثال التالي حالة نموذجية يخبر فيها العنصر <data> نظام Android بأنّ المكوّن يمكنه الحصول على بيانات الصور من موفّر محتوى وعرضها:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

إنّ الفلاتر التي تحدّد نوع بيانات ولكن ليس معرّف موارد موحّدًا هي الأكثر شيوعًا، لأنّ معظم البيانات المتاحة يتم توزيعها من خلال مقدّمي المحتوى.

هناك إعداد شائع آخر وهو عامل تصفية يتضمّن مخططًا ونوع بيانات. على سبيل المثال، يوضّح العنصر <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()، لمستقبِلات البث.