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

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() برای گیرنده های پخش وجود دارد.

،

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

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

    یک Activity یک صفحه را در یک برنامه نشان می دهد. شما می توانید با عبور از Intent startActivity() نمونه جدیدی از Activity را شروع کنید. 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 bindService() از مؤلفه دیگر به سرویس متصل شوید. برای اطلاعات بیشتر ، به راهنمای خدمات مراجعه کنید.

  • ارائه پخش

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

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

انواع هدف

دو نوع هدف وجود دارد:

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

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

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

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

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

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

قصد

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

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

نام مؤلفه
نام مؤلفه برای شروع.

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

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

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

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

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

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

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

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

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

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

کاتلین

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

جاوا

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

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

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

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

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

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

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

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

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

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

به عنوان مثال ، هنگام ایجاد قصد ارسال ایمیل با 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 داشته باشد اما به کلاس بسته بندی شده یا سریالی دسترسی نداشته باشد ، این سیستم یک RuntimeException ایجاد می کند.

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

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

مثال هدف صریح

هدف صریح موردی است که شما برای راه اندازی یک مؤلفه برنامه خاص مانند یک فعالیت یا سرویس خاص در برنامه خود استفاده می کنید. برای ایجاد یک هدف صریح ، نام مؤلفه را برای شیء 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() خوانده می شود ، سیستم تمام برنامه های نصب شده را بررسی می کند تا مشخص شود کدام یک از این نوع قصد را کنترل می کنند (قصد با عمل ACTION_SEND و داده های "متن/ساده" را دارد). اگر فقط یک برنامه وجود داشته باشد که بتواند آن را اداره کند ، آن برنامه بلافاصله باز می شود و هدف داده می شود. اگر هیچ برنامه دیگری نتواند آن را اداره کند ، برنامه شما می تواند 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);
}

راه اندازی قصد ناامن

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

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

  1. برنامه شما قصد توخالی را از موارد اضافی یک هدف تحویل داده شده است.
  2. برنامه شما بلافاصله یک مؤلفه برنامه را با استفاده از آن قصد تو در تو ، مانند انتقال قصد به startActivity() ، startService() یا bindService() شروع می کند.

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

برای پرتاب قصد ناامن بررسی کنید

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

کاتلین

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

از اهداف با مسئولیت پذیری بیشتری استفاده کنید

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

فقط موارد اضافی اساسی را در اهداف کپی کرده و هرگونه فاضلاب و اعتبار سنجی لازم را انجام دهید. برنامه شما ممکن است موارد اضافی را از یک هدف به قصد دیگری که برای راه اندازی یک مؤلفه جدید استفاده می شود ، کپی کند. این اتفاق زمانی رخ می دهد که برنامه شما با putExtras(Intent) یا putExtras(Bundle) تماس می گیرد. اگر برنامه شما یکی از این عملیات ها را انجام می دهد ، فقط موارد اضافی را که از مؤلفه دریافت کننده انتظار دارد کپی کنید. اگر هدف دیگر (که نسخه آن را دریافت می کند) مؤلفه ای را راه اندازی می کند که قبل از کپی کردن آنها به قصد راه اندازی این مؤلفه ، موارد اضافی را صادر می کند ، ضد عفونی و اعتبار می دهد.

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

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

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

  1. برنامه شما هدفی را ایجاد می کند که در برنامه دیگری فعالیت می کند. در این هدف ، شما یک شیء 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>
عمل قصد پذیرفته شده را در ویژگی 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 نشان می دهد که این اصلی ترین نقطه ورود است و انتظار هیچ داده ای را ندارد.
  • رده CATEGORY_LAUNCHER نشان می دهد که نماد این فعالیت باید در راه اندازی برنامه سیستم قرار گیرد. اگر عنصر <activity> یک نماد را با icon مشخص نکند ، سیستم از عنصر <application> از نماد استفاده می کند.

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

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

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

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

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

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

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

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

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

با استفاده از یک هدف در انتظار

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

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

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

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

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

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

For more information about using pending intents, see the documentation for each of the respective use cases, such as in the Notifications and App Widgets API guides.

Specify mutability

If your app targets Android 12 or higher, you must specify the mutability of each PendingIntent object that your app creates. To declare that a given PendingIntent object is mutable or immutable, use the PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_IMMUTABLE flag, respectively.

If your app attempts to create a PendingIntent object without setting either mutability flag, the system throws an IllegalArgumentException , and the following message appears in 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.

Create immutable pending intents whenever possible

In most cases, your app should create immutable PendingIntent objects, as shown in the following code snippet. If a PendingIntent object is immutable, then other apps cannot modify the intent to adjust the result of invoking the 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);

However, certain use cases require mutable PendingIntent objects instead:

  • Supporting direct reply actions in notifications . The direct reply requires a change to the clip data in the PendingIntent object that's associated with the reply. Usually, you request this change by passing FILL_IN_CLIP_DATA as a flag to the fillIn() method.
  • Associating notifications with the Android Auto framework, using instances of CarAppExtender .
  • Placing conversations in bubbles using instances of PendingIntent . A mutable PendingIntent object allows the system to apply the correct flags, such as FLAG_ACTIVITY_MULTIPLE_TASK and FLAG_ACTIVITY_NEW_DOCUMENT .
  • Requesting device location information by calling requestLocationUpdates() or similar APIs. The mutable PendingIntent object allows the system to add intent extras that represent location lifecycle events. These events include a change in location and a provider becoming available.
  • Scheduling alarms using AlarmManager . The mutable PendingIntent object allows the system to add the EXTRA_ALARM_COUNT intent extra. This extra represents the number of times that a repeating alarm has been triggered. By containing this extra, the intent can accurately notify an app as to whether a repeating alarm was triggered multiple times, such as when the device was asleep.

If your app creates a mutable PendingIntent object, it's strongly recommended that you use an explicit intent and fill in the ComponentName . That way, whenever another app invokes the PendingIntent and passes control back to your app, the same component in your app always starts.

Use explicit intents within pending intents

To better define how other apps can use your app's pending intents, always wrap a pending intent around an explicit intent . To help follow this best practice, do the following:

  1. Check that the action, package, and component fields of the base intent are set.
  2. Use FLAG_IMMUTABLE , added in Android 6.0 (API level 23), to create pending intents. This flag prevents apps that receive a PendingIntent from filling in unpopulated properties. If your app's minSdkVersion is 22 or lower, you can provide safety and compatibility together using the following code:

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

Intent resolution

When the system receives an implicit intent to start an activity, it searches for the best activity for the intent by comparing it to intent filters based on three aspects:

  • اقدام.
  • Data (both URI and data type).
  • دسته بندی.

The following sections describe how intents are matched to the appropriate components according to the intent filter declaration in an app's manifest file.

Action test

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

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

To pass this filter, the action specified in the Intent must match one of the actions listed in the filter.

If the filter does not list any actions, there is nothing for an intent to match, so all intents fail the test. However, if an Intent does not specify an action, it passes the test as long as the filter contains at least one action.

Category test

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

<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. به عنوان مثال:

  • 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. قوانین به شرح زیر است:

  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.