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

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().

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

أنواع الأهداف

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

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

يوضِّح الشكل 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);
}

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

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

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

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

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

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

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

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

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

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

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

  1. يجتاز الغرض الذي لا يتضمّن معرّف الموارد المنتظم (URI) أو نوع MIME الاختبار إلا إذا لم يحدِّد الفلتر أي معرّفات URI أو أنواع 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 (عنوان URL) الغرض، سيتعذّر اختبار البيانات في حال عدم توفّر عناصر <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()، لأجهزة استقبال البث.