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

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

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

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

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

  • راه اندازی یک سرویس

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

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

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

  • ارائه پخش

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

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

انواع قصد

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

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

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

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

هنگامی که از یک intent ضمنی استفاده می کنید، سیستم Android مؤلفه مناسب را برای شروع با مقایسه محتوای intent با فیلترهای intent اعلام شده در فایل مانیفست سایر برنامه های دستگاه پیدا می کند. اگر intent با فیلتر intent مطابقت داشته باشد، سیستم آن مؤلفه را راه اندازی می کند و شی Intent را به آن تحویل می دهد. اگر چندین فیلتر هدف سازگار باشند، سیستم یک دیالوگ نمایش می دهد تا کاربر بتواند از کدام برنامه استفاده کند.

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

احتیاط: برای اطمینان از ایمن بودن برنامه‌تان، همیشه هنگام راه‌اندازی یک Service از یک هدف صریح استفاده کنید و فیلترهای هدف را برای سرویس‌های خود اعلام نکنید. استفاده از یک قصد ضمنی برای شروع یک سرویس یک خطر امنیتی است زیرا نمی توانید مطمئن باشید که چه سرویسی به این هدف پاسخ می دهد و کاربر نمی تواند ببیند کدام سرویس شروع می شود. با شروع Android 5.0 (سطح API 21)، اگر شما bindService() با یک هدف ضمنی فراخوانی کنید، سیستم یک استثنا ایجاد می کند.

ساختن یک قصد

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

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

نام جزء
نام جزء برای شروع.

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

توجه: هنگام راه اندازی یک Service ، همیشه نام مؤلفه را مشخص کنید . در غیر این صورت، نمی توانید مطمئن باشید که چه سرویسی به هدف پاسخ می دهد و کاربر نمی تواند ببیند کدام سرویس شروع می شود.

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

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

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

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

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

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

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

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

کاتلین

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

جاوا

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

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

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

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

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

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

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

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

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

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

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

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

کاتلین

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

جاوا

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

احتیاط : هنگام ارسال هدفی که انتظار دارید برنامه دیگری دریافت کند، از داده‌های Parcelable یا Serializable استفاده نکنید. اگر برنامه ای سعی کند به داده های موجود در یک شی Bundle دسترسی پیدا کند اما به کلاس parceled یا serialized دسترسی نداشته باشد، سیستم RuntimeException ایجاد می کند.

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

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

مثال قصد صریح

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

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

مثال قصد ضمنی

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

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

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

شکل 2. گفتگوی انتخابگر.

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

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

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

برای نشان دادن انتخابگر، با استفاده از createChooser() یک Intent ایجاد کنید و آن را به startActivity() ارسال کنید، همانطور که در مثال زیر نشان داده شده است. این مثال یک گفتگو با لیستی از برنامه هایی را نشان می دهد که به قصد ارسال شده به متد createChooser() پاسخ می دهند و از متن ارائه شده به عنوان عنوان گفتگو استفاده می کند.

کاتلین

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

راه‌اندازی‌های هدف ناامن را شناسایی کنید

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

اگر برنامه شما هر دو عملکرد زیر را انجام دهد، سیستم یک هدف ناامن را شناسایی می کند و یک نقض StrictMode رخ می دهد:

  1. برنامه شما یک هدف تودرتو را از موارد اضافی یک هدف ارائه شده جدا می کند.
  2. برنامه شما فوراً یک مؤلفه برنامه را با استفاده از آن هدف تودرتو، مانند ارسال 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) را صدا می کند. اگر برنامه شما یکی از این عملیات ها را انجام می دهد، فقط موارد اضافی را کپی کنید که مؤلفه دریافت کننده انتظار دارد. اگر هدف دیگر (که کپی را دریافت می‌کند) مؤلفه‌ای را راه‌اندازی می‌کند که صادر نشده است، موارد اضافی را قبل از کپی کردن آنها در هدفی که مؤلفه را راه‌اندازی می‌کند، ضدعفونی و اعتبارسنجی کنید.

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

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

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

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

شکل 2. نمودار ارتباط بین برنامه ای هنگام استفاده از یک هدف معلق تو در تو.

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

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

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

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

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

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

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

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

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

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

به عنوان مثال، در اینجا یک اعلامیه فعالیت با فیلتر قصد برای دریافت یک هدف ACTION_SEND زمانی که نوع داده متنی است آمده است:

<activity android:name="ShareActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

می‌توانید فیلتری ایجاد کنید که شامل بیش از یک نمونه از <action> ، <data> یا <category> باشد. اگر این کار را انجام می دهید، باید مطمئن باشید که کامپوننت می تواند هر یک و همه ترکیبات آن عناصر فیلتر را مدیریت کند.

هنگامی که می خواهید چندین نوع هدف را مدیریت کنید، اما فقط در ترکیبات خاصی از نوع عملکرد، داده و دسته، باید چندین فیلتر قصد ایجاد کنید.

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

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

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

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

