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

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

  • بدء نشاط

    يمثّل Activity شاشة واحدة في أحد التطبيقات. ويمكنك بدء مثيل جديد من Activity من خلال تمرير Intent إلى startActivity(). تصف السمة Intent النشاط للبدء ويحمل أي بيانات ضرورية.

    إذا كنت تريد تلقّي نتيجة من النشاط عند انتهائه، يُرجى الاتصال بـ startActivityForResult(). يتلقّى نشاطك النتيجة كعنصر Intent منفصل في استدعاء onActivityResult() الخاص بنشاطك. للمزيد من المعلومات، يُرجى الاطّلاع على دليل الأنشطة.

  • بدء خدمة

    Service هو مكوِّن ينفذ عمليات في الخلفية بدون واجهة مستخدم. باستخدام نظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يمكنك بدء خدمة باستخدام JobScheduler. لمزيد من المعلومات عن "JobScheduler"، يمكنك الاطّلاع على API-reference documentation.

    بالنسبة إلى الإصدارات الأقدم من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، يمكنك بدء خدمة باستخدام أساليب الفئة Service. يمكنك بدء خدمة لتنفيذ عملية لمرة واحدة (مثل تنزيل ملف) من خلال تمرير Intent إلى startService(). ويصف Intent الخدمة للبدء وينقل أي بيانات ضرورية.

    إذا تم تصميم الخدمة باستخدام واجهة خادم عميل، يمكنك الربط بالخدمة من مكوِّن آخر من خلال تمرير Intent إلى bindService(). لمزيد من المعلومات، يُرجى الاطّلاع على دليل الخدمات.

  • إرسال بث

    الإعلان على جميع الأجهزة هو رسالة يمكن لأي تطبيق استقبالها. يقدم النظام عمليات بث متنوعة لأحداث النظام، مثل عند تشغيل النظام أو بدء شحن الجهاز. يمكنك إرسال بث إلى التطبيقات الأخرى من خلال إدخال Intent إلى sendBroadcast() أو sendOrderedBroadcast().

يشرح الجزء المتبقي من هذه الصفحة آلية عمل النية وكيفية استخدامها. للحصول على معلومات ذات صلة، يمكنك الاطّلاع على قسمَي التفاعل مع التطبيقات الأخرى ومشاركة المحتوى.

أنواع النية

هناك نوعان من الأغراض:

  • تحدد الأغراض الصريحة التطبيق الذي سيلبي الغرض من خلال تقديم اسم حزمة التطبيق المستهدف أو اسم فئة مكون مؤهل بالكامل. ستستخدم عادةً هدفًا صريحًا لبدء مكون في تطبيقك الخاص، لأنك تعرف اسم فئة النشاط أو الخدمة التي تريد البدء بها. على سبيل المثال، يمكنك بدء نشاط جديد داخل تطبيقك استجابةً لإجراءٍ ما يجريه المستخدم، أو بدء خدمة لتنزيل ملف في الخلفية.
  • لا تسمي الأهداف الضمنية مكوِّنًا معيّنًا، بل تشير بدلاً من ذلك إلى إجراء عام ينبغي تنفيذه، ما يسمح لمكوِّن من تطبيق آخر بالتعامل مع هذا الإجراء. على سبيل المثال، إذا كنت تريد أن تعرض للمستخدم موقعًا على الخريطة، فيمكنك استخدام هدف ضمني لطلب أن يعرض تطبيق آخر مؤهّل موقعًا محددًا على الخريطة.

يوضّح الشكل 1 كيفية استخدام الغرض عند بدء أحد الأنشطة. عندما يسمي الكائن Intent مكوِّن نشاط محدّد صراحةً، يبدأ النظام هذا المكوِّن على الفور.

الشكل 1. كيف يتم تقديم هدف ضمني من خلال النظام لبدء نشاط آخر: [1] ينشئ النشاط أ Intent مع وصف للإجراء ويمرره إلى startActivity(). [2] يبحث نظام Android في جميع التطبيقات عن فلتر أهداف يتطابق مع الهدف. عند العثور على مطابقة، [3] يبدأ النظام نشاط المطابقة (النشاط ب) من خلال استدعاء طريقة onCreate() وتمرير Intent.

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

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

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

تعزيز النية

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

في ما يلي المعلومات الأساسية التي يتضمّنها Intent:

