مقاصد و فیلترهای مقاصد

یک Intent یک شیء پیام‌رسان است که می‌توانید از آن برای درخواست یک عمل از یک کامپوننت دیگر برنامه استفاده کنید. اگرچه intentها ارتباط بین کامپوننت‌ها را به چندین روش تسهیل می‌کنند، اما سه مورد استفاده اساسی وجود دارد:

  • شروع یک فعالیت

    یک Activity نشان دهنده یک صفحه نمایش واحد در یک برنامه است. شما می‌توانید با ارسال یک Intent به startActivity() یک نمونه جدید از یک Activity را شروع کنید. Intent activity مورد نظر برای شروع را توصیف می‌کند و داده‌های لازم را در خود جای می‌دهد.

    اگر می‌خواهید پس از اتمام activity، نتیجه‌ای از آن دریافت کنید، تابع startActivityForResult() را فراخوانی کنید. activity شما نتیجه را به عنوان یک شیء Intent جداگانه در تابع onActivityResult() دریافت می‌کند. برای اطلاعات بیشتر، به راهنمای Activities مراجعه کنید.

  • شروع یک سرویس

    یک Service ، کامپوننتی است که عملیات را در پس‌زمینه و بدون رابط کاربری انجام می‌دهد. با اندروید ۵.۰ (سطح API ۲۱) و بالاتر، می‌توانید یک سرویس را با JobScheduler راه‌اندازی کنید. برای اطلاعات بیشتر در مورد JobScheduler ، به API-reference documentation آن مراجعه کنید.

    برای نسخه‌های پایین‌تر از اندروید ۵.۰ (سطح API 21)، می‌توانید با استفاده از متدهای کلاس Service ، یک سرویس را راه‌اندازی کنید. می‌توانید با ارسال یک Intent به startService() یک سرویس را برای انجام یک عملیات یک‌باره (مانند دانلود فایل) راه‌اندازی کنید. Intent سرویسی را که باید شروع شود توصیف می‌کند و داده‌های لازم را حمل می‌کند.

    اگر سرویس با رابط کلاینت-سرور طراحی شده باشد، می‌توانید با ارسال یک Intent به bindService() از یک کامپوننت دیگر به سرویس متصل شوید. برای اطلاعات بیشتر، به راهنمای سرویس‌ها مراجعه کنید.

  • ارائه یک پخش

    یک broadcast پیامی است که هر برنامه‌ای می‌تواند دریافت کند. سیستم broadcastهای مختلفی را برای رویدادهای سیستمی ارسال می‌کند، مانند زمانی که سیستم بوت می‌شود یا دستگاه شروع به شارژ شدن می‌کند. شما می‌توانید با ارسال یک Intent به sendBroadcast() یا sendOrderedBroadcast() یک broadcast را به برنامه‌های دیگر ارسال کنید.

ادامه‌ی این صفحه نحوه‌ی کار و استفاده از intentها را توضیح می‌دهد. برای اطلاعات مرتبط، به «تعامل با سایر برنامه‌ها و اشتراک‌گذاری محتوا» مراجعه کنید.

انواع قصد

دو نوع قصد وجود دارد:

  • اینتنت‌های صریح با مشخص کردن یک ComponentName کامل، مشخص می‌کنند که کدام کامپوننت از کدام برنامه، اینتنت را برآورده می‌کند. شما معمولاً از یک اینتنت صریح برای شروع یک کامپوننت در برنامه خود استفاده می‌کنید، زیرا نام کلاس اکتیویتی یا سرویسی را که می‌خواهید شروع کنید می‌دانید. به عنوان مثال، ممکن است در پاسخ به یک اقدام کاربر، یک اکتیویتی جدید را در برنامه خود شروع کنید، یا یک سرویس را برای دانلود یک فایل در پس‌زمینه شروع کنید.
  • اینتنت‌های ضمنی، یک کامپوننت خاص را نامگذاری نمی‌کنند، بلکه در عوض یک عمل کلی را برای اجرا اعلام می‌کنند که به یک کامپوننت از برنامه‌ی دیگر اجازه می‌دهد آن را مدیریت کند. برای مثال، اگر می‌خواهید مکانی را روی نقشه به کاربر نشان دهید، می‌توانید از یک اینتنت ضمنی برای درخواست از یک برنامه‌ی توانمند دیگر برای نمایش یک مکان مشخص روی نقشه استفاده کنید.

شکل ۱ نحوه استفاده از یک intent هنگام شروع یک activity را نشان می‌دهد. وقتی شیء Intent یک جزء activity خاص را به صراحت نامگذاری می‌کند، سیستم بلافاصله آن جزء را شروع می‌کند.

شکل 1. نحوه‌ی ارائه‌ی یک intent ضمنی از طریق سیستم برای شروع یک activity دیگر: [1] Activity A یک Intent با شرح action ایجاد می‌کند و آن را به startActivity() ارسال می‌کند. [2] سیستم اندروید تمام برنامه‌ها را برای یافتن یک فیلتر intent که با intent مطابقت داشته باشد، جستجو می‌کند. هنگامی که یک تطابق پیدا شد، [3] سیستم activity منطبق ( Activity B ) را با فراخوانی متد onCreate() و ارسال Intent به آن، آغاز می‌کند.

وقتی از یک intent ضمنی استفاده می‌کنید، سیستم اندروید با مقایسه‌ی محتوای intent با فیلترهای intent تعریف‌شده در فایل manifest سایر برنامه‌های روی دستگاه، کامپوننت مناسب برای شروع را پیدا می‌کند. اگر intent با یک فیلتر intent مطابقت داشته باشد، سیستم آن کامپوننت را اجرا کرده و شیء Intent را به آن تحویل می‌دهد. اگر چندین فیلتر intent سازگار باشند، سیستم یک کادر محاوره‌ای نمایش می‌دهد تا کاربر بتواند برنامه‌ی مورد نظر خود را انتخاب کند.

