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

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

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

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

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

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

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

إنشاء نية

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

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

اسم المكوّن
اسم المكوّن المطلوب تشغيله

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

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

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

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

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

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

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

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

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

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

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

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

ميزات إضافية
أزواج المفتاح/القيمة التي تحمل معلومات إضافية مطلوبة لإنجاز الإجراء المطلوب. مثلما تستخدم بعض الإجراءات أنواعًا معيّنة من معرّفات الموارد المنتظمة (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.

Flags
يتم تحديد العلامات في فئة 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 ويحمل بيانات "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);
}

رصد عمليات تشغيل intent غير الآمنة

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

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

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

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

التحقّق من عمليات تشغيل الأهداف غير الآمنة

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

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build())
}

Java

protected void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
        // Other StrictMode checks that you've previously added.
        // ...
        .detectUnsafeIntentLaunch()
        .penaltyLog()
        // Consider also adding penaltyDeath()
        .build());
}

استخدام الأهداف بشكل أكثر مسؤولية

للحدّ من احتمال تشغيل نية غير آمنة وانتهاك StrictMode، اتّبِع أفضل الممارسات التالية.

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

لا تصدِّر مكوّنات تطبيقك بدون داعٍ. على سبيل المثال، إذا كنت تنوي تشغيل مكوِّن تطبيق باستخدام رسالة intent داخلية مدمجة، اضبط سمة android:exported لهذا المكوِّن على false.

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

يوضّح المخطّط البياني في الشكل 2 كيفية نقل النظام للتحكّم من تطبيقك (العميل) إلى تطبيق آخر (الخدمة)، ثم العودة إلى تطبيقك:

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

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

تلقّي طلب ضمني

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

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

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

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

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

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

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

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

ملاحظة: لتلقّي الأهداف الضمنية، يجب تضمين الفئة CATEGORY_DEFAULT في فلتر الأهداف. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. إذا لم تعلن عن هذه الفئة في intent filter، لن يتم حلّ أي implicit intents في نشاطك.

على سبيل المثال، إليك بيان نشاط يتضمّن فلتر intent لتلقّي ACTION_SEND intent عندما يكون نوع البيانات نصًا:

<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>. وفي هذه الحالة، يجب التأكّد من أنّ المكوِّن يمكنه التعامل مع أيّ من مجموعات عناصر الفلتر هذه أو جميعها.

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

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

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

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

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

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

لتوضيح بعض سلوكيات intent filter، إليك مثال من ملف البيان لتطبيق مشاركة على وسائل التواصل الاجتماعي:

<activity android:name="MainActivity" android:exported="true">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity" android:exported="false">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

النشاط الأول، MainActivity، هو نقطة الدخول الرئيسية للتطبيق، وهو النشاط الذي يفتح عندما يشغّل المستخدم التطبيق في البداية باستخدام رمز مشغّل التطبيقات:

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

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

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

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

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

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

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

في الحالات التالية، لا يتم فرض مطابقة الأهداف:

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

مزيد من المعلومات عن مطابقة نية الشراء

استخدام intent مؤجّلة

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

تشمل حالات الاستخدام الرئيسية لـ intent المؤجّلة ما يلي:

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

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

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

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

لمزيد من المعلومات حول استخدام الـ intents المؤجّلة، يُرجى الاطّلاع على المستندات الخاصة بكل حالة من حالات الاستخدام المعنيّة، مثل أدلة واجهة برمجة التطبيقات الإشعارات والتطبيقات المصغّرة.

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

إذا كان تطبيقك يستهدف الإصدار 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.

إنشاء intent مؤجّلة غير قابلة للتغيير كلما أمكن ذلك

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

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

استخدام عناصر intent صريحة ضمن عناصر intent المؤجّلة

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

  1. تأكَّد من إدخال قيمة في حقول الإجراء والحزمة والمكوِّن في رسالة intent الأساسية.
  2. استخدِم FLAG_IMMUTABLE، التي تمت إضافتها في الإصدار Android 6.0 (مستوى واجهة برمجة التطبيقات 23)، لإنشاء intent مؤجّلة. تمنع هذه العلامة التطبيقات التي تتلقّى PendingIntent من ملء السمات غير المعبأة. إذا كان minSdkVersion لتطبيقك هو 22 أو أقل، يمكنك توفير الأمان والتوافق معًا باستخدام الرمز التالي:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

حلّ النزاعات المتعلّقة بالنيّة

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

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

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

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

لتحديد إجراءات intent المقبولة، يمكن أن يعلن فلتر intent عن صفر أو أكثر من عناصر <action>، كما هو موضّح في المثال التالي:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

لتجاوز هذا الفلتر، يجب أن يتطابق الإجراء المحدّد في Intent مع أحد الإجراءات المدرَجة في الفلتر.

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

اختبار الفئة

لتحديد فئات intent المقبولة، يمكن أن يعلن intent filter عن صفر أو أكثر من عناصر <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(). إذا أردت أن يتلقّى نشاطك implicit intent، يجب أن يتضمّن فئة لـ "android.intent.category.DEFAULT" في intent filter، كما هو موضّح في مثال <intent-filter> السابق.

اختبار البيانات

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

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

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

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

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

مطابقة النية

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

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