اسم المكوّن
اسم المكوِّن للبدء.

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

ملاحظة: عند بدء Service، حدِّد اسم المكوِّن دائمًا. وبخلاف ذلك، لا يمكنك التأكد من الخدمة التي ستستجيب للقصد، ولا يمكن للمستخدم معرفة الخدمة التي تبدأ.

ويمثّل هذا الحقل من Intent كائن ComponentName، ويمكنك تحديده باستخدام اسم فئة مؤهل بالكامل للمكوِّن المستهدف، بما في ذلك اسم حزمة التطبيق، مثل com.example.ExampleActivity. يمكنك ضبط اسم المكوِّن باستخدام setComponent() أو setClass() أو setClassName() أو باستخدام الدالة الإنشائية Intent.

الإجراء
سلسلة تحدّد الإجراء العام المطلوب تنفيذه (مثل view أو pick)

وفي حالة النية من البث، هذا هو الإجراء الذي حدث ويتم الإبلاغ عنه. يحدد الإجراء إلى حد كبير كيفية تنظيم بقية الغرض، وخاصة المعلومات التي تتضمنها البيانات والإضافات.

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

ACTION_VIEW
استخدِم هذا الإجراء في هدف بالاستناد إلى startActivity() عندما يكون لديك بعض المعلومات التي يمكن أن يعرضها نشاط للمستخدم، مثل صورة يمكن عرضها في تطبيق معرض أو عنوان لعرضه في تطبيق خرائط.
ACTION_SEND
تُعرف أيضًا باسم هدف المشاركة، ويجب استخدامها في الغرض من خلال 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) الخاص به. على سبيل المثال، قد لا يتمكن النشاط الذي يمكنه عرض الصور من تشغيل ملف صوتي، على الرغم من أن تنسيقات معرف الموارد المنتظم (URI) قد تكون متشابهة. يساعد تحديد نوع MIME لبياناتك نظام Android في العثور على أفضل مكوّن لتلقّي الغرض. ومع ذلك، يمكن أحيانًا استنتاج نوع MIME من معرّف الموارد المنتظم (URI)، خاصةً عندما تكون البيانات هي معرّف الموارد المنتظم (URI) content:. يشير معرّف الموارد المنتظم (URI) content: إلى أنّ البيانات متوفّرة على الجهاز ويتم التحكّم فيها من خلال ContentProvider، ما يجعل البيانات من نوع MIME مرئية للنظام.

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

تنبيه: إذا كنت تريد ضبط كلّ من معرّف الموارد المنتظم (URI) ونوع MIME، لا تستدعي setData() و setType() لأنّ كل منهما يُبطل قيمة الآخر. استخدِم setDataAndType() دائمًا لضبط كل من نوع URI ونوع MIME.

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

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

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

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

العناصر الإضافية
أزواج المفتاح/القيمة التي تتضمن المعلومات الإضافية المطلوبة لتنفيذ الإجراء المطلوب. مثلما تستخدم بعض الإجراءات أنواعًا معينة من معرفات الموارد المنتظمة (URI) للبيانات، تستخدم بعض الإجراءات أيضًا عناصر إضافية معينة.

يمكنك إضافة بيانات إضافية باستخدام طرق 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().

مثال على نية صريحة

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

لمزيد من المعلومات حول إنشاء خدمة وتشغيلها، اطّلِع على دليل الخدمات.

مثال على النية الضمنية

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

على سبيل المثال، إذا كان لديك محتوى تريد أن يشاركه المستخدم مع مستخدمين آخرين، أنشئ هدفًا باستخدام إجراء 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 وتتضمّن بيانات "نصية أو عادية"). إذا كان هناك تطبيق واحد فقط يمكنه التعامل مع هذه المشكلة، فسيتم فتح التطبيق على الفور ويتم إعطاؤه الهدف. إذا لم تتمكن أي تطبيقات أخرى من معالجة هذه المشكلة، يمكن لتطبيقك رصد ActivityNotFoundException الذي يحدث. إذا قبلت أنشطة متعددة الهدف، يعرض النظام مربع حوار مثل ذلك المعروض في الشكل 2، حتى يتمكن المستخدم من اختيار التطبيق المراد استخدامه.

يتم أيضًا توفير مزيد من المعلومات حول تشغيل تطبيقات أخرى في الدليل حول إرسال المستخدم إلى تطبيق آخر.