یک فیلتر intent عبارتی در فایل مانیفست یک برنامه است که نوع intentهایی را که کامپوننت می‌خواهد دریافت کند، مشخص می‌کند. برای مثال، با اعلام یک فیلتر intent برای یک activity، به برنامه‌های دیگر این امکان را می‌دهید که activity شما را مستقیماً با نوع خاصی از intent شروع کنند. به همین ترتیب، اگر هیچ فیلتر intent برای یک activity اعلام نکنید ، آن activity فقط می‌تواند با یک intent صریح شروع شود.

احتیاط: برای اطمینان از ایمن بودن برنامه خود، همیشه هنگام شروع یک Service از یک intent صریح استفاده کنید و فیلترهای intent را برای سرویس‌های خود تعریف نکنید. استفاده از intent ضمنی برای شروع یک سرویس یک خطر امنیتی است زیرا نمی‌توانید مطمئن باشید که کدام سرویس به intent پاسخ خواهد داد و کاربر نمی‌تواند ببیند کدام سرویس شروع می‌شود. از اندروید ۵.۰ (سطح API ۲۱)، اگر bindService() با intent ضمنی فراخوانی کنید، سیستم یک exception ایجاد می‌کند.

ساختن یک هدف

یک شیء Intent حاوی اطلاعاتی است که سیستم اندروید از آنها برای تعیین کامپوننتی که باید شروع به کار کند (مانند نام دقیق کامپوننت یا دسته کامپوننتی که باید intent را دریافت کند) استفاده می‌کند، به علاوه اطلاعاتی که کامپوننت گیرنده برای انجام صحیح عمل (مانند عملی که باید انجام شود و داده‌هایی که باید بر اساس آنها عمل شود) از آنها استفاده می‌کند.

اطلاعات اصلی موجود در یک Intent به شرح زیر است:

نام کامپوننت
نام کامپوننتی که قرار است شروع شود.

این اختیاری است، اما بخش مهمی از اطلاعات است که یک intent را صریح می‌کند، به این معنی که intent باید فقط به کامپوننت برنامه که توسط نام کامپوننت تعریف شده است، تحویل داده شود. بدون نام کامپوننت، intent ضمنی است و سیستم تصمیم می‌گیرد که کدام کامپوننت باید intent را بر اساس سایر اطلاعات intent (مانند action، data و category - که در زیر توضیح داده شده است) دریافت کند. اگر نیاز به شروع یک کامپوننت خاص در برنامه خود دارید، باید نام کامپوننت را مشخص کنید.

نکته: هنگام شروع یک Service ، همیشه نام کامپوننت را مشخص کنید . در غیر این صورت، نمی‌توانید مطمئن باشید که کدام سرویس به intent پاسخ خواهد داد و کاربر نمی‌تواند ببیند کدام سرویس شروع می‌شود.

این فیلد از Intent یک شیء ComponentName است که می‌توانید آن را با استفاده از نام کلاس کامل کامپوننت هدف، شامل نام پکیج برنامه، مثلاً com.example.ExampleActivity ، مشخص کنید. می‌توانید نام کامپوننت را با setComponent() ، setClass() ، setClassName() یا با سازنده‌ی Intent تنظیم کنید.

اکشن
رشته‌ای که عمل عمومی مورد نظر برای اجرا (مانند view یا pick ) را مشخص می‌کند.

در مورد قصد پخش، این عملی است که انجام شده و گزارش می‌شود. این عمل تا حد زیادی نحوه ساختار بقیه قصد را تعیین می‌کند - به ویژه اطلاعاتی که در داده‌ها و موارد اضافی موجود است.

شما می‌توانید اکشن‌های خودتان را برای استفاده توسط intentها در برنامه‌تان (یا برای استفاده توسط برنامه‌های دیگر برای فراخوانی کامپوننت‌ها در برنامه‌تان) مشخص کنید، اما معمولاً ثابت‌های اکشن را که توسط کلاس Intent یا سایر کلاس‌های فریم‌ورک تعریف شده‌اند، مشخص می‌کنید. در اینجا چند اکشن رایج برای شروع یک activity آورده شده است:

ACTION_VIEW
از این اکشن در یک intent با startActivity() زمانی استفاده کنید که اطلاعاتی دارید که یک activity می‌تواند به کاربر نشان دهد، مانند عکسی برای مشاهده در یک برنامه گالری یا آدرسی برای مشاهده در یک برنامه نقشه.
ACTION_SEND
این قابلیت که با نام اشتراک‌گذاری نیز شناخته می‌شود، زمانی که داده‌هایی دارید که کاربر می‌تواند از طریق برنامه‌ی دیگری مانند برنامه‌ی ایمیل یا برنامه‌ی اشتراک‌گذاری شبکه‌های اجتماعی به اشتراک بگذارد، باید در یک اینتنت با startActivity() استفاده شود.

برای مشاهده‌ی ثابت‌های بیشتری که اقدامات عمومی را تعریف می‌کنند، به مرجع کلاس Intent مراجعه کنید. سایر اقدامات در جای دیگری از چارچوب اندروید تعریف شده‌اند، مانند Settings برای اقداماتی که صفحات خاصی را در برنامه‌ی تنظیمات سیستم باز می‌کنند.

شما می‌توانید اکشن (عملکرد) یک intent را با استفاده از setAction() یا با استفاده از سازنده‌ی Intent مشخص کنید.

