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. إذا كانت فلاتر الأهداف المتعدّدة متوافقة، يعرض النظام مربّع حوار ليتمكّن المستخدم من اختيار التطبيق الذي يريد استخدامه.
فلتر Intent هو تعبير في ملف بيان التطبيق يحدّد نوع الأهداف التي يريد المكوّن تلقّيها. على سبيل المثال، من خلال تعريف فلتر أهداف لنشاط معيّن، يمكنك السماح للتطبيقات الأخرى ببدء نشاطك مباشرةً باستخدام نوع معيّن من الأهداف. وبالمثل، إذا لم تحدّد أي فلاتر أهداف لنشاط معيّن، لا يمكن بدء هذا النشاط إلا باستخدام هدف ضمني.
تنبيه: لضمان أمان تطبيقك، احرص دائمًا على استخدام
intent ضمني
عند بدء Service، ولا
تحدّد فلاتر intent لخدماتك. يُعد استخدام intent ضمني لبدء خدمة خطرًا أمنيًا لأنّه لا يمكنك التأكّد من الخدمة التي ستستجيب للـ intent، ولا يمكن للمستخدم معرفة الخدمة التي يتم تشغيلها. اعتبارًا من الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات)، يعرض النظام استثناءً إذا طلبت bindService() باستخدام نية ضمنية.
إنشاء نية
يحمل العنصر Intent معلومات يستخدمها نظام التشغيل Android لتحديد المكوّن الذي سيتم تشغيله (مثل اسم المكوّن أو فئة المكوّن التي يجب أن تتلقّى الغرض)، بالإضافة إلى معلومات يستخدمها المكوّن المتلقّي لتنفيذ الإجراء بشكل صحيح (مثل الإجراء المطلوب تنفيذه والبيانات التي سيتم تنفيذ الإجراء عليها).
تتضمّن Intent المعلومات الأساسية التالية:
- اسم المكوّن
- اسم المكوّن الذي سيتم تشغيله.
هذا الحقل اختياري، ولكنّه يمثّل جزءًا مهمًا من المعلومات التي تجعل الغرض صريحًا، ما يعني أنّه يجب تسليم الغرض إلى مكوّن التطبيق المحدّد باسم المكوّن فقط. بدون اسم مكوّن، يكون الغرض ضمنيًا ويحدّد النظام المكوّن الذي يجب أن يتلقّى الغرض استنادًا إلى معلومات الغرض الأخرى (مثل الإجراء والبيانات والفئة الموضّحة أدناه). إذا كنت بحاجة إلى بدء تشغيل مكوّن معيّن في تطبيقك، عليك تحديد اسم المكوّن.
ملاحظة: عند بدء
Service، يجب دائمًا تحديد اسم المكوّن. بخلاف ذلك، لا يمكنك التأكّد من الخدمة التي ستستجيب للغرض، ولن يتمكّن المستخدم من معرفة الخدمة التي ستبدأ.هذا الحقل الخاص بالسمة
Intentهو عنصرComponentName، ويمكنك تحديده باستخدام اسم فئة مؤهَّل بالكامل للمكوّن المستهدَف، بما في ذلك اسم حزمة التطبيق، على سبيل المثال،com.example.ExampleActivity. يمكنك ضبط اسم المكوّن باستخدامsetComponent()أوsetClass()أوsetClassName()أو باستخدام الدالة الإنشائيةIntent. - الإجراء
- سلسلة تحدّد الإجراء العام المطلوب تنفيذه (مثل عرض أو اختيار).
في حالة intent البث، يكون هذا هو الإجراء الذي تم تنفيذه ويتم الإبلاغ عنه. يحدّد الإجراء إلى حد كبير كيفية تنظيم بقية الغرض، لا سيما المعلومات الواردة في البيانات والإضافات.
يمكنك تحديد إجراءاتك الخاصة لاستخدامها من خلال الأهداف داخل تطبيقك (أو لاستخدامها من خلال تطبيقات أخرى لاستدعاء المكوّنات في تطبيقك)، ولكنّك عادةً ما تحدّد ثوابت الإجراءات التي تحدّدها الفئة
Intentأو فئات إطار العمل الأخرى. في ما يلي بعض الإجراءات الشائعة لبدء نشاط:ACTION_VIEW- استخدِم هذا الإجراء في هدف مع
startActivity()عندما تتوفّر لديك بعض المعلومات التي يمكن أن يعرضها نشاط للمستخدم، مثل صورة لعرضها في تطبيق معرض الصور، أو عنوان لعرضه في تطبيق خرائط. ACTION_SEND- يُعرف هذا الإجراء أيضًا باسم الغرض share، ويجب استخدامه في غرض مع
startActivity()عندما يكون لديك بعض البيانات التي يمكن للمستخدم مشاركتها من خلال تطبيق آخر، مثل تطبيق البريد الإلكتروني أو تطبيق المشاركة على وسائل التواصل الاجتماعي.
راجِع مرجع الفئة
Intentللحصول على مزيد من الثوابت التي تحدّد الإجراءات العامة. يتم تحديد الإجراءات الأخرى في مكان آخر ضمن إطار عمل Android، مثلSettingsللإجراءات التي تفتح شاشات معيّنة في تطبيق "الإعدادات" الخاص بالنظام.يمكنك تحديد الإجراء الخاص بغرض باستخدام
setAction()أو باستخدام أداة إنشاءIntent.إذا حدّدت إجراءاتك الخاصة، احرص على تضمين اسم حزمة تطبيقك كبادئة، كما هو موضّح في المثال التالي:
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- البيانات
- معرّف الموارد المنتظم (URI) (وهو عنصر
Uri) الذي يشير إلى البيانات التي سيتم اتخاذ إجراء بشأنها و/أو نوع MIME الخاص بهذه البيانات. يتم تحديد نوع البيانات المقدَّمة بشكل عام من خلال إجراء الهدف. على سبيل المثال، إذا كان الإجراءACTION_EDIT، يجب أن تتضمّن البيانات معرّف الموارد الموحّد (URI) للمستند المطلوب تعديله.عند إنشاء غرض، من المهم غالبًا تحديد نوع البيانات (نوع MIME) بالإضافة إلى معرّف الموارد المنتظم (URI). على سبيل المثال، قد لا يتمكّن نشاط يمكنه عرض الصور من تشغيل ملف صوتي، حتى إذا كانت تنسيقات معرّف الموارد المنتظم متشابهة. يساعد تحديد نوع MIME للبيانات نظام Android في العثور على أفضل مكوّن لتلقّي الغرض. ومع ذلك، يمكن في بعض الأحيان استنتاج نوع MIME من معرّف الموارد المنتظم (URI)، خاصةً عندما تكون البيانات معرّف موارد منتظم (URI) من النوع
content:. يشير معرّف الموارد الموحّدcontent:إلى أنّ البيانات مخزّنة على الجهاز ويتحكّم فيهاContentProvider، ما يتيح للنظام الاطّلاع على نوع MIME الخاص بالبيانات.لضبط عنوان URI للبيانات فقط، استخدِم
setData(). لضبط نوع MIME فقط، استخدِمsetType(). إذا لزم الأمر، يمكنك ضبط كليهما بشكل صريح باستخدامsetDataAndType().تنبيه: إذا كنت تريد ضبط كلّ من معرّف الموارد المنتظم (URI) ونوع MIME، لا تستخدِم
setData()وsetType()لأنّ كلّ منهما يبطل قيمة الآخر. استخدِمsetDataAndType()دائمًا لضبط كل من معرّف الموارد الموحّد ونوع MIME. - الفئة
- سلسلة تحتوي على معلومات إضافية حول نوع المكوّن الذي يجب أن يتعامل مع الغرض. يمكن وضع أي عدد من أوصاف الفئات في intent، ولكن معظم intents لا تتطلّب فئة.
في ما يلي بعض الفئات الشائعة:
CATEGORY_BROWSABLE- يسمح النشاط المستهدف بتشغيله من خلال متصفّح ويب لعرض البيانات التي يشير إليها رابط، مثل صورة أو رسالة إلكترونية.
CATEGORY_LAUNCHER- النشاط هو النشاط الأوّلي لمهمة ويتم إدراجه في مشغّل تطبيقات النظام.
اطّلِع على وصف فئة
Intentللحصول على القائمة الكاملة للفئات.يمكنك تحديد فئة باستخدام
addCategory().
تمثّل الخصائص المذكورة أعلاه (اسم المكوّن والإجراء والبيانات والفئة) الخصائص المحدِّدة للغرض. من خلال قراءة هذه الخصائص، يمكن لنظام التشغيل Android تحديد مكوّن التطبيق الذي يجب تشغيله. ومع ذلك، يمكن أن يتضمّن الغرض معلومات إضافية لا تؤثّر في كيفية تحويله إلى أحد مكونات التطبيق. يمكن أن يوفّر الهدف أيضًا المعلومات التالية:
- الميزات الإضافية
- أزواج المفاتيح والقيم التي تتضمّن معلومات إضافية مطلوبة لتنفيذ الإجراء المطلوب
وكما تستخدم بعض الإجراءات أنواعًا معيّنة من معرّفات الموارد المنتظمة للبيانات، تستخدم بعض الإجراءات أيضًا عناصر إضافية معيّنة.
يمكنك إضافة بيانات إضافية باستخدام طرق
putExtra()مختلفة، يقبل كل منها مَعلمتَين: اسم المفتاح والقيمة. يمكنك أيضًا إنشاء عنصرBundleيتضمّن جميع البيانات الإضافية، ثم إدراجBundleفيIntentباستخدامputExtras().على سبيل المثال، عند إنشاء هدف لإرسال رسالة إلكترونية باستخدام
ACTION_SEND، يمكنك تحديد المستلِم to باستخدام المفتاحEXTRA_EMAIL، وتحديد الموضوع باستخدام المفتاحEXTRA_SUBJECT.تحدّد الفئة
Intentالعديد من الثوابتEXTRA_*لأنواع البيانات الموحّدة. إذا كنت بحاجة إلى تعريف مفاتيح إضافية خاصة بك (لأغراض يتلقّاها تطبيقك)، احرص على تضمين اسم حزمة تطبيقك كبادئة، كما هو موضّح في المثال التالي:Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
تنبيه: لا تستخدِم بيانات
ParcelableأوSerializableعند إرسال غرض تتوقّع أن يتلقّاه تطبيق آخر. إذا حاول تطبيق الوصول إلى البيانات في عنصرBundleولكن لم يكن لديه إذن بالوصول إلى الفئة التي تم تجميعها أو تسلسلها، سيصدر النظامRuntimeException. - الأعلام
- يتم تحديد العلامات في الفئة
Intentالتي تعمل كبيانات وصفية للغرض. قد توجّه العلامات نظام Android بشأن كيفية تشغيل نشاط معيّن (على سبيل المثال، المهمة التي يجب أن ينتمي إليها النشاط) وكيفية التعامل معه بعد تشغيله (على سبيل المثال، ما إذا كان ينتمي إلى قائمة الأنشطة الحديثة).لمزيد من المعلومات، يُرجى الاطّلاع على طريقة
setFlags().
مثال على رسالة intent صريحة
النية الضمنية هي تلك التي تستخدمها لتشغيل مكوّن تطبيق معيّن، مثل نشاط أو خدمة معيّنة في تطبيقك. لإنشاء نية ضمنية، حدِّد اسم المكوّن لكائن Intent، وجميع خصائص النية الأخرى اختيارية.
على سبيل المثال، إذا أنشأت خدمة في تطبيقك باسم DownloadService،
مصمَّمة لتنزيل ملف من الويب، يمكنك بدء تشغيلها باستخدام الرمز التالي:
Kotlin
// Executed in an Activity, so 'this' is theContext// 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 theContext// 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 الضمنية إجراءً يمكنه استدعاء أي تطبيق على الجهاز قادر على تنفيذ هذا الإجراء. يكون استخدام 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); }
رصد عمليات إطلاق الأهداف غير الآمنة
قد يطلق تطبيقك أهدافًا للتنقّل بين المكوّنات داخل تطبيقك، أو لتنفيذ إجراء نيابةً عن تطبيق آخر. ولتحسين أمان النظام الأساسي، يوفّر الإصدار 12 من نظام التشغيل Android (المستوى 31 من واجهة برمجة التطبيقات) والإصدارات الأحدث ميزة لتحديد المشاكل تحذّرك إذا كان تطبيقك يطلق هدفًا بطريقة غير آمنة. على سبيل المثال، قد ينفّذ تطبيقك عملية تشغيل غير آمنة لرسالة intent متداخلة، وهي رسالة intent يتم تمريرها كبيانات إضافية في رسالة intent أخرى.
إذا كان تطبيقك ينفّذ الإجراءَين التاليَين، سيرصد النظام عملية تشغيل غير آمنة لغرض ، وسيحدث انتهاك StrictMode:
- يفكّ تطبيقك حزمة هدف متداخل من الإضافات الخاصة بهدف تم تسليمه.
- يبدأ تطبيقك على الفور أحد مكونات التطبيق باستخدام هذا الغرض المتداخل،
مثل تمرير الغرض إلى
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 كيف ينقل النظام التحكّم من تطبيقك (العميل) إلى تطبيق آخر (الخدمة)، ثم يعيده إلى تطبيقك:
- ينشئ تطبيقك رسالة intent تستدعي نشاطًا في تطبيق آخر. وضمن رسالة intent هذه، يمكنك إضافة عنصر
PendingIntentكبيانات إضافية. يستدعي رمز PendingIntent هذا أحد المكوّنات في تطبيقك، ولكن لم يتم تصدير هذا المكوّن. - عند تلقّي Intent لتطبيقك، يستخرج التطبيق الآخر العنصر
PendingIntentالمتداخل. - يستدعي التطبيق الآخر الطريقة
send()على العنصرPendingIntent. - بعد إعادة التحكّم إلى تطبيقك، يستدعي النظام النية المعلّقة باستخدام سياق تطبيقك.
الشكل 2. مخطّط بياني يوضّح عملية التواصل بين التطبيقات عند استخدام نية معلّقة متداخلة
تلقّي intent ضمني
للإعلان عن الأهداف الضمنية التي يمكن أن يتلقّاها تطبيقك، عليك تعريف فلتر هدف واحد أو أكثر لكل مكوّن من مكوّنات تطبيقك باستخدام العنصر <intent-filter> في ملف البيان.
يحدّد كل فلتر أهداف نوع الأهداف التي يقبلها استنادًا إلى إجراء الهدف وبياناته وفئته. لا يرسل النظام هدفًا ضمنيًا إلى أحد مكونات تطبيقك إلا إذا كان بإمكان الهدف المرور عبر أحد فلاتر الأهداف.
ملاحظة: يتم دائمًا تسليم الأهداف الصريحة إلى وجهتها، بغض النظر عن أي فلاتر Intent يعرّفها المكوِّن.
يجب أن يحدّد أحد مكوّنات التطبيق فلاتر منفصلة لكل مهمة فريدة يمكنه تنفيذها.
على سبيل المثال، قد يتضمّن أحد الأنشطة في تطبيق معرض الصور فلترَين: فلتر
لعرض صورة، وفلتر آخر لتعديل صورة. عندما يبدأ النشاط، يفحص Intent ويقرّر كيفية التصرف استنادًا إلى المعلومات الواردة في Intent (مثل عرض عناصر تحكّم المحرّر أو عدم عرضها).
يتم تحديد كل فلتر intent من خلال عنصر <intent-filter> في ملف البيان الخاص بالتطبيق، ويكون هذا العنصر مضمّنًا في مكوِّن التطبيق ذي الصلة (مثل عنصر <activity>).
في كل مكوّن تطبيق يتضمّن عنصر <intent-filter>، اضبط قيمة android:exported بشكل صريح.
تشير هذه السمة إلى ما إذا كان بإمكان التطبيقات الأخرى الوصول إلى أحد مكونات التطبيق. في بعض الحالات، مثل الأنشطة التي تتضمّن فلاتر الأهداف الخاصة بها الفئة LAUNCHER، يكون من المفيد ضبط هذه السمة على true. وفي الحالات الأخرى،
من الأفضل ضبط هذه السمة على false.
تحذير: إذا كان أحد الأنشطة أو الخدمات أو مستقبِلات البث في تطبيقك يستخدم فلاتر الأهداف ولم يضبط قيمة android:exported بشكل صريح، لن يمكن تثبيت تطبيقك على جهاز يعمل بالإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث.
داخل <intent-filter>،
يمكنك تحديد نوع الأهداف التي تريد قبولها باستخدام عنصر واحد أو أكثر
من العناصر الثلاثة التالية:
<action>- تُعلن عن قبول إجراء الهدف، وذلك في السمة
name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية للإجراء، وليس الثابت الخاص بالفئة. <data>- تحدّد هذه السمة نوع البيانات المقبولة، وذلك باستخدام سمة واحدة أو أكثر تحدّد جوانب مختلفة من معرّف URI الخاص بالبيانات (
schemeوhostوportوpath) ونوع MIME. <category>- تُعلن عن فئة الغرض المقبولة في السمة
name. يجب أن تكون القيمة هي قيمة السلسلة الحرفية للإجراء، وليس الثابت الخاص بالفئة.ملاحظة: لتلقّي الأهداف الضمنية، يجب تضمين الفئة
CATEGORY_DEFAULTفي فلتر الأهداف. تتعامل الطريقتانstartActivity()وstartActivityForResult()مع جميع الأهداف على أنّها تعرّف الفئةCATEGORY_DEFAULT. إذا لم تحدّد هذه الفئة في فلتر الأهداف، لن يتم حل أي أهداف ضمنية لنشاطك.
على سبيل المثال، إليك بيان نشاط يتضمّن فلتر أهداف لتلقّي هدف 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إلى أنّ هذا هو نقطة الدخول الرئيسية ولا يتوقّع أي بيانات Intent. - تشير الفئة
CATEGORY_LAUNCHERإلى أنّه يجب وضع رمز هذا النشاط في مشغّل التطبيقات بالنظام. إذا لم يحدّد العنصر<activity>رمزًا باستخدامicon، سيستخدم النظام الرمز من العنصر<application>.
يجب ربط هذين العنصرين معًا لكي يظهر النشاط في مشغّل التطبيقات.
يهدف النشاط الثاني، ShareActivity، إلى تسهيل مشاركة النصوص ومحتوى الوسائط. على الرغم من أنّ المستخدمين قد يدخلون هذا النشاط من خلال الانتقال إليه من MainActivity، يمكنهم أيضًا الدخول إلى ShareActivity مباشرةً من تطبيق آخر يرسل هدفًا ضمنيًا يطابق أحد فلترَي الأهداف.
ملاحظة: نوع MIME،
application/vnd.google.panorama360+jpg، هو نوع بيانات خاص يحدّد
الصور البانورامية التي يمكنك التعامل معها باستخدام واجهات برمجة التطبيقات Google
Panorama.
مطابقة الأهداف مع فلاتر الأهداف في التطبيقات الأخرى
إذا كان تطبيق آخر يستهدف الإصدار 13 من نظام التشغيل Android (المستوى 33 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنه معالجة الغرض من تطبيقك فقط إذا كان الغرض يتطابق مع الإجراءات والفئات الخاصة بعنصر <intent-filter> في هذا التطبيق الآخر. وإذا لم يعثر النظام على تطابق، سيظهر الخطأ ActivityNotFoundException.
يجب أن يتعامل تطبيق الإرسال مع هذا الاستثناء.
وبالمثل، إذا عدّلت تطبيقك ليصبح يستهدف الإصدار 13 من نظام التشغيل Android أو الإصدارات الأحدث، لن يتم تسليم أي أغراض مصدرها تطبيقات خارجية إلى أحد المكوّنات التي تم تصديرها في تطبيقك إلا إذا كان الغرض يتطابق مع الإجراءات والفئات الخاصة بعنصر <intent-filter> الذي يحدّده تطبيقك. ويحدث هذا السلوك بغض النظر عن إصدار حزمة SDK المستهدَف للتطبيق المُرسِل.
في الحالات التالية، لا يتم فرض مطابقة النية:
- الأهداف التي يتم تسليمها إلى المكوّنات التي لا تحدّد أي فلاتر أهداف
- الأهداف التي تنشأ من داخل التطبيق نفسه
- الأهداف الصادرة من النظام، أي الأهداف التي يتم إرسالها من "معرّف المستخدم الفريد للنظام" (uid=1000) تشمل تطبيقات النظام
system_serverوالتطبيقات التي تم ضبطandroid:sharedUserIdفيها علىandroid.uid.system. - الطلبات التي تنشأ من الجذر
مزيد من المعلومات عن مطابقة النية
استخدام طلب معلّق
عنصر PendingIntent هو برنامج تضمين لعنصر Intent. الغرض الأساسي من PendingIntent
هو منح إذن لتطبيق خارجي
باستخدام Intent المضمّن كما لو كان يتم تنفيذه من
عملية تطبيقك.
تشمل حالات الاستخدام الرئيسية لـ PendingIntent ما يلي:
- يتم الإعلان عن هدف سيتم تنفيذه عندما يتّخذ المستخدم إجراءً بشأن الإشعار
(ينفّذ نظام التشغيل Android
NotificationManagerIntent). - يتم الإعلان عن هدف سيتم تنفيذه عندما ينفِّذ المستخدم إجراءً باستخدام أداة التطبيق (ينفِّذ تطبيق الشاشة الرئيسية
Intent). - تحديد هدف سيتم تنفيذه في وقت مستقبلي محدّد (ينفّذ نظام التشغيل Android
AlarmManagerIntent).
وكما أنّ كل عنصر Intent مصمّم ليتم التعامل معه من خلال نوع معيّن من مكونات التطبيق (إما Activity أو Service أو BroadcastReceiver)، يجب أيضًا إنشاء PendingIntent مع مراعاة ذلك. عند استخدام PendingIntent، لا ينفّذ تطبيقك الغرض باستخدام طلب مثل startActivity(). بدلاً من ذلك، يجب الإفصاح عن نوع المكوّن المقصود عند إنشاء
PendingIntent من خلال استدعاء طريقة الإنشاء المعنية:
PendingIntent.getActivity()لـIntentيبدأ بـActivity-
PendingIntent.getService()مقابلIntentالذي يبدأ بـService -
PendingIntent.getBroadcast()مقابلIntentالذي يبدأ بـBroadcastReceiver
ما لم يكن تطبيقك يتلقّى طلبات 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القابل للتغيير للنظام بإضافة بيانات إضافية خاصة بالأهداف تمثّل أحداث مراحل النشاط المتعلقة بالموقع الجغرافي. وتشمل هذه الأحداث تغييرًا في الموقع الجغرافي وتوفّر مقدّم خدمة. - جدولة المنبّهات باستخدام
AlarmManagerيسمح العنصر القابل للتغييرPendingIntentللنظام بإضافةEXTRA_ALARM_COUNTإلى Intent. يمثّل هذا العنصر الإضافي عدد المرات التي تم فيها تشغيل منبّه متكرّر. من خلال تضمين هذا العنصر الإضافي، يمكن أن يرسل الغرض إشعارًا دقيقًا إلى التطبيق بشأن ما إذا تم تشغيل المنبّه المتكرّر عدة مرات، مثلاً عندما كان الجهاز في وضع السكون.
إذا كان تطبيقك ينشئ عنصر PendingIntent قابلاً للتغيير، ننصحك بشدة باستخدام intent صريح وملء ComponentName. بهذه الطريقة، عندما يستدعي تطبيق آخر PendingIntent ويعيد التحكّم إلى تطبيقك، يبدأ دائمًا المكوّن نفسه في تطبيقك.
استخدام الأهداف الضمنية في الأهداف المعلّقة
لتحديد كيفية استخدام التطبيقات الأخرى للنوايا المعلّقة في تطبيقك بشكل أفضل، عليك دائمًا تضمين نية معلّقة في نية صريحة. للمساعدة في اتّباع أفضل الممارسات هذه، يُرجى اتّباع الخطوات التالية:
- التأكُّد من إدخال قيمة في حقول الإجراء والحزمة والمكوِّن في رسالة Intent الأساسية
-
استخدِم طريقة
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().
إذا كنت تريد أن يتلقّى نشاطك أهدافًا ضمنية، يجب أن يتضمّن فئة "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
في معرّف الموارد المنتظم هذا، يكون المخطط content والمضيف com.example.project والمنفذ 200 والمسار folder/subfolder/etc.
كل سمة من هذه السمات اختيارية في عنصر <data>،
ولكن هناك تبعيات خطية:
- إذا لم يتم تحديد مخطط، سيتم تجاهل المضيف.
- إذا لم يتم تحديد مضيف، سيتم تجاهل المنفذ.
- إذا لم يتم تحديد كل من المخطط والمضيف، سيتم تجاهل المسار.
عند مقارنة معرّف الموارد المنتظم (URI) في هدف بمواصفات معرّف الموارد المنتظم في فلتر، تتم مقارنته فقط بأجزاء معرّف الموارد المنتظم المضمّنة في الفلتر. مثلاً:
- إذا كان الفلتر يحدّد المخطط فقط، ستتطابق جميع معرّفات URI التي تستخدم هذا المخطط مع الفلتر.
- إذا حدّد فلتر مخططًا وسلطة بدون مسار، ستجتاز جميع معرّفات الموارد الموحّدة التي تتضمّن المخطط والسلطة نفسها الفلتر، بغض النظر عن مساراتها.
- إذا حدّد الفلتر مخططًا وسلطة ومسارًا، لن تجتاز الفلتر سوى معرّفات الموارد الموحّدة التي تتضمّن المخطط والسلطة والمسار نفسهما.
ملاحظة: يمكن أن يحتوي تحديد المسار على علامة نجمة بدل (*) لطلب مطابقة جزئية فقط لاسم المسار.
يقارن اختبار البيانات كلاً من معرّف الموارد المنتظم (URI) ونوع MIME في الغرض بمعرّف موارد منتظم (URI) ونوع MIME محدّدين في الفلتر. القواعد هي كما يلي:
- لا يجتاز الغرض الذي لا يحتوي على معرّف موارد منتظم (URI) أو نوع MIME الاختبار إلا إذا كان الفلتر لا يحدّد أي معرّفات موارد منتظمة أو أنواع MIME.
- لا يجتاز الاختبار أي هدف يتضمّن معرّف موارد منتظم (URI) ولكن لا يتضمّن نوع MIME (لا صريحًا ولا يمكن استنتاجه من معرّف الموارد المنتظم) إلا إذا كان معرّف الموارد المنتظم الخاص به يطابق تنسيق معرّف الموارد المنتظم الخاص بالفلتر، ولا يحدّد الفلتر أيضًا نوع MIME.
- يجتاز الغرض الذي يحتوي على نوع MIME ولكن ليس على معرّف الموارد المنتظم (URI) الاختبار فقط إذا كانت قائمة الفلاتر تتضمّن نوع MIME نفسه ولا تحدّد تنسيق معرّف الموارد المنتظم (URI).
- لا يجتاز جزء نوع MIME من الاختبار إلا إذا كان هذا النوع يطابق نوعًا مدرَجًا في الفلتر. ويجتاز الجزء الخاص بمعرّف الموارد المنتظم (URI) الاختبار
إما إذا كان معرّف الموارد المنتظم يتطابق مع معرّف موارد منتظم في الفلتر أو إذا كان يتضمّن معرّف موارد منتظم
content:أوfile:ولم يحدّد الفلتر معرّف موارد منتظم. بعبارة أخرى، يُفترض أنّ أحد المكوّنات يتوافق مع بياناتcontent:وfile:إذا كانت قوائم الفلاتر الخاصة به تتضمّن فقط نوع MIME.
ملاحظة: إذا حدّدت النية معرّف موارد منتظم (URI) أو نوع MIME، سيفشل اختبار البيانات إذا لم تتضمّن <intent-filter> أي عناصر <data>.
تعكس القاعدة الأخيرة، أي القاعدة (د)، التوقّع بأنّ المكوّنات يمكنها الحصول على بيانات محلية من ملف أو موفّر محتوى.
لذلك، يمكن أن تتضمّن فلاترها نوع بيانات فقط، ولا تحتاج إلى تسمية مخطّطَي content: وfile: بشكل صريح.
يوضّح المثال التالي حالة نموذجية يخبر فيها العنصر <data> نظام Android بأنّ المكوّن يمكنه الحصول على بيانات الصور من موفّر محتوى وعرضها:
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
إنّ الفلاتر التي تحدّد نوع بيانات ولكن لا تحدّد معرّف موارد موحّدًا هي الأكثر شيوعًا، لأنّ معظم البيانات المتاحة يتم توزيعها من خلال مقدّمي المحتوى.
هناك إعداد شائع آخر وهو عامل تصفية يتضمّن مخططًا ونوع بيانات. على سبيل المثال، يوضّح العنصر <data> التالي لنظام التشغيل Android أنّ المكوّن يمكنه استرداد بيانات الفيديو من الشبكة لتنفيذ الإجراء:
<intent-filter> <data android:scheme="http" android:mimeType="video/*" /> ... </intent-filter>
مطابقة النية
تتم مطابقة الأهداف مع فلاتر الأهداف ليس فقط لاكتشاف مكوِّن مستهدف لتفعيله، ولكن أيضًا لاكتشاف معلومات حول مجموعة المكوِّنات على الجهاز. على سبيل المثال، يملأ تطبيق Home مشغّل التطبيقات
من خلال العثور على جميع الأنشطة التي تتضمّن فلاتر الأهداف التي تحدّد الإجراء
ACTION_MAIN والفئة
CATEGORY_LAUNCHER.
لا يكون التطابق ناجحًا إلا إذا كانت الإجراءات والفئات في Intent تتطابق مع الفلتر، كما هو موضّح في مستندات فئة IntentFilter.
يمكن لتطبيقك استخدام ميزة مطابقة الأهداف بطريقة مشابهة لما يفعله تطبيق Home.
يحتوي PackageManager على مجموعة من query...()
الطرق التي تعرض جميع المكوّنات التي يمكنها قبول غرض معيّن،
بالإضافة إلى سلسلة مشابهة من طرق resolve...() التي تحدّد أفضل
مكوّن للردّ على غرض معيّن. على سبيل المثال، يعرض queryIntentActivities() قائمة بجميع الأنشطة التي يمكنها تنفيذ الغرض الذي تم تمريره كوسيطة، ويعرض queryIntentServices() قائمة مشابهة بالخدمات.
ولا تنشّط أيًّا من الطريقتَين المكوّنات، بل تعرض فقط المكوّنات التي يمكنها الاستجابة. تتوفّر طريقة مشابهة،
queryBroadcastReceivers()، لمستقبِلات البث.