الشكل 2. مربّع حوار أداة الاختيار

فرض أداة اختيار التطبيق

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

ومع ذلك، إذا كان بإمكان تطبيقات متعددة الاستجابة لهدف البحث وكان المستخدم يريد استخدام تطبيق مختلف في كل مرة، يجب عرض مربع حوار أداة الاختيار بوضوح. يطلب مربع حوار المحدد من المستخدم تحديد التطبيق الذي سيستخدمه في الإجراء (لا يمكن للمستخدم اختيار تطبيق افتراضي للإجراء). على سبيل المثال، عندما ينفّذ تطبيقك "المشاركة" من خلال إجراء 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);
}

رصد عمليات إطلاق النية غير الآمنة

قد يشغِّل تطبيقك عناصر intent للتنقل بين المكوّنات داخل تطبيقك أو لتنفيذ إجراء نيابةً عن تطبيق آخر. لتحسين أمان النظام الأساسي، يوفّر الإصدار Android 12 (المستوى 31 من واجهة برمجة التطبيقات) والإصدارات الأحدث ميزة تصحيح الأخطاء التي تحذّرك إذا كان تطبيقك ينفِّذ إطلاقًا غير آمن لـ intent. على سبيل المثال، قد ينفِّذ تطبيقك عملية إطلاق غير آمنة لهدف متداخل، وهو هدف يتم تمريره كهدف إضافي في هدف آخر.

إذا نفَّذ تطبيقك كلا الإجراءين التاليَين، سيرصد النظام عملية إطلاق نية غير آمنة، ويحدث انتهاك للسياسة StrictMode:

  1. لا يوفّر تطبيقك غرضًا مدمجًا من العناصر الإضافية لهدف تم تسليمه.
  2. يبدأ تطبيقك على الفور مكوّن تطبيق باستخدام هذا الغرض المدمج، مثل تمرير الغرض إلى startActivity()، startService()، أو bindService().

للحصول على مزيد من التفاصيل حول كيفية تحديد هذا الموقف وإجراء تغييرات على تطبيقك، يُرجى قراءة مشاركة المدونة حول Android Nesting Intents على Medium.

التحقّق من عمليات إطلاق النية غير الآمنة

للتحقّق من عمليات إطلاق النية غير الآمنة في تطبيقك، يُرجى الاتصال بالرقم detectUnsafeIntentLaunch() عند ضبط VmPolicy، كما هو موضّح في مقتطف الرمز التالي. إذا اكتشف تطبيقك انتهاكًا صارمًا للوضع المقيّد، فقد ترغب في إيقاف تنفيذ التطبيق لحماية المعلومات التي يحتمل أن تكون حساسة.

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. ينشئ تطبيقك غرضًا يستدعي نشاطًا في تطبيق آخر. وضمن هذا الغرض، تتم إضافة كائن PendingIntent كعنصر إضافي. يستدعي هذا الغرض المعلق مكونًا في تطبيقك؛ ولا يتم تصدير هذا المكون.
  2. بعد تلقّي الغرض من التطبيق، يستخرج التطبيق الآخر عنصر PendingIntent المدمج.
  3. يستدعي التطبيق الآخر الإجراء send() في الكائن PendingIntent.
  4. بعد إعادة ضبط عنصر التحكّم إلى تطبيقك، يستدعي النظام النية المعلَّقة باستخدام سياق تطبيقك.

الشكل 2. رسم تخطيطي للاتصال بين التطبيقات عند استخدام غرض مدمج في انتظار المراجعة.

تلقي هدف ضمني

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

ملاحظة: يتم دائمًا تسليم الغرض الصريح إلى هدفه، بغض النظر عن أي فلاتر أهداف يذكرها المكوِّن.

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

يتم تحديد كل فلتر أهداف من خلال عنصر <intent-filter> في ملف البيان الخاص بالتطبيق، ويكون مدمجًا في المكوِّن المقابل في التطبيق (مثل عنصر <activity>).

في كل مكون من مكونات التطبيق التي تشتمل على عنصر <intent-filter>، حدِّد قيمة بوضوح لـ android:exported. تحدد هذه السمة ما إذا كان يمكن للتطبيقات الأخرى الوصول إلى مكوِّن التطبيق. في بعض الحالات، مثل الأنشطة التي تتضمّن فلاتر الأهداف فيها الفئة LAUNCHER، من المفيد ضبط هذه السمة على true. بخلاف ذلك، يكون ضبط هذه السمة أكثر أمانًا على false.

