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

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() .

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

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

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

  • Явные намерения определяют, какой компонент какого приложения будет соответствовать намерению, указывая полное ComponentName . Обычно вы используете явное намерение для запуска компонента в своём приложении, поскольку знаете имя класса активности или службы, которую хотите запустить. Например, вы можете запустить новую активность в своём приложении в ответ на действие пользователя или запустить службу для загрузки файла в фоновом режиме.
  • Неявные намерения не называют конкретный компонент, а вместо этого объявляют общее действие, которое необходимо выполнить, что позволяет компоненту из другого приложения выполнить его. Например, если вы хотите показать пользователю местоположение на карте, вы можете использовать неявное намерение, чтобы запросить у другого совместимого приложения отображение указанного местоположения на карте.

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

Рисунок 1. Как неявное намерение передаётся через систему для запуска другого действия: [1] Действие A создаёт Intent с описанием действия и передаёт его методу startActivity() . [2] Система Android ищет во всех приложениях фильтр намерений, соответствующий этому намерению. При обнаружении совпадения [3] система запускает соответствующее действие ( действие B ), вызывая его метод onCreate() и передавая ему Intent .

При использовании неявного намерения система Android находит подходящий компонент для запуска, сравнивая его содержимое с фильтрами намерений, объявленными в файле манифеста других приложений на устройстве. Если намерение соответствует фильтру намерений, система запускает этот компонент и передаёт ему объект Intent . Если несколько фильтров намерений совместимы, система отображает диалоговое окно, позволяющее пользователю выбрать приложение для использования.

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

Внимание: Чтобы обеспечить безопасность вашего приложения, всегда используйте явное намерение при запуске Service и не объявляйте фильтры намерений для своих служб. Использование неявного намерения для запуска службы представляет угрозу безопасности, поскольку невозможно точно знать, какая служба отреагирует на намерение, и пользователь не видит, какая служба запустится. Начиная с Android 5.0 (уровень API 21), система выдаёт исключение при вызове bindService() с неявным намерением.

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

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

Основная информация, содержащаяся в 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 ), ссылающийся на данные, над которыми необходимо выполнить действие, и/или тип MIME этих данных. Тип предоставляемых данных обычно определяется действием намерения. Например, если действие — ACTION_EDIT , данные должны содержать URI документа, который нужно изменить.

При создании намерения часто важно указать тип данных (MIME-тип) в дополнение к URI. Например, активность, способная отображать изображения, вероятно, не сможет воспроизвести аудиофайл, даже если форматы URI будут схожими. Указание MIME-типа данных помогает системе Android найти оптимальный компонент для обработки вашего намерения. Однако иногда MIME-тип можно определить по URI, особенно если данные представляют собой URI content: URI content: указывает, что данные расположены на устройстве и контролируются 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 вы можете указать получателя с помощью ключа 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 или 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)

Ява

// 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 , содержащее данные типа «text/plain»). Если есть только одно приложение, способное обработать намерение, оно немедленно открывается и получает это намерение. Если другие приложения не могут его обработать, ваше приложение может перехватить возникающее исключение ActivityNotFoundException . Если несколько действий принимают намерение, система отображает диалоговое окно, подобное показанному на рисунке 2, чтобы пользователь мог выбрать, какое приложение использовать.

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

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

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

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

Однако, если на намерение могут реагировать несколько приложений, и пользователь может каждый раз использовать другое приложение, следует явно отображать диалоговое окно выбора. В этом диалоговом окне пользователю предлагается выбрать приложение для выполнения действия (пользователь не может выбрать приложение по умолчанию). Например, когда ваше приложение выполняет команду «Поделиться» с помощью действия ACTION_SEND , пользователи могут захотеть поделиться информацией через другое приложение в зависимости от текущей ситуации, поэтому всегда следует использовать диалоговое окно выбора, как показано на рисунке 2.

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

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

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

Более подробную информацию о том, как определить эту ситуацию и внести изменения в свое приложение, можно найти в публикации блога об Android Nesting Intents на Medium.

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

Чтобы проверить наличие небезопасных запусков намерений в вашем приложении, вызовите метод detectUnsafeIntentLaunch() при настройке VmPolicy , как показано в следующем фрагменте кода. Если ваше приложение обнаруживает нарушение 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, следуйте этим рекомендациям.

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

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

Используйте PendingIntent вместо вложенного Intent. Таким образом, когда другое приложение распаковывает 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 панорам 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 системы Android выполняет Intent ).
  • Объявление намерения, которое будет выполнено, когда пользователь выполняет действие с вашим виджетом приложения (приложение на главном экране выполняет Intent ).
  • Объявление намерения, которое будет выполнено в указанное время в будущем ( AlarmManager системы Android выполняет Intent ).

Так же, как каждый объект Intent предназначен для обработки определённым типом компонента приложения ( Activity , Service или BroadcastReceiver ), PendingIntent также должен создаваться с теми же соображениями. При использовании отложенного намерения приложение не выполняет его с помощью вызова, например, startActivity() . Вместо этого необходимо объявить предполагаемый тип компонента при создании PendingIntent , вызвав соответствующий метод-создателя:

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

Котлин

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

Если ваше приложение создаёт изменяемый объект PendingIntent , настоятельно рекомендуется использовать явное намерение и заполнить 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 всё равно будет успешно выполнено. Таким образом, намерение без категорий всегда проходит этот тест, независимо от того, какие категории объявлены в фильтре.