اگر اکشن‌های خودتان را تعریف می‌کنید، حتماً نام پکیج برنامه‌تان را به عنوان پیشوند وارد کنید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
داده‌ها
URI (یک شیء Uri ) که به داده‌هایی که باید روی آنها عمل شود و/یا نوع MIME آن داده‌ها اشاره دارد. نوع داده ارائه شده معمولاً توسط عمل intent تعیین می‌شود. برای مثال، اگر عمل ACTION_EDIT باشد، داده‌ها باید حاوی URI سندی باشند که باید ویرایش شود.

هنگام ایجاد یک intent، اغلب مشخص کردن نوع داده (نوع MIME آن) علاوه بر URI آن مهم است. به عنوان مثال، یک activity که قادر به نمایش تصاویر است، احتمالاً قادر به پخش یک فایل صوتی نخواهد بود، حتی اگر فرمت‌های URI مشابه باشند. مشخص کردن نوع MIME داده‌های شما به سیستم اندروید کمک می‌کند تا بهترین کامپوننت را برای دریافت intent شما پیدا کند. با این حال، نوع MIME گاهی اوقات می‌تواند از URI استنباط شود - به خصوص زمانی که داده‌ها از نوع content: URI باشند. content: URI نشان می‌دهد که داده‌ها در دستگاه قرار دارند و توسط یک ContentProvider کنترل می‌شوند، که باعث می‌شود نوع MIME داده‌ها برای سیستم قابل مشاهده باشد.

برای تنظیم فقط URI داده، تابع setData() را فراخوانی کنید. برای تنظیم فقط نوع MIME، تابع setType() را فراخوانی کنید. در صورت لزوم، می‌توانید هر دو را به طور صریح با setDataAndType() تنظیم کنید.

احتیاط: اگر می‌خواهید هم نوع URI و هم نوع MIME را تنظیم کنید، setData() و setType() را فراخوانی نکنید زیرا هر کدام مقدار دیگری را خنثی می‌کنند. همیشه از setDataAndType() برای تنظیم هر دو نوع URI و MIME استفاده کنید.

دسته بندی
رشته‌ای حاوی اطلاعات اضافی در مورد نوع کامپوننتی که باید intent را مدیریت کند. هر تعداد توصیف دسته‌بندی را می‌توان در یک intent قرار داد، اما اکثر intentها نیازی به دسته‌بندی ندارند. در اینجا چند دسته‌بندی رایج آورده شده است:
CATEGORY_BROWSABLE
اکتیویتی هدف به خود اجازه می‌دهد تا توسط یک مرورگر وب شروع به کار کند تا داده‌های ارجاع شده توسط یک لینک، مانند یک تصویر یا یک پیام ایمیل، را نمایش دهد.
CATEGORY_LAUNCHER
این فعالیت، فعالیت اولیه‌ی یک وظیفه است و در لانچر برنامه‌ی سیستم فهرست شده است.

برای مشاهده‌ی لیست کامل دسته‌ها، به توضیحات کلاس Intent مراجعه کنید.

شما می‌توانید با استفاده از addCategory() یک دسته‌بندی مشخص کنید.

این ویژگی‌های ذکر شده در بالا (نام کامپوننت، اکشن، داده و دسته‌بندی) ویژگی‌های تعریف‌شده‌ی یک intent را نشان می‌دهند. با خواندن این ویژگی‌ها، سیستم اندروید قادر است تشخیص دهد که کدام کامپوننت برنامه باید شروع شود. با این حال، یک intent می‌تواند اطلاعات اضافی را حمل کند که بر نحوه‌ی اتصال آن به یک کامپوننت برنامه تأثیری ندارد. یک intent همچنین می‌تواند اطلاعات زیر را ارائه دهد:

موارد اضافی
جفت‌های کلید-مقدار که اطلاعات اضافی مورد نیاز برای انجام عمل درخواستی را حمل می‌کنند. همانطور که برخی از اقدامات از انواع خاصی از URIهای داده استفاده می‌کنند، برخی از اقدامات نیز از موارد اضافی خاصی استفاده می‌کنند.

شما می‌توانید داده‌های اضافی را با متدهای مختلف putExtra() اضافه کنید که هر کدام دو پارامتر می‌پذیرند: نام کلید و مقدار. همچنین می‌توانید یک شیء Bundle با تمام داده‌های اضافی ایجاد کنید، سپس Bundle با putExtras() در Intent وارد کنید.

برای مثال، هنگام ایجاد یک اینتنت برای ارسال ایمیل با ACTION_SEND ، می‌توانید گیرنده را با کلید EXTRA_EMAIL و موضوع را با کلید EXTRA_SUBJECT مشخص کنید.

کلاس Intent ثابت‌های EXTRA_* زیادی را برای انواع داده‌های استاندارد مشخص می‌کند. اگر نیاز دارید کلیدهای اضافی خودتان را (برای Intentهایی که برنامه‌تان دریافت می‌کند) تعریف کنید، حتماً نام بسته برنامه‌تان را به عنوان پیشوند وارد کنید، همانطور که در مثال زیر نشان داده شده است:

کاتلین

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

جاوا

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

هشدار : هنگام ارسال یک intent که انتظار دارید برنامه دیگری آن را دریافت کند، از داده‌های Parcelable یا Serializable استفاده نکنید. اگر برنامه‌ای سعی کند به داده‌های یک شیء Bundle دسترسی پیدا کند اما به کلاس parceled یا serialized دسترسی نداشته باشد، سیستم خطای RuntimeException صادر می‌کند.

پرچم‌ها
پرچم‌ها (Flag) در کلاس Intent تعریف می‌شوند که به عنوان فراداده (metadata) برای intent عمل می‌کنند. این پرچم‌ها ممکن است به سیستم اندروید دستور دهند که چگونه یک activity را راه‌اندازی کند (برای مثال، activity باید به کدام task تعلق داشته باشد) و چگونه پس از راه‌اندازی با آن رفتار کند (برای مثال، آیا در لیست فعالیت‌های اخیر قرار دارد یا خیر).

