Намерения и фильтры намерений

Intent — это объект обмена сообщениями, который можно использовать для запроса действия у другого компонента приложения . Хотя интенты облегчают взаимодействие между компонентами несколькими способами, существуют три основных варианта их использования:

  • Начало деятельности

    Activity представляет собой отдельный экран в приложении. Вы можете запустить новый экземпляр Activity , передав Intent в startActivity() . Intent описывает Activity, которую нужно запустить, и содержит все необходимые данные.

    Если вы хотите получить результат от активности после её завершения, вызовите метод 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() .

Остальная часть этой страницы объясняет, как работают интенты и как их использовать. Дополнительную информацию см. в разделах «Взаимодействие с другими приложениями» и «Обмен контентом» .

Типы намерений

Существует два типа намерений:

  • Explicit intents specify which component of which application will satisfy the intent, by specifying a full ComponentName . You'll typically use an explicit intent to start a component in your own app, because you know the class name of the activity or service you want to start. For example, you might start a new activity within your app in response to a user action, or start a service to download a file in the background.
  • Неявные намерения не указывают конкретный компонент, а вместо этого объявляют общее действие, которое необходимо выполнить, что позволяет компоненту из другого приложения его обработать. Например, если вы хотите показать пользователю местоположение на карте, вы можете использовать неявное намерение, чтобы запросить у другого приложения, поддерживающего эту функцию, отображение указанного местоположения на карте.

На рисунке 1 показано, как используется Intent при запуске активности. Когда объект Intent явно указывает на конкретный компонент активности, система немедленно запускает этот компонент.

Figure 1. How an implicit intent is delivered through the system to start another activity: [1] Activity A creates an Intent with an action description and passes it to startActivity() . [2] The Android System searches all apps for an intent filter that matches the intent. When a match is found, [3] the system starts the matching activity ( Activity B ) by invoking its onCreate() method and passing it the Intent .

When you use an implicit intent, the Android system finds the appropriate component to start by comparing the contents of the intent to the intent filters declared in the manifest file of other apps on the device. If the intent matches an intent filter, the system starts that component and delivers it the Intent object. If multiple intent filters are compatible, the system displays a dialog so the user can pick which app to use.

An intent filter is an expression in an app's manifest file that specifies the type of intents that the component would like to receive. For instance, by declaring an intent filter for an activity, you make it possible for other apps to directly start your activity with a certain kind of intent. Likewise, if you do not declare any intent filters for an activity, then it can be started only with an explicit intent.

Caution: To ensure that your app is secure, always use an explicit intent when starting a Service and do not declare intent filters for your services. Using an implicit intent to start a service is a security hazard because you can't be certain what service will respond to the intent, and the user can't see which service starts. Beginning with Android 5.0 (API level 21), the system throws an exception if you call bindService() with an implicit intent.

Формирование намерения

Объект Intent содержит информацию, которую система Android использует для определения того, какой компонент следует запустить (например, точное имя компонента или категория компонента, который должен получить Intent), а также информацию, которую компонент-получатель использует для правильного выполнения действия (например, действие, которое необходимо выполнить, и данные, с которыми необходимо работать).

Основная информация, содержащаяся в Intent включает в себя следующее:

Название компонента
Название компонента, с которого нужно начать.

This is optional, but it's the critical piece of information that makes an intent explicit , meaning that the intent should be delivered only to the app component defined by the component name. Without a component name, the intent is implicit and the system decides which component should receive the intent based on the other intent information (such as the action, data, and category—described below). If you need to start a specific component in your app, you should specify the component name.

Примечание: При запуске Service всегда указывайте имя компонента . В противном случае вы не можете быть уверены, какая служба отреагирует на намерение, и пользователь не сможет увидеть, какая служба запустилась.

Это поле объекта Intent представляет собой объект ComponentName , который можно указать, используя полное имя класса целевого компонента, включая имя пакета приложения, например, com.example.ExampleActivity . Имя компонента можно задать с помощью setComponent() , setClass() , setClassName() или конструктора Intent .

Действие
Строка, указывающая на общее действие, которое необходимо выполнить (например, просмотр или выбор ).