فیلترهای نمونه

برای نشان دادن برخی از رفتارهای فیلتر قصد، در اینجا مثالی از فایل مانیفست یک برنامه اشتراک‌گذاری اجتماعی آورده شده است:

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

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

اولین اکتیویتی، MainActivity ، نقطه ورود اصلی برنامه است — فعالیتی که زمانی باز می شود که کاربر ابتدا برنامه را با نماد راه انداز راه اندازی می کند:

  • عمل ACTION_MAIN نشان می‌دهد که این نقطه ورودی اصلی است و انتظار هیچ داده‌ای را ندارد.
  • دسته CATEGORY_LAUNCHER نشان می‌دهد که نماد این فعالیت باید در راه‌انداز برنامه سیستم قرار گیرد. اگر عنصر <activity> نمادی را با icon مشخص نمی کند، سیستم از نماد عنصر <application> استفاده می کند.

این دو باید با هم جفت شوند تا فعالیت در راه‌انداز برنامه ظاهر شود.

دومین فعالیت، ShareActivity ، برای تسهیل اشتراک گذاری متن و محتوای رسانه ای در نظر گرفته شده است. اگرچه کاربران ممکن است با پیمایش به آن از MainActivity وارد این فعالیت شوند، اما می‌توانند مستقیماً از برنامه دیگری وارد ShareActivity شوند که یک هدف ضمنی مطابق با یکی از دو فیلتر هدف صادر می‌کند.

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

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

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

به طور مشابه، اگر برنامه خود را به‌گونه‌ای به‌روزرسانی کنید که Android 13 یا بالاتر را هدف قرار دهد، تمام اهدافی که از برنامه‌های خارجی نشأت می‌گیرند، تنها در صورتی به یکی از مؤلفه‌های صادر شده از برنامه شما تحویل داده می‌شوند که این هدف با عملکردها و دسته‌های عنصر <intent-filter> مطابقت داشته باشد. برنامه اعلام می کند. این رفتار بدون توجه به نسخه SDK هدف برنامه ارسال کننده رخ می دهد.

در موارد زیر، مطابقت قصد اجرا نمی شود:

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

درباره تطبیق قصد بیشتر بیاموزید.

استفاده از یک قصد معلق

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

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

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

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

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

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

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

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

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

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

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

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

  1. بررسی کنید که فیلدهای اقدام، بسته و مؤلفه در هدف پایه تنظیم شده باشند.
  2. از FLAG_IMMUTABLE ، اضافه شده در Android 6.0 (سطح API 23)، برای ایجاد اهداف معلق استفاده کنید. این پرچم از پر کردن ویژگی‌های خالی از برنامه‌هایی که PendingIntent دریافت می‌کنند جلوگیری می‌کند. اگر minSdkVersion برنامه شما 22 یا کمتر است، می توانید با استفاده از کد زیر ایمنی و سازگاری را با هم ارائه دهید:

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

تفکیک قصد

هنگامی که سیستم یک قصد ضمنی برای شروع یک فعالیت دریافت می کند، با مقایسه آن با فیلترهای هدف بر اساس سه جنبه، بهترین فعالیت را برای هدف جستجو می کند:

  • اقدام
  • داده ها (هم URI و هم نوع داده).
  • دسته بندی.

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

تست اقدام

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

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

برای عبور از این فیلتر، عمل مشخص شده در Intent باید با یکی از اقدامات لیست شده در فیلتر مطابقت داشته باشد.

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

تست دسته

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

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

برای اینکه یک هدف در آزمون دسته بندی موفق شود، هر دسته در Intent باید با یک دسته در فیلتر مطابقت داشته باشد. معکوس آن ضروری نیست—فیلتر intent ممکن است دسته های بیشتری از آنچه در Intent مشخص شده است را اعلام کند و Intent همچنان عبور می کند. بنابراین، یک هدف بدون دسته، صرف نظر از اینکه چه دسته‌هایی در فیلتر اعلان شده‌اند، همیشه از این آزمون عبور می‌کند.

توجه: Android به طور خودکار دسته CATEGORY_DEFAULT را برای تمام مقاصد ضمنی ارسال شده به startActivity() و startActivityForResult() اعمال می کند. اگر می‌خواهید فعالیت شما مقاصد ضمنی دریافت کند، باید دسته‌ای برای "android.intent.category.DEFAULT" در فیلترهای هدف خود داشته باشد، همانطور که در مثال قبلی <intent-filter> نشان داده شده است.

تست داده ها

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

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

هر عنصر <data> می تواند یک ساختار URI و یک نوع داده (نوع رسانه MIME) را مشخص کند. هر قسمت از URI یک ویژگی جداگانه است: scheme ، host ، port و path :

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