برای اطلاعات بیشتر، به متد setFlags() مراجعه کنید.

مثالی از قصد صریح

یک intent صریح، intentی است که شما برای راه اندازی یک کامپوننت خاص برنامه، مانند یک activity یا service خاص در برنامه خود، از آن استفاده می کنید. برای ایجاد یک intent صریح، نام کامپوننت را برای شیء Intent تعریف کنید - سایر ویژگی های intent اختیاری هستند.

برای مثال، اگر در برنامه خود سرویسی به نام DownloadService ساخته‌اید که برای دانلود فایل از وب طراحی شده است، می‌توانید آن را با کد زیر شروع کنید:

کاتلین

// 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)

جاوا

// 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 می‌دهد. به این ترتیب، این intent به صراحت کلاس DownloadService را در برنامه شروع می‌کند.

برای اطلاعات بیشتر در مورد ساخت و شروع یک سرویس، به راهنمای سرویس‌ها مراجعه کنید.

مثالی از قصد ضمنی

یک intent ضمنی، عملی را مشخص می‌کند که می‌تواند هر برنامه‌ای را روی دستگاه که قادر به انجام آن عمل است، فراخوانی کند. استفاده از intent ضمنی زمانی مفید است که برنامه شما نمی‌تواند آن عمل را انجام دهد، اما برنامه‌های دیگر احتمالاً می‌توانند و شما می‌خواهید کاربر انتخاب کند که از کدام برنامه استفاده کند.

برای مثال، اگر محتوایی دارید که می‌خواهید کاربر آن را با دیگران به اشتراک بگذارد، با استفاده از اکشن ACTION_SEND یک اینتنت ایجاد کنید و موارد اضافی که محتوای مورد نظر برای اشتراک‌گذاری را مشخص می‌کنند، اضافه کنید. وقتی startActivity() با آن اینتنت فراخوانی می‌کنید، کاربر می‌تواند برنامه‌ای را انتخاب کند که از طریق آن محتوا را به اشتراک بگذارد.

کاتلین

// 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.
}

جاوا

// 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() فراخوانی می‌شود، سیستم تمام برنامه‌های نصب شده را بررسی می‌کند تا مشخص کند کدام یک می‌توانند این نوع intent (intent با اکشن ACTION_SEND و حامل داده‌های "text/plain") را مدیریت کنند. اگر فقط یک برنامه بتواند آن را مدیریت کند، آن برنامه بلافاصله باز می‌شود و intent به آن داده می‌شود. اگر هیچ برنامه دیگری نتواند آن را مدیریت کند، برنامه شما می‌تواند ActivityNotFoundException که رخ می‌دهد را دریافت کند. اگر چندین activity این intent را بپذیرند، سیستم کادر محاوره‌ای مانند شکل 2 را نمایش می‌دهد، بنابراین کاربر می‌تواند انتخاب کند که از کدام برنامه استفاده کند.

اطلاعات بیشتر در مورد راه‌اندازی برنامه‌های دیگر نیز در راهنمای مربوط به ارسال کاربر به برنامه دیگر ارائه شده است.

شکل ۲. یک پنجره انتخابگر.

اجبار به انتخاب برنامه

وقتی بیش از یک برنامه وجود دارد که به قصد ضمنی شما پاسخ می‌دهد، کاربر می‌تواند برنامه‌ای را که می‌خواهد استفاده کند انتخاب کند و آن برنامه را به عنوان گزینه پیش‌فرض برای عمل قرار دهد. امکان انتخاب یک برنامه پیش‌فرض هنگام انجام عملی که کاربر احتمالاً می‌خواهد هر بار از همان برنامه استفاده کند، مفید است، مانند هنگام باز کردن یک صفحه وب (کاربران اغلب فقط یک مرورگر وب را ترجیح می‌دهند).

با این حال، اگر چندین برنامه می‌توانند به intent پاسخ دهند و کاربر ممکن است بخواهد هر بار از برنامه‌ی متفاوتی استفاده کند، باید به صراحت یک کادر محاوره‌ای انتخابگر نمایش دهید. کادر محاوره‌ای انتخابگر از کاربر می‌خواهد که برنامه‌ی مورد نظر برای این عمل را انتخاب کند (کاربر نمی‌تواند یک برنامه‌ی پیش‌فرض برای این عمل انتخاب کند). به عنوان مثال، وقتی برنامه‌ی شما با عمل ACTION_SEND عمل "اشتراک‌گذاری" را انجام می‌دهد، کاربران ممکن است بسته به وضعیت فعلی خود بخواهند با استفاده از یک برنامه‌ی متفاوت اشتراک‌گذاری کنند، بنابراین شما همیشه باید از کادر محاوره‌ای انتخابگر، همانطور که در شکل 2 نشان داده شده است، استفاده کنید.

To show the chooser, create an Intent using createChooser() and pass it to startActivity() , as shown in the following example. This example displays a dialog with a list of apps that respond to the intent passed to the createChooser() method and uses the supplied text as the dialog title.

کاتلین

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)
}

جاوا

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);
}

تشخیص اجرای نیت‌های ناامن

برنامه شما ممکن است اینتنت‌ها را برای پیمایش بین اجزای داخل برنامه یا انجام عملی از طرف یک برنامه دیگر اجرا کند. برای بهبود امنیت پلتفرم، اندروید ۱۲ (سطح API 31) و بالاتر یک ویژگی اشکال‌زدایی ارائه می‌دهد که اگر برنامه شما یک اینتنت را به صورت ناامن اجرا کند، به شما هشدار می‌دهد. به عنوان مثال، برنامه شما ممکن است یک اینتنت تو در تو را به صورت ناامن اجرا کند، که اینتنتی است که به عنوان یک اینتنت اضافی در یک اینتنت دیگر ارسال می‌شود.