В случае трансляции информации, речь идёт о действии, которое произошло и о котором сообщается. Это действие во многом определяет структуру остальной части сообщения, в частности, информацию, содержащуюся в данных и дополнительных материалах.

Вы можете указывать собственные действия для использования интентами внутри вашего приложения (или для использования другими приложениями для вызова компонентов в вашем приложении), но обычно вы указываете константы действий, определенные классом Intent или другими классами фреймворка. Вот несколько распространенных действий для запуска активности:

ACTION_VIEW
Используйте это действие в Intent с startActivity() когда у вас есть информация, которую Activity может отобразить пользователю, например, фотография для просмотра в галерее или адрес для просмотра в приложении с картами.
ACTION_SEND
Также известный как намерение поделиться , его следует использовать в намерении с startActivity() когда у вас есть данные, которыми пользователь может поделиться через другое приложение, например, почтовое приложение или приложение для обмена в социальных сетях.

Дополнительные константы, определяющие общие действия, см. в справочнике по классу Intent Другие действия определяются в других разделах платформы Android, например, в Settings , где описаны действия, открывающие определенные экраны в системном приложении «Настройки».

Вы можете указать действие для интента с помощью setAction() или с помощью конструктора Intent .

Если вы определяете собственные действия, обязательно укажите имя пакета вашего приложения в качестве префикса, как показано в следующем примере:

Котлин

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

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Данные
URI (объект Uri ), ссылающийся на данные, с которыми необходимо выполнить действие, и/или MIME-тип этих данных. Тип предоставляемых данных обычно определяется действием намерения. Например, если действие — ACTION_EDIT , данные должны содержать URI документа для редактирования.

When creating an intent, it's often important to specify the type of data (its MIME type) in addition to its URI. For example, an activity that's able to display images probably won't be able to play an audio file, even though the URI formats could be similar. Specifying the MIME type of your data helps the Android system find the best component to receive your intent. However, the MIME type can sometimes be inferred from the URI—particularly when the data is a content: URI. A content: URI indicates the data is located on the device and controlled by a ContentProvider , which makes the data MIME type visible to the system.

Чтобы задать только URI данных, вызовите setData() . Чтобы задать только MIME-тип, вызовите setType() . При необходимости вы можете явно задать оба параметра с помощью setDataAndType() .

Внимание: Если вы хотите установить одновременно URI и MIME-тип, не вызывайте setData() и setType() поскольку каждый из них обнуляет значение другого. Всегда используйте setDataAndType() для установки как URI, так и MIME-типа.

Категория
Строка, содержащая дополнительную информацию о типе компонента, который должен обрабатывать намерение. В намерение можно поместить любое количество описаний категорий, но большинству намерений категория не требуется. Вот некоторые распространенные категории:
CATEGORY_BROWSABLE
Целевая активность позволяет веб-браузеру запускать её для отображения данных, на которые ссылается ссылка, например, изображения или электронного письма.
CATEGORY_LAUNCHER
Данная операция является начальным этапом выполнения задачи и отображается в панели запуска приложений системы.

Полный список категорий см. в описании класса Intent .

Вы можете указать категорию с помощью addCategory() .

These properties listed above (component name, action, data, and category) represent the defining characteristics of an intent. By reading these properties, the Android system is able to resolve which app component it should start. However, an intent can carry additional information that does not affect how it is resolved to an app component. An intent can also supply the following information:

Дополнительные материалы
Пары «ключ-значение», содержащие дополнительную информацию, необходимую для выполнения запрошенного действия. Подобно тому, как некоторые действия используют определенные типы URI данных, другие действия также используют определенные дополнительные данные.

Дополнительные данные можно добавить с помощью различных методов putExtra() , каждый из которых принимает два параметра: имя ключа и значение. Также можно создать объект Bundle со всеми дополнительными данными, а затем вставить этот Bundle в Intent с помощью putExtras() .

Например, при создании намерения отправить электронное письмо с ACTION_SEND вы можете указать получателя с помощью ключа EXTRA_EMAIL , а тему письма — с помощью ключа EXTRA_SUBJECT .

Класс Intent определяет множество констант EXTRA_* для стандартизированных типов данных. Если вам необходимо объявить собственные дополнительные ключи (для интентов, которые получает ваше приложение), обязательно укажите имя пакета вашего приложения в качестве префикса, как показано в следующем примере:

Котлин

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

Внимание : не используйте данные Parcelable или Serializable при отправке Intent, который, как вы ожидаете, получит другое приложение. Если приложение попытается получить доступ к данным в объекте Bundle , но не имеет доступа к классу Parcelable или Serializable, система вызовет исключение RuntimeException .

Флаги
В классе Intent определены флаги, которые функционируют как метаданные для интента. Флаги могут указывать системе Android, как запустить активность (например, к какой задаче она должна принадлежать) и как обрабатывать её после запуска (например, следует ли её добавить в список последних активностей).

Для получения более подробной информации см. метод 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)

Java

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

Конструктор Intent(Context, Class) передает Context приложения, а компоненту — объект Class . Таким образом, этот Intent явно запускает класс DownloadService в приложении.

Для получения дополнительной информации о создании и запуске сервиса см. руководство по сервисам .

Пример неявного намерения

Неявное намерение определяет действие, которое может вызвать любое приложение на устройстве, способное выполнить это действие. Использование неявного намерения полезно, когда ваше приложение не может выполнить это действие, но другие приложения, вероятно, могут, и вы хотите, чтобы пользователь выбрал, какое приложение использовать.

Например, если у вас есть контент, которым вы хотите, чтобы пользователь поделился с другими людьми, создайте интент с действием 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.
}

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

When startActivity() is called, the system examines all of the installed apps to determine which ones can handle this kind of intent (an intent with the ACTION_SEND action and that carries "text/plain" data). If there's only one app that can handle it, that app opens immediately and is given the intent. If no other apps can handle it, your app can catch the ActivityNotFoundException that occurs. If multiple activities accept the intent, the system displays a dialog such as the one shown in Figure 2, so the user can pick which app to use.

Более подробная информация о запуске других приложений также представлена ​​в руководстве по перенаправлению пользователя в другое приложение .

Рисунок 2. Диалоговое окно выбора.

Принудительное использование средства выбора приложений

Если на ваше неявное намерение реагирует несколько приложений, пользователь может выбрать, какое из них использовать, и сделать его приложением по умолчанию для выполнения действия. Возможность выбора приложения по умолчанию полезна при выполнении действия, для которого пользователь, вероятно, захочет использовать одно и то же приложение каждый раз, например, при открытии веб-страницы (пользователи часто предпочитают использовать только один веб-браузер).

However, if multiple apps can respond to the intent and the user might want to use a different app each time, you should explicitly show a chooser dialog. The chooser dialog asks the user to select which app to use for the action (the user cannot select a default app for the action). For example, when your app performs "share" with the ACTION_SEND action, users may want to share using a different app depending on their current situation, so you should always use the chooser dialog, as shown in Figure 2.

Чтобы отобразить окно выбора, создайте Intent с помощью createChooser() и передайте его методу startActivity() , как показано в следующем примере. В этом примере отображается диалоговое окно со списком приложений, которые реагируют на Intent, переданный методу 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)
}

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

Выявление небезопасных запусков намерений

Your app might launch intents to navigate between components inside of your app, or to perform an action on behalf of another app. To improve platform security, Android 12 (API level 31) and higher provide a debugging feature that warns you if your app performs an unsafe launch of an intent. For example, your app might perform an unsafe launch of a nested intent , which is an intent that is passed as an extra in another intent.

Если ваше приложение выполняет оба следующих действия, система обнаруживает небезопасный запуск интента, и происходит нарушение StrictMode :

  1. Ваше приложение отделяет вложенный интент от дополнительных данных доставленного интента.
  2. Ваше приложение немедленно запускает компонент приложения , используя этот вложенный интент, например, передавая интент в startActivity() , startService() или bindService() .

Для получения более подробной информации о том, как выявить эту ситуацию и внести изменения в ваше приложение, прочитайте статью в блоге Android о вложенных интентах на Medium.

Проверьте наличие небезопасных запусков намерений.

Чтобы проверить наличие небезопасных запусков Intent в вашем приложении, вызовите detectUnsafeIntentLaunch() при настройке VmPolicy , как показано в следующем фрагменте кода. Если ваше приложение обнаружит нарушение StrictMode, вы можете остановить выполнение приложения, чтобы защитить потенциально конфиденциальную информацию.