مثال زیر مقادیر ممکن را برای این ویژگی ها نشان می دهد:

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

در این URI ، این طرح content است ، میزبان com.example.project ، پورت 200 است ، و مسیر folder/subfolder/etc است.

هر یک از این ویژگی ها در یک عنصر <data> اختیاری است ، اما وابستگی های خطی وجود دارد:

  • اگر یک طرح مشخص نشده باشد ، میزبان نادیده گرفته می شود.
  • اگر یک میزبان مشخص نشده باشد ، بندر نادیده گرفته می شود.
  • اگر هر دو طرح و میزبان مشخص نشده باشند ، مسیر نادیده گرفته می شود.

هنگامی که URI در یک هدف با مشخصات URI در یک فیلتر مقایسه می شود ، فقط با قسمت های URI موجود در فیلتر مقایسه می شود. به عنوان مثال:

  • اگر یک فیلتر فقط یک طرح را مشخص کند ، تمام URI ها با آن طرح با فیلتر مطابقت دارند.
  • اگر یک فیلتر یک طرح و یک اقتدار را مشخص کند اما هیچ مسیری ندارد ، تمام URI ها با همان طرح و اقتدار بدون در نظر گرفتن مسیرهای خود ، فیلتر را تصویب می کنند.
  • اگر یک فیلتر یک طرح ، یک اقتدار و یک مسیر را مشخص کند ، فقط URIS با همان طرح ، اقتدار و مسیر از فیلتر عبور می کند.

توجه: مشخصات مسیر می تواند حاوی یک ستاره کارت وحشی (*) باشد تا فقط به یک مسابقه جزئی از نام مسیر نیاز داشته باشد.

آزمون داده ها هم URI و هم نوع MIME را در هدف با نوع URI و MIME مشخص شده در فیلتر مقایسه می کند. قوانین به شرح زیر است:

  1. قصد که شامل URI باشد و نه از نوع MIME فقط در صورتی که فیلتر هیچ نوع URIS یا MIME را مشخص نکند ، آزمایش را پشت سر می گذارد.
  2. هدفی که حاوی URI باشد اما هیچ نوع میمونی (نه صریح و نه از URI) فقط در صورتی که URI آن با فرمت URI فیلتر مطابقت داشته باشد ، آزمایش را پشت سر می گذارد و فیلتر نیز نوع MIME را مشخص نمی کند.
  3. هدفی که حاوی یک نوع میمون باشد اما URI فقط در صورتی که فیلتر همان نوع MIME را لیست کرده و فرمت URI را مشخص نمی کند ، آزمایش را پشت سر می گذارد.
  4. این هدف که شامل یک URI و یک نوع MIME (صریح یا قابل استنباط از URI) باشد ، قسمت نوع MIME از آزمون را تنها درصورتی که این نوع با یک نوع ذکر شده در فیلتر مطابقت داشته باشد ، عبور می کند. اگر URI آن با URI در فیلتر مطابقت داشته باشد یا در صورت داشتن content: یا file: URI و فیلتر URI را مشخص نمی کند ، قسمت URI از آزمون را عبور می دهد. به عبارت دیگر ، یک مؤلفه برای پشتیبانی content: و file: DATA اگر فیلتر آن فقط یک نوع 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>

تطبیق با هدف

اهداف نه تنها برای کشف یک مؤلفه هدف برای فعال کردن ، بلکه برای کشف چیزی در مورد مجموعه اجزای موجود در دستگاه هماهنگ می شوند. به عنوان مثال ، برنامه Home با یافتن کلیه فعالیتها با فیلترهای قصد که عملکرد ACTION_MAIN Action and CATEGORY_LAUNCHER را مشخص می کند ، پرتاب برنامه را جمع می کند. یک مسابقه فقط در صورت موفقیت آمیز است که اقدامات و دسته بندی ها در مسابقه با فیلتر مطابقت داشته باشند ، همانطور که در مستندات برای کلاس IntentFilter توضیح داده شده است.

برنامه شما می تواند از تطبیق قصد به روشی مشابه آنچه که برنامه خانگی انجام می دهد استفاده کند. PackageManager مجموعه ای از query...() را دارد که تمام مؤلفه هایی را که می توانند یک هدف خاص را بپذیرند و یک سری مشابه از resolve...() باز می گردند که بهترین مؤلفه برای پاسخ به یک هدف را تعیین می کند. به عنوان مثال ، queryIntentActivities() لیستی از کلیه فعالیتهایی را که می توانند قصد تصویب شده را به عنوان یک استدلال انجام دهند ، باز می گرداند ، و queryIntentServices() لیست مشابهی از خدمات را برمی گرداند. هیچ یک از اجزای سازنده را فعال نمی کنند. آنها فقط مواردی را که می توانند پاسخ دهند ، لیست می کنند. یک روش مشابه ، queryBroadcastReceivers() برای گیرنده های پخش وجود دارد.