اگر برنامه شما هر دو اقدام زیر را انجام دهد، سیستم اجرای یک intent ناامن را تشخیص می‌دهد و نقض StrictMode رخ می‌دهد:

  1. برنامه شما یک intent تو در تو را از موارد اضافی یک intent تحویل داده شده جدا می‌کند.
  2. برنامه شما بلافاصله با استفاده از آن intent تو در تو، مانند ارسال intent به startActivity() ، startService() یا bindService() ، یک کامپوننت برنامه را اجرا می‌کند.

برای جزئیات بیشتر در مورد نحوه شناسایی این وضعیت و ایجاد تغییرات در برنامه خود، پست وبلاگ در مورد Android Nesting Intents را در Medium بخوانید.

بررسی راه‌اندازی‌های با هدف ناامن

برای بررسی اجرای Intentهای ناامن در برنامه خود، هنگام پیکربندی VmPolicy ، تابع detectUnsafeIntentLaunch() را فراخوانی کنید، همانطور که در قطعه کد زیر نشان داده شده است. اگر برنامه شما نقض StrictMode را تشخیص دهد، ممکن است بخواهید اجرای برنامه را برای محافظت از اطلاعات حساس بالقوه متوقف کنید.

کاتلین

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

جاوا

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 به intent دیگری که برای راه اندازی یک کامپوننت جدید استفاده می‌شود، کپی کند. این اتفاق زمانی رخ می‌دهد که برنامه شما putExtras(Intent) یا putExtras(Bundle) فراخوانی می‌کند. اگر برنامه شما یکی از این عملیات‌ها را انجام می‌دهد، فقط موارد اضافی را که کامپوننت گیرنده انتظار دارد کپی کنید. اگر intent دیگر (که کپی را دریافت می‌کند) کامپوننتی را راه اندازی می‌کند که export نشده است، موارد اضافی را قبل از کپی کردن آنها به intent که کامپوننت را راه اندازی می‌کند، پاکسازی و اعتبارسنجی کنید.

اجزای برنامه خود را بی‌جهت export نکنید. برای مثال، اگر قصد دارید یک جزء برنامه را با استفاده از یک intent تو در تو داخلی راه‌اندازی کنید، ویژگی android:exported آن جزء را روی false تنظیم کنید.

به جای یک intent تو در تو، از یک PendingIntent استفاده کنید. به این ترتیب، وقتی برنامه دیگری PendingIntent مربوط به Intent حاوی خود را unparcel می‌کند، برنامه دیگر می‌تواند PendingIntent با استفاده از هویت برنامه شما اجرا کند. این پیکربندی به برنامه دیگر اجازه می‌دهد تا هر کامپوننتی، از جمله کامپوننت‌های export نشده، را به طور ایمن در برنامه شما اجرا کند.

نمودار شکل ۲ نشان می‌دهد که چگونه سیستم، کنترل را از برنامه (کلاینت) شما به برنامه دیگر (سرویس) و دوباره به برنامه شما منتقل می‌کند:

  1. برنامه شما یک intent ایجاد می‌کند که یک activity را در برنامه دیگری فراخوانی می‌کند. درون آن intent، یک شیء PendingIntent به عنوان یک شیء اضافی اضافه می‌کنید. این intent در حال انتظار، یک کامپوننت را در برنامه شما فراخوانی می‌کند؛ این کامپوننت export نشده است.
  2. پس از دریافت intent برنامه شما، برنامه دیگر شیء PendingIntent تو در تو را استخراج می‌کند.
  3. برنامه‌ی دیگر، متد send() را روی شیء PendingIntent فراخوانی می‌کند.
  4. پس از بازگرداندن کنترل به برنامه شما، سیستم با استفاده از زمینه برنامه شما، قصد (intent) در حال انتظار (pending intent) را فراخوانی می‌کند.

شکل ۲. نمودار ارتباط بین برنامه‌ها هنگام استفاده از یک intent در حال انتظار تو در تو.

دریافت یک قصد ضمنی

برای اعلام اینکه برنامه شما کدام intent های ضمنی را می‌تواند دریافت کند، یک یا چند intent filter برای هر یک از اجزای برنامه خود با عنصر <intent-filter> در فایل مانیفست خود تعریف کنید. هر intent filter نوع intent هایی را که می‌پذیرد بر اساس عملکرد، داده و دسته intent مشخص می‌کند. سیستم فقط در صورتی intent ضمنی را به جزء برنامه شما ارائه می‌دهد که intent بتواند از یکی از فیلترهای intent شما عبور کند.

نکته: یک intent صریح، صرف نظر از هرگونه فیلتر intent که کامپوننت اعلام می‌کند، همیشه به مقصد خود تحویل داده می‌شود.

یک کامپوننت برنامه باید برای هر کار منحصر به فردی که می‌تواند انجام دهد، فیلترهای جداگانه‌ای تعریف کند. برای مثال، یک اکتیویتی در یک برنامه گالری تصاویر ممکن است دو فیلتر داشته باشد: یک فیلتر برای مشاهده تصویر و فیلتر دیگر برای ویرایش تصویر. وقتی اکتیویتی شروع می‌شود، Intent را بررسی می‌کند و بر اساس اطلاعات موجود در Intent تصمیم می‌گیرد که چگونه رفتار کند (مانند نمایش یا عدم نمایش کنترل‌های ویرایشگر).

هر فیلتر intent توسط یک عنصر <intent-filter> در فایل manifest برنامه تعریف می‌شود که در کامپوننت برنامه مربوطه (مانند عنصر <activity> ) قرار دارد.

