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вbindService(). Дополнительную информацию можно найти в руководстве по сервисам. - Как отправить оповещение
Трансляция – это сообщение, которое может получить любое приложение. Система передает различные широковещательные сообщения о системных событиях, например о загрузке системы или начале зарядки устройства. Вы можете передать трансляцию в другие приложения, передав
IntentвsendBroadcast()илиsendOrderedBroadcast().
В оставшейся части этой страницы объясняется, как работают намерения и как их использовать. Дополнительную информацию можно найти в статьях Взаимодействие с другими приложениями и Обмен контентом.
Типы намерений
Существует два типа намерений:
- Явные объекты intent указывают, какой компонент какого приложения будет обрабатывать объект intent, с помощью полного имени
ComponentName. Обычно явные намерения используются для запуска компонентов в собственном приложении, поскольку вы знаете название класса действия или сервиса, который хотите запустить. Например, вы можете запустить новое действие в приложении в ответ на действие пользователя или запустить сервис для скачивания файла в фоновом режиме. - Неявные объекты intent не указывают на конкретный компонент, а объявляют общее действие, которое нужно выполнить. Это позволяет компоненту из другого приложения обработать его. Например, если вы хотите показать пользователю местоположение на карте, вы можете использовать неявное намерение, чтобы запросить у другого приложения, способного показать указанное местоположение на карте.
На рисунке 1 показано, как намерение используется при запуске действия. Если объект Intent явно указывает на определенный компонент действия, система сразу же запускает его.
Рисунок 1. Как неявный интент передается через систему для запуска другой операции: [1] Activity A создает объект Intent с описанием действия и передает его в startActivity(). [2] Система Android ищет во всех приложениях фильтр интентов, соответствующий интенту. Когда совпадение найдено, [3] система запускает соответствующий объект activity (Activity B), вызывая его метод onCreate() и передавая ему объект Intent.
При использовании неявного интента система Android находит подходящий компонент для запуска, сравнивая содержимое интента с фильтрами интентов, объявленными в файле манифеста других приложений на устройстве. Если интент соответствует фильтру интентов, система запускает этот компонент и передает ему объект Intent. Если совместимых фильтров интентов несколько, система показывает диалоговое окно, в котором пользователь может выбрать приложение.
Фильтр интентов – это выражение в файле манифеста приложения, которое указывает, какие типы интентов должен получать компонент. Например, если вы объявите фильтр интентов для объекта activity, другие приложения смогут напрямую вызывать этот объект с помощью определенного интента. Аналогично, если вы не объявляете фильтры интентов для операции, ее можно запустить только с помощью явного интента.
Внимание! Чтобы обеспечить безопасность приложения, всегда используйте явный интент при запуске Service и не объявляйте фильтры интентов для своих сервисов. Использование неявного намерения для запуска сервиса представляет угрозу безопасности, поскольку вы не можете быть уверены, какой сервис ответит на намерение, а пользователь не может видеть, какой сервис запускается. Начиная с Android 5.0 (уровень API 21), система выдает исключение, если вы вызываете bindService() с неявным намерением.
Создание намерения
Объект Intent содержит информацию, которую система Android использует для определения того, какой компонент следует запустить (например, точное название компонента или категорию компонента, который должен получить намерение), а также информацию, которую компонент-получатель использует для правильного выполнения действия (например, действие, которое необходимо выполнить, и данные, с которыми необходимо выполнить действие).
В файле Intent содержится следующая основная информация:
- Название компонента
- Название компонента, который нужно запустить.
Это необязательный, но важный элемент, который делает намерение явным. Это означает, что намерение должно быть доставлено только компоненту приложения, указанному в названии. Если название компонента не указано, намерение является неявным и система решает, какой компонент должен получить намерение, на основе другой информации о намерении (например, действия, данных и категории, описанных ниже). Если вам нужно запустить определенный компонент в приложении, укажите его название.
Примечание. При запуске
Serviceвсегда указывайте название компонента. В противном случае вы не можете быть уверены, какой сервис ответит на намерение, а пользователь не сможет увидеть, какой сервис запускается.Это поле
Intentпредставляет собой объектComponentName, который можно указать с помощью полного имени класса целевого компонента, включая название пакета приложения, напримерcom.example.ExampleActivity. Название компонента можно задать с помощьюsetComponent(),setClass(),setClassName()или конструктораIntent. - Действие
- Строка, которая указывает общее действие, которое нужно выполнить (например, view или pick).
В случае с намерением трансляции это действие, которое произошло и о котором сообщается. Действие в значительной степени определяет структуру остальной части намерения, в частности информацию, содержащуюся в данных и дополнительных сведениях.
Вы можете указать собственные действия для использования намерениями в вашем приложении (или для использования другими приложениями для вызова компонентов в вашем приложении), но обычно вы указываете константы действий, определенные классом
Intentили другими классами фреймворка. Вот несколько распространенных действий для начала активности:ACTION_VIEW- Используйте это действие в объекте Intent с
startActivity(), когда у вас есть информация, которую объект Activity может показать пользователю, например фотография для просмотра в галерее или адрес для просмотра на карте. ACTION_SEND- Также известное как намерение поделиться, его следует использовать в интенте с
startActivity(), если у вас есть данные, которыми пользователь может поделиться через другое приложение, например почтовое приложение или приложение для публикации в соцсетях.
Дополнительные константы, определяющие общие действия, можно найти в справочнике по классу
Intent. Другие действия определены в других частях фреймворка Android, например вSettingsдля действий, которые открывают определенные экраны в приложении "Настройки".Действие для намерения можно указать с помощью
setAction()или конструктораIntent.Если вы определяете собственные действия, обязательно добавьте название пакета приложения в качестве префикса, как показано в следующем примере:
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- Вид ответа
- URI (объект
Uri), который ссылается на данные, с которыми нужно выполнить действие, и/или MIME-тип этих данных. Тип предоставляемых данных обычно определяется действием намерения. Например, если действие –ACTION_EDIT, данные должны содержать URI документа, который нужно отредактировать.При создании намерения часто важно указать тип данных (его MIME-тип) в дополнение к его URI. Например, действие, которое может показывать изображения, скорее всего, не сможет воспроизвести аудиофайл, даже если форматы URI похожи. Указание MIME-типа данных помогает системе Android найти наиболее подходящий компонент для получения намерения. Однако иногда MIME-тип можно определить по URI, особенно если данные представляют собой URI
content:. URIcontent:указывает, что данные находятся на устройстве и управляютсяContentProvider, который делает MIME-тип данных видимым для системы.Чтобы задать только 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вы можете указать получателя (to) с помощью ключаEXTRA_EMAIL, а тему (subject) – с помощью ключаEXTRA_SUBJECT.Класс
Intentопределяет множество константEXTRA_*для стандартизированных типов данных. Если вам нужно объявить собственные дополнительные ключи (для намерений, которые получает ваше приложение), обязательно добавьте название пакета приложения в качестве префикса, как показано в следующем примере:Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
Внимание! Не используйте данные
ParcelableилиSerializableпри отправке намерения, которое должно получить другое приложение. Если приложение попытается получить доступ к данным в объектеBundle, но у него не будет доступа к сериализованному классу или классу, реализующему интерфейс Parcelable, система выдаст ошибкуRuntimeException. - Флаги
- Флаги определяются в классе
Intentи служат метаданными для намерения. Флаги могут указывать системе Android, как запускать операцию (например, к какой задаче она должна относиться) и как обрабатывать ее после запуска (например, должна ли она входить в список недавних операций).Дополнительную информацию можно найти в описании метода
setFlags().
Пример явного интента
Явное намерение используется для запуска определенного компонента приложения, например действия или сервиса. Чтобы создать явное намерение, укажите название компонента для объекта Intent. Все остальные свойства намерения необязательны.
Например, если вы создали в приложении сервис DownloadService, предназначенный для скачивания файла из интернета, вы можете запустить его с помощью следующего кода:
Kotlin
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" val downloadIntent = Intent(this, DownloadService::class.java).apply { data =Uri.parse(fileUrl) } startService(downloadIntent)
Java
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" Intent downloadIntent = new Intent(this, DownloadService.class); downloadIntent.setData(Uri.parse(fileUrl)); startService(downloadIntent);
Конструктор Intent(Context, Class) предоставляет приложению объект Context, а компоненту – объект Class. Таким образом, это намерение явно запускает класс DownloadService в приложении.
Подробную информацию о создании и запуске сервиса можно найти в руководстве по сервисам.
Пример неявного интента
Неявное намерение указывает действие, которое может вызвать любое приложение на устройстве, способное выполнить это действие. Неявные намерения полезны, когда ваше приложение не может выполнить действие, но другие приложения, вероятно, могут, и вы хотите, чтобы пользователь выбрал, какое приложение использовать.
Например, если вы хотите, чтобы пользователь мог поделиться контентом с другими людьми, создайте намерение с действием ACTION_SEND и добавьте дополнительные данные, указывающие на контент, которым нужно поделиться. Когда вы вызываете startActivity() с этим намерением, пользователь может выбрать приложение, через которое он хочет поделиться контентом.
Kotlin
// Create the text message with a string. val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, textMessage) type = "text/plain" } // Try to invoke the intent. try { startActivity(sendIntent) } catch (e: ActivityNotFoundException) { // Define what your app should do if no activity can handle the intent. }
Java
// Create the text message with a string. Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType("text/plain"); // Try to invoke the intent. try { startActivity(sendIntent); } catch (ActivityNotFoundException e) { // Define what your app should do if no activity can handle the intent. }
При вызове startActivity() система проверяет все установленные приложения, чтобы определить, какие из них могут обрабатывать этот тип намерения (намерение с действием ACTION_SEND, которое содержит данные text/plain). Если такое приложение только одно, оно сразу же открывается и получает намерение. Если ни одно приложение не может обработать намерение, ваше приложение может перехватить возникающее исключение ActivityNotFoundException. Если намерение принимается несколькими действиями, система показывает диалоговое окно, как на рисунке 2, чтобы пользователь мог выбрать приложение.
Дополнительную информацию о запуске других приложений можно найти в руководстве Перенаправление пользователя в другое приложение.
Рисунок 2. Диалоговое окно выбора.
Принудительный выбор приложения
Если на неявное намерение отвечает несколько приложений, пользователь может выбрать, какое из них использовать по умолчанию. Возможность выбрать приложение по умолчанию полезна, когда пользователь выполняет действие, для которого он, скорее всего, хочет каждый раз использовать одно и то же приложение, например при открытии веб-страницы (пользователи часто предпочитают только один веб-браузер).
Однако если на намерение могут ответить несколько приложений и пользователь может захотеть использовать каждый раз другое приложение, вы должны явно показать диалоговое окно выбора. В диалоговом окне выбора пользователю предлагается выбрать приложение, которое будет использоваться для действия (пользователь не может выбрать приложение по умолчанию для действия). Например, когда ваше приложение выполняет действие "поделиться" с помощью ACTION_SEND, пользователи могут захотеть поделиться с помощью другого приложения в зависимости от текущей ситуации, поэтому вы всегда должны использовать диалоговое окно выбора, как показано на рисунке 2.
Чтобы показать окно выбора, создайте объект Intent с помощью createChooser() и передайте его в startActivity(), как показано в примере ниже.
В этом примере показано диалоговое окно со списком приложений, которые отвечают на намерение, переданное методу createChooser(), и используют предоставленный текст в качестве заголовка диалогового окна.
Kotlin
val sendIntent = Intent(Intent.ACTION_SEND) ... // Always use string resources for UI text. // This says something like "Share this photo with" val title: String = resources.getString(R.string.chooser_title) // Create intent to show the chooser dialog val chooser: Intent = Intent.createChooser(sendIntent, title) // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(packageManager) != null) { startActivity(chooser) }
Java
Intent sendIntent = new Intent(Intent.ACTION_SEND); ... // Always use string resources for UI text. // This says something like "Share this photo with" String title = getResources().getString(R.string.chooser_title); // Create intent to show the chooser dialog Intent chooser = Intent.createChooser(sendIntent, title); // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
Обнаружение небезопасных запусков намерений
Приложение может запускать объекты Intent для навигации между компонентами внутри приложения или для выполнения действия от имени другого приложения. Чтобы повысить безопасность платформы, в Android 12 (уровень API 31) и более поздних версиях предусмотрена функция отладки, которая предупреждает о небезопасном запуске объекта Intent. Например, приложение может небезопасно запускать вложенное намерение, которое передается как дополнительное в другом намерении.
Если ваше приложение выполняет оба действия, система обнаруживает небезопасный запуск намерения и происходит нарушение StrictMode:
- Приложение распаковывает вложенное намерение из дополнительных данных доставленного намерения.
- Приложение сразу запускает компонент, используя вложенный объект Intent, например передает его в
startActivity(),startService()илиbindService().
Подробную информацию о том, как выявить эту проблему и внести изменения в приложение, можно найти в записи блога о вложенных объектах Intent в Android на сайте Medium.
Проверка на наличие небезопасных запусков намерений
Чтобы проверить, есть ли в приложении небезопасные запуски намерений, вызовите detectUnsafeIntentLaunch() при настройке VmPolicy, как показано в следующем фрагменте кода. Если приложение обнаружит нарушение StrictMode, вы можете остановить его работу, чтобы защитить потенциально конфиденциальную информацию.
Kotlin
fun onCreate() { StrictMode.setVmPolicy(VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()) }
Java
protected void onCreate() { StrictMode.setVmPolicy(new VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()); }
Используйте намерения более ответственно
Чтобы снизить вероятность небезопасного запуска намерения и нарушения StrictMode, следуйте приведенным ниже рекомендациям.
Копируйте только необходимые дополнительные данные в намерениях и выполняйте необходимую очистку и проверку. Приложение может копировать дополнительные данные из одного намерения в другое, которое используется для запуска нового компонента. Это происходит, когда приложение вызывает putExtras(Intent) или putExtras(Bundle).
Если ваше приложение выполняет одну из этих операций, копируйте только те дополнительные данные, которые ожидает получить компонент-получатель. Если другое намерение (которое получает копию) запускает компонент, который не экспортируется, очистите и проверьте дополнительные данные, прежде чем копировать их в намерение, запускающее компонент.
Не экспортируйте компоненты приложения без необходимости. Например, если вы хотите запустить компонент приложения с помощью внутреннего вложенного объекта intent, задайте для атрибута android:exported этого компонента значение false.
Используйте PendingIntent вместо вложенного намерения. В таком случае, когда другое приложение распакует объект PendingIntent из содержащего его объекта Intent, оно сможет запустить объект PendingIntent, используя идентификационные данные вашего приложения. Эта конфигурация позволяет другому приложению безопасно запускать любой компонент в вашем приложении, в том числе неэкспортированный.
На рис. 2 показано, как система передает управление от вашего (клиентского) приложения другому (сервисному) приложению и обратно.
- Ваше приложение создает намерение, которое вызывает действие в другом приложении. В это намерение вы добавляете объект
PendingIntentв качестве дополнительного. Этот отложенный интент вызывает компонент в вашем приложении, который не экспортируется. - Получив намерение вашего приложения, другое приложение извлекает вложенный объект
PendingIntent. - Другое приложение вызывает метод
send()для объектаPendingIntent. - После возврата управления вашему приложению система вызывает отложенный интент, используя контекст приложения.
Рисунок 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>- Объявляет принятую категорию намерения в атрибуте
name. Значение должно быть строковым значением действия, а не константой класса.Примечание. Чтобы получать неявные интенты, необходимо включить категорию
CATEGORY_DEFAULTв фильтр интентов. МетодыstartActivity()иstartActivityForResult()обрабатывают все намерения так, как если бы они объявляли категориюCATEGORY_DEFAULT. Если вы не объявите эту категорию в фильтре интентов, неявные интенты не будут разрешаться в вашу Activity.
Например, ниже приведено объявление действия с фильтром интентов, который позволяет получать интент 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 другого приложения, всегда используйте явное намерение для запуска собственного сервиса.
Примечание.
Для всех Activity необходимо объявить фильтры интентов в файле манифеста.
Однако фильтры для широковещательных приемников можно регистрировать динамически, вызывая 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 – это специальный тип данных, который указывает на панорамные фотографии. Их можно обрабатывать с помощью Google Panorama API.
Сопоставление интентов с фильтрами интентов других приложений
Если другое приложение предназначено для Android 13 (уровень API 33) или более поздней версии, оно может обработать намерение вашего приложения, только если оно соответствует действиям и категориям элемента <intent-filter> в другом приложении. Если система не находит совпадения, она выдает ошибку ActivityNotFoundException.
Приложение-отправитель должно обработать это исключение.
Аналогично, если вы обновите приложение для Android 13 или более поздней версии, все интенты из внешних приложений будут доставляться экспортированному компоненту вашего приложения, только если они соответствуют действиям и категориям элемента <intent-filter>, объявленного в вашем приложении. Это происходит независимо от целевой версии SDK отправляющего приложения.
Сопоставление намерений не применяется в следующих случаях:
- Интенты, доставленные компонентам, которые не объявляют фильтры интентов.
- Намерения, исходящие из того же приложения.
- Намерения, исходящие от системы, то есть отправленные из системного UID (uid=1000). К системным приложениям относятся
system_serverи приложения, для которых задано значениеandroid.uid.systemвandroid:sharedUserId. - Намерения, исходящие от корневого элемента.
Подробнее о сопоставлении намерений…
Использование отложенного интента
Объект PendingIntent является оболочкой для объекта Intent. Основная цель PendingIntent – предоставить стороннему приложению разрешение на использование содержащегося в нем Intent так, как если бы он выполнялся из процесса вашего приложения.
Основные примеры использования отложенного интента:
- Объявление намерения, которое должно быть выполнено, когда пользователь выполняет действие с вашим уведомлением (
NotificationManagerсистемы Android выполняетIntent). - Объявление намерения, которое должно быть выполнено, когда пользователь выполняет действие с виджетом приложения (приложение главного экрана выполняет
Intent). - Объявление намерения, которое должно быть выполнено в определенное время в будущем (
AlarmManagerсистемы Android выполняетIntent).
Каждый объект Intent предназначен для обработки определенным типом компонента приложения (Activity, Service или BroadcastReceiver). То же самое относится и к PendingIntent. При использовании отложенного интента приложение не выполняет его с помощью вызова, например startActivity(). Вместо этого необходимо указать тип компонента при создании PendingIntent, вызвав соответствующий метод:
PendingIntent.getActivity()дляIntent, который запускаетActivity.PendingIntent.getService()дляIntent, который начинаетService.PendingIntent.getBroadcast()дляIntent, который начинаетBroadcastReceiver.
Если ваше приложение не получает отложенные интенты от других приложений, то описанные выше способы создания PendingIntent, скорее всего, будут единственными методами PendingIntent, которые вам понадобятся.
Каждый метод принимает текущий объект Context приложения, объект 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 неизменяемый, другие приложения не могут изменить намерение, чтобы скорректировать результат его вызова.
Kotlin
val pendingIntent = PendingIntent.getActivity(applicationContext, REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE);
Однако в некоторых случаях требуются изменяемые объекты PendingIntent:
- Поддержка быстрых ответов в уведомлениях. Для быстрого ответа требуется изменить данные буфера обмена в объекте PendingIntent, связанном с ответом. Обычно для этого в метод
fillIn()передается флагFILL_IN_CLIP_DATA. - Связывание уведомлений с фреймворком Android Auto с помощью экземпляров
CarAppExtender. - Размещение чатов во всплывающих окнах с помощью экземпляров
PendingIntent. Изменяемый объектPendingIntentпозволяет системе применять правильные флаги, такие какFLAG_ACTIVITY_MULTIPLE_TASKиFLAG_ACTIVITY_NEW_DOCUMENT. - Запрашивать данные о местоположении устройства, вызывая
requestLocationUpdates()или аналогичные API. Изменяемый объектPendingIntentпозволяет системе добавлять дополнительные намерения, которые представляют события жизненного цикла местоположения. К таким событиям относятся изменение местоположения и появление доступного поставщика. - Планирование будильников с помощью
AlarmManager. Изменяемый объектPendingIntentпозволяет системе добавлять дополнительный параметр намеренияEXTRA_ALARM_COUNT. Это дополнительное значение представляет собой количество срабатываний повторяющегося будильника. Благодаря этому дополнительному параметру намерение может точно уведомить приложение о том, что повторяющийся будильник сработал несколько раз, например когда устройство находилось в спящем режиме.
Если ваше приложение создает изменяемый объект PendingIntent, настоятельно рекомендуем использовать явное намерение и заполнить ComponentName. Таким образом, когда другое приложение вызывает PendingIntent и передает управление обратно вашему приложению, всегда запускается один и тот же компонент.
Используйте явные интенты в отложенных интентах
Чтобы точнее определить, как другие приложения могут использовать отложенные интенты вашего приложения, всегда оборачивайте отложенный интент в явный. Чтобы следовать этой рекомендации, сделайте следующее:
- Убедитесь, что заданы все поля действия, пакета и компонента базового намерения.
-
Для создания отложенных интентов используйте
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 и тип данных).
- Category (Категория).
В разделах ниже описано, как интенты сопоставляются с подходящими компонентами в соответствии с объявлением фильтра интентов в файле манифеста приложения.
Тестирование действий
Чтобы указать допустимые действия интента, фильтр интентов может объявить ноль или более элементов <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 все равно будет передаваться. Поэтому намерение без категорий всегда проходит эту проверку, независимо от того, какие категории объявлены в фильтре.
Примечание. 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 с такими же схемой и источником независимо от пути.
- Если в фильтре указаны схема, источник и путь, то он будет пропускать только URI с такими же схемой, источником и путем.
Примечание. В спецификации пути можно использовать подстановочный знак – звездочку (*), чтобы требовалось только частичное совпадение названия пути.
Проверка данных сравнивает URI и MIME-тип в намерении с URI и MIME-типом, указанными в фильтре. Правила следующие:
- Намерение, не содержащее ни URI, ни MIME-тип, проходит проверку, только если в фильтре не указаны URI или MIME-типы.
- Намерение, которое содержит URI, но не содержит MIME-тип (ни явный, ни выводимый из URI), проходит проверку, только если его URI соответствует формату URI фильтра и фильтр также не указывает MIME-тип.
- Намерение, содержащее MIME-тип, но не URI, проходит проверку, только если в фильтре указан тот же MIME-тип и не задан формат URI.
- Намерение, содержащее и URI, и MIME-тип (явный или выводимый из URI), проходит проверку MIME-типа, только если этот тип соответствует типу, указанному в фильтре. Проверка URI считается пройденной, если URI намерения совпадает с URI в фильтре или если у намерения есть URI
content:илиfile:, а в фильтре URI не указан. Другими словами, предполагается, что компонент поддерживает данныеcontent:иfile:, если его фильтр содержит только MIME-тип.
Примечание. Если в намерении указан URI или MIME-тип, проверка данных не будет пройдена, если в элементе <intent-filter> нет элементов <data>.
Последнее правило (d) отражает ожидание,
что компоненты могут получать локальные данные из файла или поставщика контента.
Поэтому в фильтрах можно указать только тип данных, не называя схемы 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 заполняет панель запуска приложений, находя все Activity с фильтрами интентов, которые указывают действие ACTION_MAIN и категорию CATEGORY_LAUNCHER.
Совпадение будет успешным, только если действия и категории в намерении соответствуют фильтру, как описано в документации для класса IntentFilter.
Ваше приложение может использовать сопоставление намерений так же, как это делает приложение Google Home.
В классе PackageManager есть набор методов query...(), которые возвращают все компоненты, способные принять определенное намерение, и аналогичный набор методов resolve...(), которые определяют лучший компонент для ответа на намерение. Например, queryIntentActivities() возвращает список всех действий, которые могут выполнить намерение, переданное в качестве аргумента, а queryIntentServices() возвращает аналогичный список сервисов.
Ни один из методов не активирует компоненты, а только перечисляет те, которые могут ответить. Для широковещательных приемников есть похожий метод – queryBroadcastReceivers().