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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Примечание: При запуске 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 документа для редактирования.

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

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

При вызове функции startActivity() система проверяет все установленные приложения, чтобы определить, какие из них могут обрабатывать этот тип намерения (намерение с действием ACTION_SEND , содержащее данные в формате "text/plain"). Если только одно приложение может его обработать, оно немедленно открывается и получает это намерение. Если ни одно другое приложение не может его обработать, ваше приложение может перехватить исключение ActivityNotFoundException . Если несколько действий принимают намерение, система отображает диалоговое окно, подобное показанному на рисунке 2, чтобы пользователь мог выбрать, какое приложение использовать.

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

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

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

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

Однако, если на намерение могут реагировать несколько приложений, и пользователь может захотеть использовать разные приложения каждый раз, следует явно отобразить диалоговое окно выбора. Диалоговое окно выбора запрашивает у пользователя, какое приложение использовать для действия (пользователь не может выбрать приложение по умолчанию для действия). Например, когда ваше приложение выполняет действие «поделиться» с помощью действия ACTION_SEND , пользователи могут захотеть поделиться с помощью другого приложения в зависимости от текущей ситуации, поэтому всегда следует использовать диалоговое окно выбора, как показано на рисунке 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);
}

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

Ваше приложение может запускать интенты для навигации между компонентами внутри приложения или для выполнения действия от имени другого приложения. Для повышения безопасности платформы Android 12 (уровень API 31) и выше предоставляет функцию отладки, которая предупреждает вас, если ваше приложение выполняет небезопасный запуск интента. Например, ваше приложение может выполнить небезопасный запуск вложенного интента , который передается в качестве дополнительного параметра в другой интент.

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

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

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

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

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

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

Каждый метод принимает текущий Context приложения, Intent , который вы хотите обернуть, и один или несколько флагов, определяющих, как следует использовать Intent (например, можно ли использовать Intent более одного раза).

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

Укажите изменчивость

Если ваше приложение ориентировано на Android 12 или выше, необходимо указать изменяемость каждого создаваемого приложением объекта PendingIntent . Чтобы объявить, что данный объект PendingIntent является изменяемым или неизменяемым, используйте флаг PendingIntent.FLAG_MUTABLE или PendingIntent.FLAG_IMMUTABLE соответственно.

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

PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.

По возможности создавайте неизменяемые ожидающие намерения.

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

Котлин

val pendingIntent = PendingIntent.getActivity(applicationContext,
        REQUEST_CODE, intent,
        /* flags */ PendingIntent.FLAG_IMMUTABLE)

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>

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>

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.