تحذير: إذا كان هناك نشاط أو خدمة أو جهاز استقبال بث في تطبيقك يستخدم فلاتر الأهداف ولم يحدِّد قيمة android:exported بشكل صريح، لا يمكن تثبيت تطبيقك على جهاز يعمل بنظام التشغيل Android 12 أو إصدار أحدث.

داخل <intent-filter>، يمكنك تحديد نوع الأغراض المطلوب قبولها باستخدام عنصر واحد أو أكثر من العناصر الثلاثة التالية:

<action>
تشير إلى الغرض من الإجراء المقبول في السمة name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية لأحد الإجراءات، وليست قيمة ثابتة الفئة.
<data>
يحدد هذا الإعداد نوع البيانات المقبولة، باستخدام سمة واحدة أو أكثر تحدد جوانب مختلفة من معرّف الموارد المنتظم (URI) للبيانات (scheme وhost وport وpath) ونوع MIME.
<category>
تشير إلى فئة الغرض المقبولة في السمة name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية لأحد الإجراءات، وليست قيمة ثابتة الفئة.

ملاحظة: لتلقّي الأغراض الضمنية، عليك تضمين الفئة CATEGORY_DEFAULT في فلتر الأهداف. تتعامل الطريقتان 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 على تسليم الغرض إلى المكوِّن. ومع ذلك، ونظرًا لأن المكوِّن قد يحتوي على فلاتر أهداف متعددة، فإن الغرض الذي لا يمر عبر أحد فلاتر المكون قد ينجح في فلتر آخر. يتوفر مزيد من المعلومات حول كيفية حل النظام للأهداف في القسم أدناه حول حل الأهداف.

تحذير: لا يُعدّ استخدام فلتر أهداف طريقة آمنة لمنع التطبيقات الأخرى من بدء تشغيل المكوّنات. على الرغم من أن فلاتر الأهداف تحظر مكوّنًا للاستجابة إلى أنواع معيّنة فقط من الأغراض الضمنية، من المحتمل أن يبدأ تطبيق آخر مكوّن تطبيقك باستخدام هدف صريح في حال حدّد المطوّر أسماء المكوّنات. إذا كان من المهم أن يتمكّن تطبيقك الخاص فقط من بدء تشغيل أحد المكوّنات، لا تُفصح عن فلاتر الأهداف في البيان. بدلاً من ذلك، اضبط السمة exported على "false" لهذا المكوّن.

وبالمثل، لتجنُّب تشغيل Service لتطبيق آخر بدون قصد . عليك دائمًا استخدام هدف صريح لبدء الخدمة الخاصة بك.

ملاحظة: بالنسبة إلى جميع الأنشطة، عليك الإفصاح عن فلاتر الأهداف في ملف البيان. ومع ذلك، يمكن تسجيل فلاتر أجهزة استقبال البث بشكل ديناميكي من خلال الاتصال بـ registerReceiver(). ويمكنك بعد ذلك إلغاء تسجيل المُستلِم من خلال "unregisterReceiver()". حيث يسمح ذلك لتطبيقك بالاستماع إلى أحداث بث معينة خلال فترة زمنية محددة فقط أثناء تشغيل التطبيق.

أمثلة على الفلاتر

لتوضيح بعض سلوكيات فلاتر الأهداف، إليك مثال من ملف البيان لتطبيق مشاركة على الشبكات الاجتماعية:

<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 إلى نقطة الدخول الرئيسية، ولا يتوقّع حدوث أي بيانات النية.
  • تشير الفئة CATEGORY_LAUNCHER إلى أنه يجب وضع رمز هذا النشاط في مشغّل تطبيقات النظام. إذا لم يحدّد العنصر <activity> رمزًا مع icon، سيستخدم النظام الرمز من العنصر <application>.

يجب إقران هذين الخيارين معًا حتى يظهر النشاط في مشغّل التطبيقات.

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

ملاحظة: نوع MIME، application/vnd.google.panorama360+jpg، هو نوع بيانات خاص يحدد الصور البانورامية، والتي يمكنك التعامل معها باستخدام واجهات برمجة تطبيقات Google بانوراما.