Примечание. 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, указанными в фильтре. Правила следующие:

  1. Намерение, не содержащее ни URI, ни типа MIME, проходит проверку только в том случае, если фильтр не указывает какие-либо URI или типы MIME.
  2. Намерение, которое содержит URI, но не имеет типа MIME (ни явного, ни выводимого из URI), проходит тест только в том случае, если его URI соответствует формату URI фильтра, а фильтр также не указывает тип MIME.
  3. Намерение, содержащее тип MIME, но не URI, проходит проверку, только если фильтр указывает тот же тип MIME и не указывает формат URI.
  4. Намерение, содержащее как URI, так и тип MIME (явный или выведенный из URI), проходит часть теста MIME-типа только в том случае, если этот тип соответствует типу, указанному в фильтре. Он проходит часть теста, посвященную 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 заполняет панель запуска приложений, находя все действия с фильтрами намерений, которые определяют действие ACTION_MAIN и категорию CATEGORY_LAUNCHER . Сопоставление считается успешным только в том случае, если действия и категории в Intent соответствуют фильтру, как описано в документации по классу IntentFilter .

Ваше приложение может использовать сопоставление намерений аналогично тому, как это делает приложение Home. PackageManager имеет набор методов query...() , которые возвращают все компоненты, которые могут принять определенное намерение, и аналогичную серию методов resolve...() , которые определяют лучший компонент для ответа на намерение. Например, queryIntentActivities() возвращает список всех действий, которые могут выполнить намерение, переданное в качестве аргумента, а queryIntentServices() возвращает аналогичный список служб. Ни один из методов не активирует компоненты; они просто перечисляют тех, кто может ответить. Существует аналогичный метод queryBroadcastReceivers() для приемников широковещательных сообщений.

,

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() .

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

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

Есть два типа намерений:

  • Явные намерения определяют, какой компонент какого приложения будет удовлетворять намерению, путем указания полного ComponentName . Обычно вы используете явное намерение запустить компонент в своем приложении, поскольку знаете имя класса действия или службы, которую хотите запустить. Например, вы можете начать новое действие в своем приложении в ответ на действие пользователя или запустить службу для загрузки файла в фоновом режиме.
  • Неявные намерения не называют конкретный компонент, а вместо этого объявляют общее действие, которое необходимо выполнить, что позволяет компоненту из другого приложения его обрабатывать. Например, если вы хотите показать пользователю местоположение на карте, вы можете использовать неявное намерение, чтобы запросить, чтобы другое совместимое приложение показало указанное местоположение на карте.

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

Рисунок 1. Как неявное намерение доставляется через систему для запуска другого действия: [1] Действие A создает Intent с описанием действия и передает его в startActivity() . [2] Система Android ищет во всех приложениях фильтр намерений, соответствующий намерению. Когда совпадение найдено, [3] система запускает действие сопоставления ( действие B ), вызывая метод onCreate() и передавая ему Intent .

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

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

Внимание: Чтобы обеспечить безопасность вашего приложения, всегда используйте явное намерение при запуске Service и не объявляйте фильтры намерений для своих служб. Использование неявного намерения запустить службу представляет собой угрозу безопасности, поскольку вы не можете быть уверены, какая служба отреагирует на это намерение, а пользователь не может видеть, какая служба запускается. Начиная с Android 5.0 (уровень API 21), система выдает исключение, если вы вызываете bindService() с неявным намерением.

Создание намерения

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

Основная информация, содержащаяся в 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 ), который ссылается на данные, над которыми необходимо действовать, и/или MIME-тип этих данных. Тип предоставляемых данных обычно определяется действием намерения. Например, если действие — ACTION_EDIT , данные должны содержать URI документа, который нужно редактировать.

При создании намерения часто важно указать тип данных (их 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.

Категория
Строка, содержащая дополнительную информацию о типе компонента, который должен обрабатывать намерение. В намерении можно поместить любое количество описаний категорий, но для большинства намерений категория не требуется. Вот некоторые распространенные категории:
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 , которые функционируют как метаданные для намерения. Флаги могут указывать системе 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)

Ява

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

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

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

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

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

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

Чтобы проверить запуск небезопасных намерений в вашем приложении, вызовите метод detectUnsafeIntentLaunch() при настройке VmPolicy , как показано в следующем фрагменте кода. Если ваше приложение обнаруживает нарушение 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, следуйте этим рекомендациям.

Скопируйте только необходимые дополнения в намерениях и выполните всю необходимую санацию и проверку. Ваше приложение может скопировать дополнительные элементы из одного намерения в другое, которое используется для запуска нового компонента. Это происходит, когда ваше приложение вызывает 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 панорам 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 , как если бы оно было выполнено из собственного процесса вашего приложения.

Основные варианты использования ожидающего намерения включают следующее:

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

Точно так же, как каждый объект Intent предназначен для обработки определенным типом компонента приложения ( Activity , Service или BroadcastReceiver ), так и PendingIntent должен создаваться с теми же соображениями. При использовании ожидающего намерения ваше приложение не выполняет его с помощью такого вызова, как startActivity() . Вместо этого вы должны объявить предполагаемый тип компонента при создании PendingIntent , вызвав соответствующий метод создателя:

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

Each method takes the current app Context , the Intent you want to wrap, and one or more flags that specify how the intent should be used (such as whether the intent can be used more than once).

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.

Тест категории

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

Сопоставление намерений

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.