در هر کامپوننت برنامه که شامل یک عنصر <intent-filter> است، به طور صریح مقداری برای android:exported تعیین کنید. این ویژگی نشان می‌دهد که آیا کامپوننت برنامه برای سایر برنامه‌ها قابل دسترسی است یا خیر. در برخی موقعیت‌ها، مانند فعالیت‌هایی که فیلترهای intent آنها شامل دسته LAUNCHER است، تنظیم این ویژگی روی true مفید است. در غیر این صورت، تنظیم این ویژگی روی false ایمن‌تر است.

هشدار: اگر یک اکتیویتی، سرویس یا گیرنده پخش در برنامه شما از فیلترهای intent استفاده می‌کند و مقدار android:exported را به صراحت تعیین نمی‌کند، برنامه شما نمی‌تواند روی دستگاهی که اندروید ۱۲ یا بالاتر را اجرا می‌کند نصب شود.

درون <intent-filter> ، می‌توانید نوع intentهایی که می‌خواهید بپذیرید را با استفاده از یک یا چند مورد از این سه عنصر مشخص کنید:

<action>
عمل مورد نظر (intent action) را در ویژگی name اعلام می‌کند. مقدار باید مقدار رشته‌ای تحت‌اللفظی یک عمل باشد، نه ثابت کلاس.
<data>
نوع داده پذیرفته شده را با استفاده از یک یا چند ویژگی که جنبه‌های مختلف URI داده ( scheme ، host ، port ، path ) و نوع MIME را مشخص می‌کنند، اعلام می‌کند.
<category>
دسته‌ی intent پذیرفته شده را در ویژگی name اعلام می‌کند. مقدار باید مقدار رشته‌ای تحت‌اللفظی یک action باشد، نه ثابت کلاس.

نکته: برای دریافت intent های ضمنی، باید دسته CATEGORY_DEFAULT را در فیلتر intent قرار دهید. متدهای startActivity() و startActivityForResult() با تمام intent ها طوری رفتار می کنند که گویی دسته CATEGORY_DEFAULT را تعریف کرده اند. اگر این دسته را در فیلتر intent خود تعریف نکنید، هیچ intent ضمنی به activity شما اعمال نخواهد شد.

برای مثال، در اینجا یک تعریف فعالیت با یک فیلتر intent برای دریافت یک intent 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> باشد. در این صورت، باید مطمئن شوید که کامپوننت می‌تواند هر ترکیبی از این عناصر فیلتر را مدیریت کند.

وقتی می‌خواهید چندین نوع intent را مدیریت کنید، اما فقط در ترکیب‌های خاصی از action، data و category، باید چندین intent filter ایجاد کنید.

یک intent ضمنی با مقایسه intent با هر یک از سه عنصر، در برابر یک فیلتر آزمایش می‌شود. برای تحویل به کامپوننت، intent باید هر سه آزمایش را با موفقیت پشت سر بگذارد. اگر حتی با یکی از آنها مطابقت نداشته باشد، سیستم اندروید intent را به کامپوننت تحویل نمی‌دهد. با این حال، از آنجا که یک کامپوننت ممکن است چندین فیلتر intent داشته باشد، intent ای که از یکی از فیلترهای کامپوننت عبور نمی‌کند، ممکن است از طریق فیلتر دیگری عبور کند. اطلاعات بیشتر در مورد نحوه حل intent ها توسط سیستم در بخش زیر در مورد Intent Resolution ارائه شده است.

احتیاط: استفاده از فیلتر intent راه امنی برای جلوگیری از اجرای کامپوننت‌های شما توسط سایر برنامه‌ها نیست. اگرچه فیلترهای intent یک کامپوننت را محدود می‌کنند تا فقط به انواع خاصی از intentهای ضمنی پاسخ دهد، اما اگر توسعه‌دهنده نام کامپوننت‌های شما را تعیین کند، برنامه دیگری می‌تواند با استفاده از intent صریح، کامپوننت برنامه شما را اجرا کند. اگر مهم است که فقط برنامه خودتان بتواند یکی از کامپوننت‌های شما را اجرا کند، فیلترهای intent را در مانیفست خود اعلام نکنید. در عوض، ویژگی exported را برای آن کامپوننت روی "false" تنظیم کنید.

به طور مشابه، برای جلوگیری از اجرای ناخواسته‌ی Service یک برنامه‌ی دیگر، همیشه از یک اینتنت صریح برای شروع سرویس خود استفاده کنید.

نکته: برای همه فعالیت‌ها، باید فیلترهای intent خود را در فایل مانیفست تعریف کنید. با این حال، فیلترهای مربوط به گیرنده‌های اعلان می‌توانند به صورت پویا با فراخوانی 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 وارد آن شوند، اما می‌توانند مستقیماً از برنامه دیگری که یک intent ضمنی مطابق با یکی از دو فیلتر intent صادر می‌کند، وارد ShareActivity شوند.

نکته: نوع MIME، application/vnd.google.panorama360+jpg ، یک نوع داده خاص است که عکس‌های پانوراما را مشخص می‌کند و می‌توانید با APIهای پانورامای گوگل آنها را مدیریت کنید.

تطبیق اهداف با فیلترهای هدف برنامه‌های دیگر

اگر برنامه‌ی دیگری اندروید ۱۳ (سطح API ۳۳) یا بالاتر را هدف قرار دهد، تنها در صورتی می‌تواند intent برنامه‌ی شما را مدیریت کند که intent شما با اکشن‌ها و دسته‌بندی‌های یک عنصر <intent-filter> در آن برنامه‌ی دیگر مطابقت داشته باشد. اگر سیستم تطابقی پیدا نکند، یک ActivityNotFoundException صادر می‌کند. برنامه‌ی فرستنده باید این exception را مدیریت کند.