مطابقة الأهداف مع فلاتر الأهداف للتطبيقات الأخرى

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

وبالمثل، إذا حدّثت تطبيقك بحيث يستهدف الإصدار 13 من نظام التشغيل Android أو الإصدارات الأحدث، يتم تسليم جميع الأهداف الناشئة من التطبيقات الخارجية إلى مكوّن تم تصديره في تطبيقك فقط إذا كان هذا الغرض يتطابق مع إجراءات وفئات عنصر <intent-filter> الذي يعلن عنه تطبيقك. ويحدث هذا السلوك بغض النظر عن إصدار حزمة تطوير البرامج (SDK) المستهدَف لتطبيق الإرسال.

في الحالات التالية، لا يتم فرض مطابقة الغرض:

  • الأهداف التي يتم تسليمها إلى مكوّنات لا تشير إلى أي فلاتر أهداف.
  • الأهداف التي تنشأ من داخل التطبيق نفسه.
  • الأهداف التي تنشأ من النظام، وهي الأهداف التي يتم إرسالها من "المعرِّف الفريد للنظام" (uid=1000). تشمل تطبيقات النظام system_server والتطبيقات التي يتم فيها ضبط android:sharedUserId على android.uid.system.
  • الأهداف التي تنشأ من الجذر.

يمكنك الاطّلاع على المزيد من المعلومات حول مطابقة النية.

استخدام هدف في انتظار المراجعة

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

تشمل حالات الاستخدام الرئيسية لغرض في انتظار المراجعة ما يلي:

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

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

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

تستخدم كل طريقة التطبيق الحالي Context والسمة Intent التي تريد تضمينها، بالإضافة إلى علامة واحدة أو أكثر تحدّد كيفية استخدام الغرض (مثل إمكانية استخدام الغرض أكثر من مرة).

للحصول على مزيد من المعلومات عن استخدام الأغراض في انتظار المراجعة، يُرجى الاطّلاع على الوثائق الخاصة بكل حالة من حالات الاستخدام ذات الصلة، مثلاً في دليلَي واجهة برمجة التطبيقات الإشعارات وApp Widgets.

تحديد قابلية التغيّر

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

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

استخدام أغراض صريحة ضمن الأغراض التي في انتظار المراجعة

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

  1. تأكَّد من ضبط حقول الإجراء والحزمة والمكوّنات الخاصة بالهدف الأساسي.
  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.
    }

حلّ الهدف

عندما يتلقّى النظام نية ضمنية لبدء نشاط، فإنّه يبحث عن أفضل نشاط لهدف الغرض من خلال مقارنته بفلاتر الأهداف استنادًا إلى ثلاثة جوانب:

  • الحركة.
  • البيانات (كل من معرّف الموارد المنتظم (URI) ونوع البيانات)
  • الفئة.

توضّح الأقسام التالية كيفية مطابقة الأغراض مع المكونات المناسبة وفقًا لبيان فلتر الأهداف في ملف بيان التطبيق.

اختبار الإجراء

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

ملاحظة: يطبِّق نظام Android تلقائيًا فئة CATEGORY_DEFAULT على جميع الأغراض الضمنية التي يتم تمريرها إلى startActivity() وstartActivityForResult(). إذا كنت تريد أن يتلقّى نشاطك أغراضًا ضمنية، يجب أن يتضمّن فئة "android.intent.category.DEFAULT" في فلاتر الأهداف، كما هو موضّح في مثال <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

في عنوان URI هذا، المخطط هو content، والمضيف هو com.example.project، والمنفذ 200، والمسار folder/subfolder/etc.

كل سمة اختيارية في عنصر <data>، ولكن هناك تبعيات خطية:

  • في حال عدم تحديد مخطط، يتم تجاهل المضيف.
  • في حال عدم تحديد المضيف، يتم تجاهل المنفذ.
  • في حال عدم تحديد كل من المخطط والمضيف، يتم تجاهل المسار.

وعندما تتم مقارنة معرّف الموارد المنتظم (URI) في أحد الأغراض بمواصفات عنوان URL في أحد الفلاتر، تتم مقارنتها فقط بأجزاء عنوان URL المضمّنة في الفلتر. مثلاً:

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

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

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

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

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

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