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

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

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

Каждый метод принимает текущий 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 Media). Каждая часть 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, а фильтр не указывает URI. Другими словами, предполагается, что компонент поддерживает content: и file: данные, если его фильтр перечисляет только тип MIME.

ПРИМЕЧАНИЕ. Если намерение указывает URI или тип MIME, тест данных не удастся, если в <data> элементах <intent-filter> >.

Это последнее правило, правило (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 Action и CATEGORY_LAUNCHER . Матч является успешным только в том случае, если действия и категории в намерениях совпадают с фильтром, как описано в документации для класса IntentFilter .

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