به طور مشابه، اگر برنامه خود را طوری به‌روزرسانی کنید که برای اندروید ۱۳ یا بالاتر مناسب باشد، تمام intent های نشأت گرفته از برنامه‌های خارجی فقط در صورتی به یک کامپوننت export شده از برنامه شما تحویل داده می‌شوند که آن intent با اقدامات و دسته‌های یک عنصر <intent-filter> که برنامه شما اعلام می‌کند، مطابقت داشته باشد. این رفتار صرف نظر از نسخه SDK هدف برنامه فرستنده رخ می‌دهد.

در موارد زیر، تطبیق هدف اعمال نمی‌شود:

  • اینتنت‌هایی که به کامپوننت‌هایی تحویل داده می‌شوند که هیچ فیلتر اینتنتی را تعریف نکرده‌اند.
  • اینتنت‌هایی که از درون همان برنامه سرچشمه می‌گیرند.
  • اینتنت‌هایی که از سیستم سرچشمه می‌گیرند؛ یعنی، اینتنت‌هایی که از "شناسه کاربری سیستم" (uid=1000) ارسال می‌شوند. برنامه‌های سیستمی شامل system_server و برنامه‌هایی هستند که android:sharedUserId را روی android.uid.system تنظیم می‌کنند.
  • اینتنت‌هایی که از ریشه سرچشمه می‌گیرند.

درباره تطبیق قصد بیشتر بدانید.

استفاده از یک قصد در حال انتظار

یک شیء PendingIntent یک پوشش (wrapper) در اطراف یک شیء Intent است. هدف اصلی یک PendingIntent اعطای مجوز به یک برنامه خارجی برای استفاده از Intent موجود است، گویی که از فرآیند خود برنامه شما اجرا شده است.

موارد استفاده عمده برای یک قصد در حال انتظار شامل موارد زیر است:

  • اعلان یک اینتنت برای اجرا هنگامی که کاربر عملی را با Notification شما انجام می‌دهد ( NotificationManager سیستم اندروید، Intent اجرا می‌کند).
  • اعلان یک اینتنت برای اجرا زمانی که کاربر عملی را با ویجت برنامه شما انجام می‌دهد (برنامه صفحه اصلی Intent اجرا می‌کند).
  • اعلان یک intent برای اجرا در زمان مشخص در آینده ( AlarmManager سیستم اندروید Intent را اجرا می‌کند).

همانطور که هر شیء Intent طوری طراحی شده است که توسط نوع خاصی از کامپوننت برنامه (یا یک Activity ، یک Service یا یک BroadcastReceiver ) مدیریت شود، یک PendingIntent نیز باید با همین ملاحظات ایجاد شود. هنگام استفاده از یک pending intent، برنامه شما intent را با فراخوانی مانند startActivity() اجرا نمی‌کند. در عوض، شما باید هنگام ایجاد PendingIntent با فراخوانی متد creator مربوطه، نوع کامپوننت مورد نظر را اعلام کنید:

مگر اینکه برنامه شما intent های در حال انتظار را از برنامه های دیگر دریافت کند ، روش های فوق برای ایجاد یک PendingIntent احتمالاً تنها روش های PendingIntent هستند که شما همیشه به آنها نیاز خواهید داشت.

هر متد، Context برنامه فعلی، Intent مورد نظر برای پوشش دادن، و یک یا چند flag که نحوه استفاده از intent را مشخص می‌کنند (مانند اینکه آیا intent می‌تواند بیش از یک بار استفاده شود) را دریافت می‌کند.

برای اطلاعات بیشتر در مورد استفاده از intent های در حال انتظار، به مستندات مربوط به هر یک از موارد استفاده مربوطه، مانند راهنماهای API اعلان‌ها و ابزارک‌های برنامه ، مراجعه کنید.

مشخص کردن تغییرپذیری

اگر برنامه شما اندروید ۱۲ یا بالاتر را هدف قرار می‌دهد، باید قابلیت تغییرپذیری هر شیء 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 تغییرناپذیر باشد، سایر برنامه‌ها نمی‌توانند intent را تغییر دهند تا نتیجه فراخوانی intent را تنظیم کنند.

کاتلین

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

جاوا

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() یا API های مشابه. شیء قابل تغییر PendingIntent به سیستم اجازه می‌دهد تا موارد اضافی intent را که نشان دهنده رویدادهای چرخه حیات موقعیت مکانی هستند، اضافه کند. این رویدادها شامل تغییر در موقعیت مکانی و در دسترس قرار گرفتن یک ارائه دهنده می‌شود.
  • زمان‌بندی آلارم‌ها با استفاده از AlarmManager . شیء قابل تغییر PendingIntent به سیستم اجازه می‌دهد تا اینتنت EXTRA_ALARM_COUNT را به صورت اضافی اضافه کند. این مقدار اضافی نشان دهنده تعداد دفعاتی است که یک آلارم تکرارشونده فعال شده است. با داشتن این مقدار اضافی، اینتنت می‌تواند به طور دقیق به برنامه اطلاع دهد که آیا یک آلارم تکرارشونده چندین بار فعال شده است یا خیر، مثلاً زمانی که دستگاه در حالت خواب بوده است.

اگر برنامه شما یک شیء PendingIntent قابل تغییر ایجاد می‌کند، اکیداً توصیه می‌شود که از یک intent صریح استفاده کنید و ComponentName وارد کنید. به این ترتیب، هر زمان که برنامه دیگری PendingIntent فراخوانی کند و کنترل را به برنامه شما بازگرداند، همان کامپوننت در برنامه شما همیشه شروع می‌شود.

استفاده از intentهای صریح در intentهای در حال انتظار

