Уведомления – это короткие сообщения о событиях в приложении, которые показываются, когда оно не используется. В этом документе рассказывается, как создать уведомление с различными функциями. Общую информацию о том, как уведомления показываются на устройствах Android, можно найти в обзоре уведомлений. Пример кода, в котором используются уведомления, можно найти в образце SociaLite на GitHub.
В коде на этой странице используются API NotificationCompat из библиотеки AndroidX. Эти API позволяют добавлять функции, доступные только в новых версиях Android, сохраняя при этом совместимость с Android 9 (уровень API 28).
Однако некоторые функции, например встроенный ответ, в более ранних версиях не работают.
Как создать простое уведомление
В свернутом виде уведомление содержит значок, заголовок и небольшой фрагмент текста. В этом разделе рассказывается, как создать уведомление, по которому пользователь сможет перейти к действию в вашем приложении.
Рисунок 1. Уведомление со значком, заголовком и текстом.
Подробнее об элементах уведомлений…
Как объявить динамическое разрешение
В Android 13 (уровень API 33) и более поздних версиях поддерживается разрешение времени выполнения для отправки уведомлений, не относящихся к исключениям (в том числе уведомлений от активных служб), из приложения.
Разрешение, которое необходимо указать в файле манифеста приложения, приведено в следующем фрагменте кода:
<manifest ...> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application ...> ... </application> </manifest>
Подробнее о динамических разрешениях…
Как задать контент уведомления
Чтобы начать работу, задайте контент и канал уведомления с помощью объекта NotificationCompat.Builder. В следующем примере показано, как создать уведомление со следующими параметрами:
Небольшой значок, заданный с помощью
setSmallIcon(). Это единственный видимый пользователям контент, который требуется.Название, заданное
setContentTitle().Основной текст, заданный с помощью
setContentText().Приоритет уведомления, заданный
setPriority(). Приоритет определяет, насколько навязчивым будет уведомление в Android 7.1 и более ранних версиях. В Android 8.0 и более поздних версий вместо этого задайте важность канала, как показано в следующем разделе.
val textTitle = "Title" val textContent = "Content" val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setContentTitle(textTitle) .setContentText(textContent) .setPriority(NotificationCompat.PRIORITY_DEFAULT)
Конструктор NotificationCompat.Builder требует указать идентификатор канала. Это необходимо для совместимости с Android 8.0 (уровень API 26) и более поздними версиями, но игнорируется более ранними версиями.
По умолчанию текст уведомления обрезается до одной строки. Вы можете показывать дополнительную информацию, создав разворачиваемое уведомление.
Рисунок 2. Разворачиваемое уведомление в свернутом и развернутом виде.
Если вы хотите, чтобы уведомление было длиннее, добавьте шаблон стиля с setStyle(), чтобы включить разворачиваемое уведомление. Например, следующий код создает текстовую область большего размера:
val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setContentTitle("My notification") .setContentText("Much longer text that cannot fit one line...") .setStyle(NotificationCompat.BigTextStyle() .bigText("Much longer text that cannot fit one line...")) .setPriority(NotificationCompat.PRIORITY_DEFAULT)
Подробнее о других стилях уведомлений, в том числе о том, как добавить изображение и элементы управления воспроизведением мультимедиа, можно узнать в статье Как создать разворачиваемое уведомление.
Как создать канал и задать его важность
Чтобы показывать уведомления на устройствах с Android 8.0 и более поздних версий, зарегистрируйте канал уведомлений приложения в системе, передав экземпляр NotificationChannel в createNotificationChannel(). Следующий код заблокирован условием в версии SDK_INT:
fun createNotificationChannel(context: Context) { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is not in the Support Library. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = context.getString(R.string.channel_name) val descriptionText = context.getString(R.string.channel_description) val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText } // Register the channel with the system. val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java) as NotificationManager notificationManager.createNotificationChannel(channel) } }
Поскольку канал уведомлений необходимо создать до того, как отправлять уведомления на устройствах с Android 8.0 и более поздних версий, этот код нужно выполнить при запуске приложения. Безопасно вызывать этот метод несколько раз, поскольку создание существующего канала ничего не делает.
Конструктор NotificationChannel требует указать уровень важности с помощью константы NotificationManager. Определяет, как прервать пользователя. Чтобы поддерживать Android 7.1 и более ранние версии, также задайте приоритет с помощью параметра setPriority(), как показано в предыдущем примере.
Хотя вы должны задать важность или приоритет, система не гарантирует поведение оповещения. Система может изменить его на основе других факторов, а пользователь всегда может настроить уровень важности канала.
Подробнее об уровнях важности уведомлений…
Как настроить действие при нажатии на уведомление
Каждое уведомление должно реагировать на нажатие, обычно открывая в приложении действие, связанное с уведомлением. Для этого укажите намерение контента, определенное с помощью объекта PendingIntent, и передайте его в setContentIntent().
В приведенном ниже фрагменте кода показано, как создать базовое намерение открыть действие, когда пользователь нажимает на уведомление:
// Create an explicit intent for an Activity in your app. val intent = Intent(context, AlertDetails::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) // Set the intent that fires when the user taps the notification. .setContentIntent(pendingIntent) .setAutoCancel(true)
Этот код вызывает функцию setAutoCancel(), которая автоматически удаляет уведомление, когда пользователь нажимает на него.
Флаги намерения в приведенном выше примере сохраняют ожидаемый пользователем переход после того, как он откроет ваше приложение с помощью уведомления. В зависимости от типа действия, которое вы хотите выполнить, можно использовать следующие варианты:
Действие, которое существует исключительно для ответов на уведомление. В обычном режиме работы приложения пользователь не переходит к этому действию, поэтому оно запускает новую задачу, а не добавляется в существующую задачу и стек возврата. Это тип намерения, созданного в предыдущем примере.
Действие, которое выполняется в обычном режиме работы приложения. В этом случае при запуске действия создается стек возврата, чтобы сохранить ожидаемое поведение кнопок "Назад" и "Вверх".
Показ уведомления
Чтобы появилось уведомление, вызовите функцию NotificationManagerCompat.notify(), передав ей уникальный идентификатор уведомления и результат функции NotificationCompat.Builder.build(). Пример:
with(NotificationManagerCompat.from(context)) { if (ActivityCompat.checkSelfPermission( context, Manifest.permission.POST_NOTIFICATIONS ) != PackageManager.PERMISSION_GRANTED ) { // TODO: Consider calling ActivityCompat#requestPermissions here // to request the missing permissions, and then overriding // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, // grantResults: IntArray) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return@with } // notificationId is a unique int for each notification that you must define. notify(notificationId, builder.build())
Сохраните идентификатор уведомления, который вы передаете в NotificationManagerCompat.notify(), поскольку он понадобится вам, если вы захотите обновить или удалить уведомление.
Кроме того, чтобы протестировать основные уведомления на устройствах с Android 13 и более поздних версий, включите уведомления вручную или создайте диалоговое окно для запроса уведомлений.
Как добавить командные кнопки
Уведомление может содержать до трех кнопок действий, позволяющих пользователю быстро отреагировать на него, например отложить напоминание или ответить на текстовое сообщение. Однако эти командные кнопки не должны дублировать действие, выполняемое при нажатии на уведомление.
Рисунок 3. Уведомление с одной кнопкой действия.
Чтобы добавить командную кнопку, передайте PendingIntent в метод addAction(). Это похоже на настройку действия по умолчанию при нажатии на уведомление, но вместо запуска действия можно выполнять другие действия, например запустить BroadcastReceiver, который выполняет задачу в фоновом режиме, чтобы действие не прерывало работу уже открытого приложения.
Например, в следующем коде показано, как отправить широковещательное сообщение определенному получателю:
val ACTION_SNOOZE = "snooze" val snoozeIntent = Intent(context, MyBroadcastReceiver::class.java).apply { action = ACTION_SNOOZE putExtra(EXTRA_NOTIFICATION_ID, 0) } val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, snoozeIntent, PendingIntent.FLAG_IMMUTABLE) val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .addAction(R.drawable.snooze, context.getString(R.string.snooze), snoozePendingIntent)
Подробнее о том, как создать BroadcastReceiver для выполнения фоновых задач, рассказывается в обзоре трансляций.
Если вы хотите создать уведомление с кнопками управления воспроизведением, например для приостановки и пропуска треков, узнайте, как создать уведомление с элементами управления медиаконтентом.
Как добавить действие "Быстрый ответ"
Действие "Быстрый ответ", появившееся в Android 7.0 (уровень API 24), позволяет пользователям вводить текст прямо в уведомление. Затем текст передается в приложение без открытия окна. Например, вы можете использовать действие быстрого ответа, чтобы пользователи могли отвечать на текстовые сообщения или обновлять списки задач прямо из уведомления.
Рисунок 4. При нажатии кнопки "Ответить" открывается поле ввода текста.
Действие быстрого ответа будет представлено в виде дополнительной кнопки в уведомлении, при нажатии на которую откроется текстовое поле. Когда пользователь закончит ввод, система прикрепит текстовый ответ к намерению, указанному для действия уведомления, и отправит намерение в ваше приложение.
Как добавить кнопку "Ответить"
Чтобы создать действие уведомления, поддерживающее быстрый ответ, выполните следующие действия:
Создайте экземпляр RemoteInput.Builder, который можно добавить в действие уведомления. Конструктор этого класса принимает строку, которую система использует в качестве ключа для текстового ввода. Затем приложение использует этот ключ, чтобы получить текст.
// Key for the string that's delivered in the action's intent. val replyLabel: String = context.resources.getString(R.string.reply_label) val remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run { setLabel(replyLabel) build() }
Создайте PendingIntent для действия "Ответить".
// Build a PendingIntent for the reply action to trigger. val replyPendingIntent: PendingIntent = PendingIntent.getBroadcast(context, conversationId, getMessageReplyIntent(conversationId), PendingIntent.FLAG_MUTABLE)
Прикрепите объект RemoteInput к действию, используя метод addRemoteInput().
// Create the reply action and add the remote input. val action: NotificationCompat.Action = NotificationCompat.Action.Builder(R.drawable.reply, context.getString(R.string.reply_label), replyPendingIntent) .addRemoteInput(remoteInput) .build()
Применить действие к уведомлению и отправить уведомление.
// Build the notification and add the action.
val newMessageNotification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle(context.getString(R.string.title))
.setContentText(context.getString(R.string.content))
.addAction(action)
.build()
// Issue the notification.
NotificationManagerCompat.from(context).notify(notificationId, newMessageNotification)
Когда пользователь запускает действие уведомления, система предлагает ему ввести ответ, как показано на рисунке 4.
Как получить пользовательский ввод из ответа
Чтобы получить данные, введенные пользователем в интерфейсе ответа на уведомление, вызовите метод RemoteInput.getResultsFromIntent(), передав ему объект Intent, полученный вашим объектом BroadcastReceiver:
private fun getMessageText(intent: Intent): CharSequence? { return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY) }
После обработки текста обновите уведомление, вызвав метод NotificationManagerCompat.notify() с тем же идентификатором и тегом (если он использовался). Это необходимо, чтобы скрыть интерфейс быстрого ответа и подтвердить пользователю, что его ответ получен и обработан правильно.
// Build a new notification, which informs the user that the system // handled their interaction with the previous notification. val repliedNotification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.message) .setContentText(context.getString(R.string.replied)) .build() // Issue the new notification. NotificationManagerCompat.from(context).notify(notificationId, repliedNotification)
Как получить другие данные
Обработка других типов данных с помощью RemoteInput выполняется аналогичным образом. В примере ниже в качестве входных данных используется изображение.
val replyLabel: String = context.resources.getString(R.string.reply_label) val remoteInput: RemoteInput = RemoteInput.Builder(KEY_REPLY).run { setLabel(replyLabel) // Allow for image data types in the input. // This method can be used again to allow for other data types. setAllowDataType("image/*", true) build() }
Вызовите RemoteInput#getDataResultsFromIntent и извлеките соответствующие данные.
class ReplyReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val dataResults = RemoteInput.getDataResultsFromIntent(intent, KEY_REPLY) val imageUri: Uri? = dataResults?.get("image/*") as? Uri if (imageUri != null) { // Extract the image context.contentResolver.openInputStream(imageUri)?.use { inputStream -> val bitmap = BitmapFactory.decodeStream(inputStream) // Display the image // ... } } } companion object { const val KEY_REPLY = "key_reply" const val KEY_TEXT_REPLY = "key_text_reply" } }
При работе с этим новым уведомлением используйте контекст, который передается в метод onReceive() получателя.
Добавьте ответ в конец уведомления, вызвав функцию setRemoteInputHistory(). Однако если вы создаете мессенджер, создайте уведомление в стиле сообщений и добавьте новое сообщение в чат.
Дополнительные советы по настройке уведомлений от мессенджеров можно найти в разделе рекомендаций по мессенджерам.
Как показать срочное сообщение
Приложению может потребоваться показать срочное сообщение, например о входящем звонке или будильнике. В таких случаях вы можете связать полноэкранный интент с уведомлением.
Когда уведомление появляется, пользователи видят одно из следующих сообщений в зависимости от того, заблокировано ли устройство:
- Если устройство пользователя заблокировано, на экране появится полноэкранный объект Activity, который закроет экран блокировки.
- Если устройство пользователя разблокировано, уведомление показывается в развернутом виде с вариантами действий.
В приведенном ниже фрагменте кода показано, как связать уведомление с полноэкранным намерением:
val fullScreenIntent = Intent(context, ImportantActivity::class.java) val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent, PendingIntent.FLAG_IMMUTABLE) val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setFullScreenIntent(fullScreenPendingIntent, true)
Настройка видимости на заблокированном экране
Чтобы настроить уровень детализации уведомлений на заблокированном экране, вызовите метод setVisibility() и укажите одно из следующих значений:
VISIBILITY_PUBLIC– на заблокированном экране показывается полное содержимое уведомлений.VISIBILITY_SECRET– на заблокированном экране не показывается никакая часть уведомления.VISIBILITY_PRIVATE– на заблокированном экране показывается только основная информация, например значок уведомления и название контента. Полный текст уведомления не показывается.
Если вы зададите значение VISIBILITY_PRIVATE, то сможете указать альтернативную версию уведомления, в которой скрыты некоторые детали. Например, приложение для обмена SMS-сообщениями может показывать уведомление "У вас 3 новых текстовых сообщения", но скрывать их содержимое и отправителей. Чтобы создать альтернативное уведомление, сначала создайте его с помощью NotificationCompat.Builder. Затем прикрепите альтернативное уведомление к обычному с помощью setPublicVersion().
Помните, что пользователь всегда может выбрать, будут ли уведомления показываться на заблокированном экране. Он может управлять ими с помощью каналов уведомлений вашего приложения.
Как изменить уведомление
Чтобы обновить уведомление после его отправки, снова вызовите функцию NotificationManagerCompat.notify(), передав ей тот же идентификатор, который вы использовали ранее. Если предыдущее уведомление было закрыто, вместо него создается новое.
Вы можете вызвать метод setOnlyAlertOnce(), чтобы уведомление прерывало пользователя (звуком, вибрацией или визуальными подсказками) только при первом появлении, а не при последующих обновлениях.
Как удалить уведомление
Уведомления остаются видимыми, пока не произойдет одно из следующих событий:
- Пользователь закрывает уведомление.
- Пользователь нажимает на уведомление, если вы вызываете
setAutoCancel()при создании уведомления. - Вы вызываете
cancel()для определенного идентификатора уведомления. При этом также удаляются текущие уведомления. - Вы вызываете функцию
cancelAll(), которая удаляет все ранее отправленные вами уведомления. - Указанное время истекает, если при создании уведомления вы задали тайм-аут с помощью
setTimeoutAfter(). При необходимости вы можете отменить уведомление до истечения указанного времени ожидания.
Рекомендации по работе с мессенджерами
При создании уведомлений для приложений обмена сообщениями и чатов следуйте приведенным ниже рекомендациям.
Как использовать MessagingStyle
В Android 7.0 (уровень API 24) и более поздних версиях есть шаблон уведомлений, предназначенный специально для сообщений. С помощью класса NotificationCompat.MessagingStyle можно изменить несколько ярлыков, которые показываются в уведомлении, в том числе название чата, дополнительные сообщения и представление контента для уведомления.
В приведенном ниже фрагменте кода показано, как настроить стиль уведомления с помощью класса MessagingStyle.
val message1 = NotificationCompat.MessagingStyle.Message( messages[0].text, messages[0].time, messages[0].sender ) val message2 = NotificationCompat.MessagingStyle.Message( messages[1].text, messages[1].time, messages[1].sender ) notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_logo) .setStyle( NotificationCompat.MessagingStyle(Person.Builder().setName("Me").build()) .addMessage(message1) .addMessage(message2) ) .build()
Начиная с Android 9.0 (уровень API 28), для оптимального отображения уведомлений и их аватаров также необходимо использовать класс Person.
При использовании NotificationCompat.MessagingStyle выполните следующие действия:
- Вызов
MessagingStyle.setConversationTitle()позволяет задать название группового чата с более чем двумя участниками. Например, это может быть название группового чата или список участников, если у чата нет названия. Без этого письмо может быть ошибочно отнесено к переписке с отправителем последнего сообщения в цепочке. - Используйте метод
MessagingStyle.setData(), чтобы добавить медиасообщения, например изображения. Поддерживаются MIME-типы изображений, соответствующие шаблону image/*.
Как использовать функцию "Быстрый ответ"
Функция "Быстрый ответ" позволяет пользователю ответить на сообщение в цепочке.
- После того как пользователь ответит на уведомление, используя встроенное действие, обновите уведомление
MessagingStyleс помощьюMessagingStyle.addMessage(). Не отменяйте и не отзывайте уведомление. Если не отменить уведомление, пользователь сможет отправить несколько ответов из него. - Чтобы действие "Ответить в тексте" было совместимо с Wear OS, вызовите функцию
Action.WearableExtender.setHintDisplayInlineAction(true). - Используйте метод
addHistoricMessage(), чтобы добавить в уведомление историю сообщений и предоставить контекст для быстрого ответа.
Как включить быстрые ответы
- Чтобы включить быстрые ответы, вызовите
setAllowGeneratedResponses(true)в действии ответа. В результате быстрые ответы будут доступны пользователям, когда уведомление передается на устройство Wear OS. Быстрые ответы генерируются моделью машинного обучения, которая работает на часах и использует контекст уведомленияNotificationCompat.MessagingStyle. При этом никакие данные не загружаются в интернет.
Как добавить метаданные уведомлений
- Назначьте метаданные уведомлений, чтобы указать системе, как обрабатывать уведомления приложения, когда устройство находится в режиме "Не беспокоить". Например, чтобы отключить режим "Не беспокоить", используйте метод
addPerson()илиsetCategory(Notification.CATEGORY_MESSAGE).