Котлин

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, следуйте этим рекомендациям.

Copy only the essential extras within intents, and perform any necessary sanitation and validation. Your app might copy the extras from one intent to another intent that is used to launch a new component. This occurs when your app calls putExtras(Intent) or putExtras(Bundle) . If your app performs one of these operations, copy only the extras that the receiving component expects. If the other intent (that receives the copy) launches a component that isn't exported , sanitize and validate the extras before copying them to the intent that launches the component.

Не экспортируйте компоненты вашего приложения без необходимости. Например, если вы планируете запускать компонент приложения с помощью внутреннего вложенного интента, установите атрибут android:exported этого компонента в значение false .

Используйте PendingIntent вместо вложенного Intent. Таким образом, когда другое приложение распакует PendingIntent своего Intent , оно сможет запустить PendingIntent используя идентификатор вашего приложения. Такая конфигурация позволяет другому приложению безопасно запускать любой компонент, включая неэкспортируемый компонент, в вашем приложении.

На диаграмме на рисунке 2 показано, как система передает управление от вашего (клиентского) приложения к другому (сервисному) приложению и обратно к вашему приложению:

  1. Ваше приложение создает интент, который вызывает активность в другом приложении. Внутри этого интента вы добавляете объект PendingIntent в качестве дополнительного параметра. Этот ожидающий интент вызывает компонент в вашем приложении; этот компонент не экспортируется.
  2. Получив интент от вашего приложения, другое приложение извлекает вложенный объект PendingIntent .
  3. Другое приложение вызывает метод send() объекта PendingIntent .
  4. После передачи управления обратно вашему приложению, система вызывает ожидающий интент, используя контекст вашего приложения.

Рисунок 2. Схема взаимодействия между приложениями при использовании вложенного ожидающего намерения.

Получение неявного намерения

To advertise which implicit intents your app can receive, declare one or more intent filters for each of your app components with an <intent-filter> element in your manifest file . Each intent filter specifies the type of intents it accepts based on the intent's action, data, and category. The system delivers an implicit intent to your app component only if the intent can pass through one of your intent filters.

Примечание: Явно заданное намерение всегда доставляется целевому объекту, независимо от каких-либо фильтров намерений, объявленных компонентом.

Компонент приложения должен объявлять отдельные фильтры для каждой уникальной задачи, которую он может выполнять. Например, в приложении-галерее изображений может быть два фильтра: один для просмотра изображения, а другой для его редактирования. При запуске активность анализирует Intent и принимает решение о дальнейшем поведении на основе содержащейся в Intent информации (например, показывать ли элементы управления редактора или нет).

Каждый фильтр намерений определяется элементом <intent-filter> в файле манифеста приложения, вложенным в соответствующий компонент приложения (например, элемент <activity> ).

В каждом компоненте приложения, содержащем элемент <intent-filter> , явно задайте значение для android:exported . Этот атрибут указывает, доступен ли компонент приложения другим приложениям. В некоторых ситуациях, например, для действий, фильтры намерений которых включают категорию LAUNCHER , полезно установить этот атрибут в true . В противном случае безопаснее установить этот атрибут в false .

Внимание: если активность, служба или приемник широковещательных сообщений в вашем приложении используют фильтры намерений и явно не задают значение для android:exported , ваше приложение нельзя установить на устройство под управлением Android 12 или выше.

Внутри элемента <intent-filter> вы можете указать тип принимаемых интентов, используя один или несколько из следующих трех элементов:

<action>
В атрибуте name указывается, что действие Intent принимается. Значение должно быть строковым значением действия, а не константой класса.
<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> . В этом случае необходимо убедиться, что компонент может обрабатывать любые комбинации этих элементов фильтра.

Если вам нужно обрабатывать несколько типов намерений, но только в определенных комбинациях действий, данных и типов категорий, то вам потребуется создать несколько фильтров для намерений.