برای تعریف بهتر نحوه استفاده سایر برنامه‌ها از intentهای در حال انتظار برنامه شما، همیشه یک intent در حال انتظار را در اطراف یک intent صریح قرار دهید. برای کمک به پیروی از این روش، موارد زیر را انجام دهید:

  1. بررسی کنید که فیلدهای action، package و component مربوط به intent پایه تنظیم شده باشند.
  2. از FLAG_IMMUTABLE که در اندروید ۶.۰ (سطح API ۲۳) اضافه شده است، برای ایجاد intentهای در حال انتظار استفاده کنید. این flag مانع از پر کردن ویژگی‌های بدون مقدار توسط برنامه‌هایی می‌شود که PendingIntent دریافت می‌کنند. اگر minSdkVersion برنامه شما 22 یا پایین‌تر است، می‌توانید با استفاده از کد زیر، ایمنی و سازگاری را با هم فراهم کنید:

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

وضوح قصد

وقتی سیستم یک intent ضمنی برای شروع یک activity دریافت می‌کند، با مقایسه‌ی intent با فیلترهای intent بر اساس سه جنبه، بهترین activity را برای intent جستجو می‌کند:

  • اکشن.
  • داده‌ها (هم آدرس اینترنتی و هم نوع داده).
  • دسته بندی.

بخش‌های زیر نحوه تطبیق intentها با کامپوننت‌های مناسب را بر اساس تعریف فیلتر intent در فایل manifest برنامه شرح می‌دهند.

آزمون عمل

برای مشخص کردن اقدامات مورد قبول 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 عملی را مشخص نکند، تا زمانی که فیلتر حداقل شامل یک عمل باشد، آزمون را با موفقیت پشت سر می‌گذارد.

آزمون رده

برای مشخص کردن دسته‌های پذیرفته‌شده‌ی intent، یک فیلتر intent می‌تواند صفر یا چند عنصر <category> را تعریف کند، همانطور که در مثال زیر نشان داده شده است:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

For an intent to pass the category test, every category in the Intent must match a category in the filter. The reverse is not necessary—the intent filter may declare more categories than are specified in the Intent and the Intent still passes. Therefore, an intent with no categories always passes this test, regardless of what categories are declared in the filter.

Note: Android automatically applies the CATEGORY_DEFAULT category to all implicit intents passed to startActivity() and startActivityForResult() . If you want your activity to receive implicit intents, it must include a category for "android.intent.category.DEFAULT" in its intent filters, as shown in the previous <intent-filter> example.

Data test

To specify accepted intent data, an intent filter can declare zero or more <data> elements, as shown in the following example:

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

Each <data> element can specify a URI structure and a data type (MIME media type). Each part of the URI is a separate attribute: scheme , host , port , and path :

<scheme>://<host>:<port>/<path>

The following example shows possible values for these attributes:

content://com.example.project:200/folder/subfolder/etc

In this URI, the scheme is content , the host is com.example.project , the port is 200 , and the path is folder/subfolder/etc .

Each of these attributes is optional in a <data> element, but there are linear dependencies:

  • If a scheme is not specified, the host is ignored.
  • If a host is not specified, the port is ignored.
  • If both the scheme and host are not specified, the path is ignored.

When the URI in an intent is compared to a URI specification in a filter, it's compared only to the parts of the URI included in the filter. For example:

  • If a filter specifies only a scheme, all URIs with that scheme match the filter.
  • If a filter specifies a scheme and an authority but no path, all URIs with the same scheme and authority pass the filter, regardless of their paths.
  • If a filter specifies a scheme, an authority, and a path, only URIs with the same scheme, authority, and path pass the filter.

Note: A path specification can contain a wildcard asterisk (*) to require only a partial match of the path name.

The data test compares both the URI and the MIME type in the intent to a URI and MIME type specified in the filter. The rules are as follows:

  1. An intent that contains neither a URI nor a MIME type passes the test only if the filter does not specify any URIs or MIME types.
  2. An intent that contains a URI but no MIME type (neither explicit nor inferable from the URI) passes the test only if its URI matches the filter's URI format and the filter likewise does not specify a MIME type.
  3. An intent that contains a MIME type but not a URI passes the test only if the filter lists the same MIME type and does not specify a URI format.
  4. An intent that contains both a URI and a MIME type (either explicit or inferable from the URI) passes the MIME type part of the test only if that type matches a type listed in the filter. It passes the URI part of the test either if its URI matches a URI in the filter or if it has a content: or file: URI and the filter does not specify a URI. In other words, a component is presumed to support content: and file: data if its filter lists only a MIME type.

Note: If an intent specifies a URI or MIME type, the data test will fail if there are no <data> elements in the <intent-filter> .

This last rule, rule (d), reflects the expectation that components are able to get local data from a file or content provider. Therefore, their filters can list just a data type and don't need to explicitly name the content: and file: schemes. The following example shows a typical case in which a <data> element tells Android that the component can get image data from a content provider and display it:

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

Filters that specify a data type but not a URI are perhaps the most common because most available data is dispensed by content providers.

Another common configuration is a filter with a scheme and a data type. For example, a <data> element like the following tells Android that the component can retrieve video data from the network in order to perform the action:

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

Intent matching

Intents are matched against intent filters not only to discover a target component to activate, but also to discover something about the set of components on the device. For example, the Home app populates the app launcher by finding all the activities with intent filters that specify the ACTION_MAIN action and CATEGORY_LAUNCHER category. A match is only successful if the actions and categories in the Intent match against the filter, as described in the documentation for the IntentFilter class.

Your application can use intent matching in a manner similar to what the Home app does. The PackageManager has a set of query...() methods that return all components that can accept a particular intent and a similar series of resolve...() methods that determine the best component to respond to an intent. For example, queryIntentActivities() returns a list of all activities that can perform the intent passed as an argument, and queryIntentServices() returns a similar list of services. Neither method activates the components; they just list the ones that can respond. There's a similar method, queryBroadcastReceivers() , for broadcast receivers.