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

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

  • بدء نشاط

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

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

  • بدء خدمة

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

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

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

  • عرض بث

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

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

أنواع النوايا

هناك نوعان من الأهداف:

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

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

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

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

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

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

إنشاء نية

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

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

اسم المكوّن
اسم المكوّن الذي سيتم تشغيله.

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

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

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

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

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

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

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

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

يمكنك تحديد الإجراء الخاص بغرض باستخدام setAction() أو باستخدام أداة إنشاء Intent.

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

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
البيانات
معرّف الموارد المنتظم (URI) (وهو عنصر Uri) الذي يشير إلى البيانات التي سيتم اتخاذ إجراء بشأنها و/أو نوع MIME الخاص بهذه البيانات. يتم تحديد نوع البيانات المقدَّمة بشكل عام من خلال إجراء الهدف. على سبيل المثال، إذا كان الإجراء هو ACTION_EDIT، يجب أن تتضمّن البيانات معرّف الموارد الموحّد (URI) للمستند المطلوب تعديله.

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

لضبط عنوان URI للبيانات فقط، استخدِم setData(). لضبط نوع MIME فقط، استخدِم setType(). إذا لزم الأمر، يمكنك ضبط كليهما بشكل صريح باستخدام setDataAndType().

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

الفئة
سلسلة تحتوي على معلومات إضافية حول نوع المكوّن الذي يجب أن يتعامل مع الغرض. يمكن وضع أي عدد من أوصاف الفئات في intent، ولكن معظم intents لا تتطلّب فئة. في ما يلي بعض الفئات الشائعة:
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 صريحة

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

لمزيد من المعلومات حول إنشاء خدمة وبدء استخدامها، يُرجى الاطّلاع على دليل الخدمات.

مثال على 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. مربّع حوار لاختيار التطبيق

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

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

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

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

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

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

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

يجب ربط هذين العنصرين معًا لكي يظهر النشاط في مشغّل التطبيقات.

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

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

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

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

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

حلّ النية

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

  • الإجراء
  • البيانات (المعرّف الموحّد للموارد ونوع البيانات)
  • الفئة.

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

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

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

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

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

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

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

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

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

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

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

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

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

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

مطابقة النية

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

يمكن لتطبيقك استخدام ميزة مطابقة الأهداف بطريقة مشابهة لما يفعله تطبيق Home. يحتوي PackageManager على مجموعة من طرق query...() تعرض جميع المكوّنات التي يمكنها قبول غرض معيّن، بالإضافة إلى سلسلة مشابهة من طرق resolve...() تحدّد أفضل مكوّن للرد على غرض معيّن. على سبيل المثال، تعرض queryIntentActivities() قائمة بجميع الأنشطة التي يمكنها تنفيذ الغرض الذي تم تمريره كمعلَمة، وتعرض queryIntentServices() قائمة مشابهة بالخدمات. ولا تنشّط أيًّا من الطريقتَين المكوّنات، بل تعرض فقط المكوّنات التي يمكنها الاستجابة. تتوفّر طريقة مشابهة، queryBroadcastReceivers()، لمستقبِلات البث.