An implicit intent is tested against a filter by comparing the intent to each of the three elements. To be delivered to the component, the intent must pass all three tests. If it fails to match even one of them, the Android system won't deliver the intent to the component. However, because a component may have multiple intent filters, an intent that does not pass through one of a component's filters might make it through on another filter. More information about how the system resolves intents is provided in the section below about Intent Resolution .

Caution: Using an intent filter isn't a secure way to prevent other apps from starting your components. Although intent filters restrict a component to respond to only certain kinds of implicit intents, another app can potentially start your app component by using an explicit intent if the developer determines your component names. If it's important that only your own app is able to start one of your components, do not declare intent filters in your manifest. Instead, set the exported attribute to "false" for that component.

Аналогично, чтобы избежать непреднамеренного запуска 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 Panorama .

Сопоставление намерений с фильтрами намерений других приложений

Если другое приложение ориентировано на 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 так, как если бы он выполнялся из собственного процесса вашего приложения.

К основным вариантам использования находящейся на рассмотрении заявки относятся следующие:

  • Объявление намерения, которое будет выполнено, когда пользователь совершит действие с вашим уведомлением ( Intent будет выполнено NotificationManager уведомлений Android).
  • Объявление намерения, которое будет выполнено, когда пользователь совершит действие с помощью вашего виджета приложения (на главном экране приложение выполнит Intent ).
  • Объявление намерения, которое должно быть выполнено в указанное будущее время ( Intent выполняется AlarmManager системы Android).

Just as each Intent object is designed to be handled by a specific type of app component (either an Activity , a Service , or a BroadcastReceiver ), so too must a PendingIntent be created with the same consideration. When using a pending intent, your app doesn't execute the intent with a call such as startActivity() . Instead, you must declare the intended component type when you create the PendingIntent by calling the respective creator method:

Если ваше приложение не получает ожидающие запросы от других приложений, то описанные выше методы создания 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)

Java

PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE);

Однако в некоторых случаях требуется использование изменяемых объектов PendingIntent :

  • Поддержка действий прямого ответа в уведомлениях . Прямой ответ требует изменения данных клипа в объекте PendingIntent, связанном с ответом. Обычно это изменение запрашивается путем передачи флага FILL_IN_CLIP_DATA методу fillIn() .
  • Связывание уведомлений с фреймворком Android Auto с использованием экземпляров CarAppExtender .
  • Размещение диалогов во всплывающих окнах с использованием экземпляров PendingIntent . Изменяемый объект PendingIntent позволяет системе применять правильные флаги, такие как FLAG_ACTIVITY_MULTIPLE_TASK и FLAG_ACTIVITY_NEW_DOCUMENT .
  • Запрос информации о местоположении устройства осуществляется путем вызова requestLocationUpdates() или аналогичных API-функций. Изменяемый объект PendingIntent позволяет системе добавлять дополнительные параметры Intent, представляющие события жизненного цикла местоположения. К таким событиям относятся изменение местоположения и появление доступного поставщика услуг.
  • Планирование будильников с помощью AlarmManager . Изменяемый объект PendingIntent позволяет системе добавлять дополнительный параметр EXTRA_ALARM_COUNT в Intent. Этот параметр представляет собой количество срабатываний повторяющегося будильника. Благодаря этому параметру Intent может точно уведомлять приложение о том, срабатывал ли повторяющийся будильник несколько раз, например, когда устройство находилось в спящем режиме.

Если ваше приложение создает изменяемый объект PendingIntent , настоятельно рекомендуется использовать явное указание Intent и заполнить поле ComponentName . Таким образом, всякий раз, когда другое приложение вызывает PendingIntent и передает управление обратно вашему приложению, всегда будет запускаться один и тот же компонент в вашем приложении.

Используйте явные указания намерений внутри ожидающих намерений.

Чтобы лучше определить, как другие приложения могут использовать ожидающие выполнения интенты вашего приложения, всегда оборачивайте ожидающий интент в явно заданный интент . Чтобы следовать этой рекомендации, выполните следующие действия:

  1. Убедитесь, что поля action, package и component базового намерения заданы.
  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 все равно пройдет проверку. Следовательно, намерение без категорий всегда проходит эту проверку, независимо от того, какие категории указаны в фильтре.

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

Data test

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

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

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

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

The following example shows possible values for these attributes:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Intent matching

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

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