Урок 184. Android Notifications. Уведомления. Основы
Notifications — это уведомления, которые пользователь видит в верхней части экрана, когда ему приходит новое письмо, сообщение, обновление и т.п. В ближайшие несколько уроков мы подробно разберем, какие возможности предоставлены разработчикам для показа уведомлений.
В этом уроке рассмотрим основы — отображение/обновление/удаление уведомления и обработка нажатия на него.
Отображение
Код создания простого уведомления выглядит так:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Title") .setContentText("Notification text"); Notification notification = builder.build(); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(1, notification);
Используем билдер, в котором указываем иконку, заголовок и текст для уведомления. Методом build получаем готовое уведомление.
Далее используем NotificationManager и его метод notify, чтобы показать созданное уведомление. Кроме notification, требуется передать id. Это необходимо, чтобы в дальнейшем мы могли использовать этот id для обновления или удаления уведомления.
Конструктор new NotificationCompat.Builder(Context) будет помечен как Deprecated, если вы используете библиотеку appCompat версии 26 и выше. Так получилось потому, что в Android API 26 появился новый конструктор и рекомендуется использовать его. Пока не обращайте внимание на это. В одном из следующих уроков мы рассмотрим использование правильного конструктора.
Запустив этот код, мы увидим уведомление

Оно отображает иконку и два текста, которые мы указывали в билдере. Нажатие на него ни к чему не приведет, т.к. мы не реализовали обработчик нажатия. Мы это сделаем чуть позже.
Обновление
Мы отобразили уведомление и теперь хотим его обновить. Для этого нужно просто снова показать уведомление методом notify и использовать при этом тот же id.
Это будет выглядеть так:
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(android.R.drawable.ic_dialog_email) .setContentTitle("Title change") .setContentText("Notification text change"); Notification notification = builder.build(); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(1, notification);
Код полностью аналогичен коду, что мы использовали при отображении уведомления. Только в билдере используем другие тексты и иконку. Самое главное, что в методе notify мы снова используем NotificationManager по этому id найдет уведомление, которое мы отобразили чуть раньше и заменит его новым

Несколько уведомлений
Чтобы показать новое уведомление, а не обновить уже существующее, надо использовать другой id в методе notify.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Title") .setContentText("Notification text"); Notification notification = builder.build(); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(1, notification);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(android.R.drawable.ic_dialog_email) .setContentTitle("Title 2") .setContentText("Notification text 2"); Notification notification = builder.build(); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(2, notification);
Мы использовали разные id в методе notify и получили два разных уведомления

Удаление
Чтобы удалить уведомление, используем NotificationManager и его метод cancel с указанием id уведомления.
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.cancel(1);
Либо методом cancelAll можем удалить все уведомления сразу
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.cancelAll();
При удалении уведомления нет необходимости проверять, отображается оно или нет. Если уведомления по каким-то причинам уже нет, то просто ничего не произойдет.
Обработка нажатия
Чтобы выполнить какое-либо действие по нажатию на уведомление, необходимо использовать PendingIntent. PendingIntent — это контейнер для Intent. Этот контейнер может быть использован для последующего запуска вложенного в него Intent.
Мы будем создавать Intent для запуска, например, Activity, упаковывать этот Intent в PendingIntent и передавать PeningIntent в уведомление. По нажатию на уведомление, система достанет из него PedningIntent и использует вложенный в него Intent, чтобы запустить Activity.
Давайте посмотрим, как это выглядит на практике:
Intent resultIntent = new Intent(this, MainActivity.class); PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Создаем Intent для запуска Activity и упаковываем его в PedningIntent.
Подробно о PedningIntent и его параметрах вы можете почитать в Уроке 119. Там я подробно рассмотрел различные кейсы на примерах с уведомлением и вызовом BroadcastReceiver.
Созданный PendingIntent нам надо будет передать в билдер уведомления. Полный код создания уведомления будет выглядеть так:
// Create PendingIntent Intent resultIntent = new Intent(this, MainActivity.class); PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Create Notification NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Title") .setContentText("Notification text") .setContentIntent(resultPendingIntent); Notification notification = builder.build(); // Show Notification NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(1, notification);
Передаем PendingIntent в метод setContentIntent билдера уведомления.
По нажатию на уведомление откроется MainActivity
Обратите внимание, что уведомление не удаляется автоматически после нажатия на него. Чтобы исправить это, можно в билдере уведомления использовать включить параметр autoCancel
setAutoCancel(true)
Уведомление, созданное с этим флагом будет закрываться после нажатия на него.
Билдер уведомления имеет еще несколько методов, которые могут быть полезны.
setNumber — позволяет добавить число в уведомление

setContentInfo — добавит текст справа
В старых версиях это выглядит так

В последних версиях он переехал в верхнюю часть уведомления

setColor — добавит фоновый цвет к иконке

setWhen — можно указать свое время для уведомления (время when). По умолчанию when = времени создания уведомления
setShowWhen — показывать ли время в уведомлении
setUsesChronometer — вместо статичного времени в уведомлении будет отображаться счетчик (00:00), показывающий сколько прошло от времени when. Может быть полезно для уведомления секундомера или звонка.
setOngoing — такое уведомление пользователь не сможет закрыть или смахнуть. Оно будет отображаться поверх обычных уведомлений.
setVibrate, setSound, setLights — настройки вибры, звука и LED индикатора устройства
setPriority — возможность установить приоритет. Доступные значения от -2 (NotificationCompat.PRIORITY_MIN) до 2 (NotificationCompat.PRIORITY_MAX). Поведение может отличаться на разных версиях Android, но общий смысл одинаков — чем выше приоритет, тем выше вероятность того, что пользователь увидит ваше уведомление.
setTimeoutAfter — возможность установить таймаут (в мсек), после которого уведомление само удалится. Добавлен в API 26.
setLargeIcon — возможность задать свою картинку в качестве иконки уведомления.
BitmapFactory.Options options = new BitmapFactory.Options(); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.startandroid, options); NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Title") .setContentText("Notification text") .setLargeIcon(bitmap);
Иконка из setSmallIcon будет видна в статусбаре, когда панель уведомлений не раскрыта.
А само уведомление будет выглядеть так:

setProgress — возможность отобразить прогрессбар в уведомлении
У метода три параметра:
max — максимальное значение прогрессбара. Укажите 0, если надо скрыть прогрессбар.
progress — текущее значение прогрессбара. Может быть от 0 до max.
indeterminate — если true, то будет показан «бесконечный» прогрессбар
int max = 100; // show notification with indeterminate progressbar builder = new NotificationCompat.Builder(this) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Some operation") .setContentText("Preparing") .setProgress(max,0, true); notificationManager.notify(1, builder.build()); new Thread(new Runnable() < @Override public void run() < try catch (InterruptedException e) < e.printStackTrace(); >int progress = 0; while (progress < max) < try < TimeUnit.MILLISECONDS.sleep(300); >catch (InterruptedException e) < e.printStackTrace(); >progress += 10; // show notification with current progress builder.setProgress(max, progress, false) .setContentText(progress + " of " + max); notificationManager.notify(1, builder.build()); > // show notification without progressbar builder.setProgress(0, 10, false) .setContentText("Completed"); notificationManager.notify(1, builder.build()); > >).start();
Сначала отображаем бесконечный прогрессбар и текст Preparing. Т.е. делаем вид, что идет подготовка к выполнению операции.
Затем в отдельном потоке имитируем выполнение операции. Каждые 300 мсек увеличиваем значение progress и обновляем уведомление, чтобы прогрессбар показал текущий прогресс. А также в тексте показываем значение прогресса и максимума.
После выполнения операции скрываем прогрессбар и показываем текст Completed.
Повторюсь, очень рекомендую прочесть и понять Урок 119. В нем я подробно рассматриваю, почему PendingIntent последнего уведомления заменяет PendingIntent предыдущих уведомлений, и как этого можно избежать, используя, например, requestCode.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
Push уведомления в Android. Грабли, костыли и велосипеды
На написание данной статьи меня подтолкнула задача, которая была поставлена передо мной в одном из рабочих проектов: реализовать Push-уведомления в приложении. Казалось, все просто: штудируешь документацию, примеры и вперед. К тому же, опыт работы с уведомлениями уже был. Но не тут то было…
Сервис, в рамках которого реализовано приложение под Android, предъявляет довольно жесткие требования к работе Push-уведомлений. Необходимо в пределах 30-60 секунд оповестить пользователя о некотором действии. При успешном оповещении с устройства пользователя отправляется запрос на сервер с соответствующим статусом. Из документации известно, что сервис GCM (Google Cloud Messaging) не гарантирует доставку PUSH-уведомлений на устройства, поэтому в качестве backdoor варианта, при нарушении этих временных рамок, наш сервис уведомляет пользователя с помощью SMS сообщения. Поскольку стоимость SMS сообщения существенно выше чем PUSH-уведомления, необходимо максимально сократить поток SMS сообщений на клиентские устройства.
Проштудировав документацию и прикрутив пуш-уведомления, мы разослали нескольким клиентам первую сборку приложения для теста и стали ждать. Результаты были примерно следующими:
- при активном Wifi соединении все работает идеально: уведомления доставляются, клиенты рады.
- при активном мобильном интернете началось самое веселье.
- включенный режим Энергосбережения (например, Stamina на устройствах Sony) влияет на работу Push уведомлений;
- у пользователя обязательно должен быть минимум 1 активный Google аккаунт на устройстве;
- необходимо удостовериться в том, что на устройстве установлена актуальная версия приложения “Сервисы Google Play”;
- проверить, не отключены ли уведомления для приложения (галочка на страничке приложения в настройках телефона);
- проверить, не ограничена ли работа фонового режима для приложения (настройка расположена в меню «Использование данных»);
- в документации к GCM указано, что уведомления рассылаются только по определенным портам, поэтому настройки роутера, файервола и антивируса так же стоит учитывать.
На данном этапе очень сильно помогла статья, написанная ребятами из Mail.ru. В ней очень подробно описаны тонкости реализации GCM на клиентской стороне, а так же моменты, в связи с которыми отказываются работать Push уведомления в мобильных сетях. В конечном счете было принято решение о том, чтобы держать свое соединение с сервером в связке с GCM.
Перед тем, как приступить к решению, стоить выделить несколько очень важных моментов, которые позволяют сузить круг потенциально «нерабочих» устройств:
- проблема возникает только при подключении к мобильному интернету;
- по данным клиентов, проблема возникает на версии андроида 4 и выше.
Бывалый разработчик под Android сходу скажет, что решений задачи как минимум 2: использовать Service или AlarmManager. Мы попробовали оба варианта. Рассмотрим первый из них.
Для того, чтобы создать неубиваемый системой сервис, который постоянно будет висеть в фоне и выполнять нашу задачу, мы использовали метод:
startForeground(int notificationID, Notification notification);
- notificationId — некоторый уникальный идентификатор уведомления, который будет выведен в статус баре и в выезжающей шторке;
- notification — само уведомление.
Реализовав данный подход, мы отправили сборку на тест. По результатам выяснилось, что система все-таки выгружает сервис, а по логам мы видели, как происходили существенные временные разрывы при запросе данных в фоне с нашего сервера. Поэтому приступили к реализации второго варианта — AlarmManager.
AlarmManager — это класс, который предоставляет работу с, грубо говоря, «будильником». Он позволяет указать время, по достижении которого система отправит широковещательное уведомление, которое позволит пробудить наше приложение и даст ему возможность выполнить необходимые действия. В работе этого метода есть некоторые ограничения, и их необходимо обработать:
- данные о «будильниках» будут стерты после перезагрузки устройства;
- данные о «будильниках» будут стерты после обновления приложения.
setRepeating()
который позволяет установить повторяющийся с некоторым интервалом «будильник». Прикрутив данный способ, стали тестировать, и тесты показали обратное — «будильник» не повторялся. Стали разбираться в чем дело, посмотрели документацию. И именно там нашли ответ на вопрос — начиная с 19 API lvl (Kitkat) абсолютно все «будильники» в системе стали разовыми. Вывод — всегда читайте документацию.
Эти грабли не были поводом для расстройства, ведь решение задачи довольно простое — запускать единоразовый «будильник» и после срабатывания переустанавливать его. При реализации этого подхода мы наткнулись на следующие грабли — оказалось, что для разных уровней API необходимо по разному устанавливать будильники, при этом в документации ничего сказано не было. Но данная проблема решилась достаточно просто — методом «тыка» и «гугления». Ниже представлен пример кода, позволяющий правильно устанавливать «будильники»:
private static void setUpAlarm(final Context context, final Intent intent, final int timeInterval) < final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); final PendingIntent pi = PendingIntent.getBroadcast(context, timeInterval, intent, 0); am.cancel(pi); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) < final AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + timeInterval, pi); am.setAlarmClock(alarmClockInfo, pi); >else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInterval, pi); else am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeInterval, pi); >
Хочу обратить внимание на флаг AlarmManager.RTC_WAKEUP — именно с помощью него система позволит нашему приложению «проснуться» при неактивном экране, когда устройство находится в заблокированном состоянии.
Данный подход с «будильникам» дал нам нужный результат — приложение в фоне корректно опрашивает сервер на наличие новых данных. Сейчас мы дорабатываем алгоритм. На данный момент реализуем и тестируем следующую оптимизацию, которая позволит сузить круг устройств и тем самым уменьшить нагрузку на сервер:
- в сообщении, отправленном средствами GCM на устройство, содержится некоторый уникальный ID;
- получив данные GET запросом в фоновом режиме проверяем, существуют ли уже запись с таким ID на устройстве;
- если локально на устройстве таких данных нет, мы запоминаем этот ID и время его получения T1;
- ждем PUSH с таким же ID, при получении запоминаем время T2 и проверяем разницу между T2 и T1;
- если разница составляет больше некоторого временного критерия (значения), то на устройстве наблюдается проблема с доставкой уведомлений и для корректной работы сервиса необходимо постоянно запрашивать данные в фоновом режиме с сервера (критерий советую выбирать исходя из решаемой задачи. В нашем случае, был выбран критерий равный 5 минутам);
- данную разницу стоит вычислять несколько раз, например 5-10 раз, только после этого делать вывод о том, что устройство действительно содержит проблему с получением Push уведомлений (таким образом исключается ситуация банального разрыва соединения, таймаута и пр.);
- необходимо прогонять данный алгоритм периодически (например, раз в неделю, или после обновления ОС на устройстве).
P.S.
В процессе тестирования очень помог инструмент, который дает возможность посмотреть информацию по отправленным пушам. Этот инструмент доступен разработчикам бесплатно. Рекомендую всем его использовать.
P.S.S.
Предрекаю, в комментариях наверняка будут вопросы о расходе батарейки. Я провел несколько тестов, оставив личный телефон на ночь с включенным мобильным интернетом. Результаты были в районе 20-25% расхода заряда за 8-9 часов. Так же клиенты, которым мы отправляли тестовые сборки, не жаловались на проблемы с увеличением расхода заряда батареи.
- Разработка мобильных приложений
- Разработка под Android
Уведомления
Кроме Toast-уведомлений, существует также другой тип уведомлений, который выводится за пределами вашего приложения, а именно, в верхней части телефона в системной строке состояния в виде значка с небольшим текстом.
Далее пользователь должен сдвинуть строку состояния экрана, чтобы увидеть расширенную информацию об уведомлении — текст, картинку. Также можно прямо в уведомлении сделать какое-то действие — написать ответ, поставить на паузу музыку и т.п. Для привлечения внимания к уведомлению можно подключить звук и вибрацию.
Уведомление может висеть в строке состояние сколь угодно долго, пока сам пока пользователь не отреагирует на него, в отличие от Toast-сообщения, которое исчезнет через несколько секунд. В Android 5.0 добавилась возможность выводить уведомление в виде отдельного небольшого всплывающего окна (если устройство не заблокировано). В особых случаях уведомление можно выводить и на экран блокировки.
Пользователь может в настройках вашего приложения отключить уведомления в любой момент. Поэтому не стоит спамить пользователя ненужными сообщениями. Также нелишним будет проводить проверку, что уведомление будет выведено на экран.
Обратите внимание, что в имени класса спрятан кот (Notification), что намекает на целевое использование уведомлений. Уведомляйте пользователя только о самом важном, например, что пора кормить кота.

Когда пользователь открывает расширенное сообщение, Android запускает объект Intent, который определён в соответствии с уведомлением. Можно также конфигурировать уведомление с добавлением звука, вибрации и мигающих индикаторов на мобильном устройстве.
Этот вид уведомления удобен в том случае, когда приложение работает в фоновом режиме и должно уведомить пользователя о каком-либо важном событии. Фоновое приложение создаёт уведомление в строке состояния, но не запускает активность самостоятельно для получения пользовательского взаимодействия. Это должен сделать только сам пользователь в удобное ему время.
Чтобы создать уведомление в строке состояния, необходимо использовать два класса:
- Notification — определяем свойства уведомления строки состояния: значок, расширенное сообщение и дополнительные параметры настройки (звук и др.)
- NotificationManager — системный сервис Android, который управляет всеми уведомлениями. Экземпляр NotificationManager создаётся при помощи вызова метода from(), а затем, когда надо показать уведомление пользователю, вызывается метод notify()
В большинстве случаев вместо Notification мы будем использовать рекомендованный NotificationCompat из AndroidX.
dependencies
Подключать эту библиотеку нет надобности, если у вас уже используется любая библиотека из AndroidX. Скорее всего оно так и есть, в проекте по умолчанию используется androidx.appcompat:appcompat, но номера версий могут отличаться, поэтому можно добавить зависимость для страховки.
Показываем уведомление
Добавим на экран активности кнопку и напишем для демонстрации работы уведомления.
Для начала вам надо создать идентификатор уведомления. Он нужен, чтобы можно было различать уведомления друг от друга. Ведь вы можете создать идеальное приложение, которое уведомляло бы хозяина, что кота надо покормить (первое уведомление), погладить (второе уведомление), почистить лоток (третье уведомление). Если у вас будет один идентификатор, то каждое новое уведомление затрёт предыдущее и хозяин не увидит свои недоработки. Это не дело. Для идентификатора используйте какое-нибудь число. Только не надо оригинальничать, ничего не имею против числа 836, но вам определённо нужно сходить к психологу.
Также следует создать идентификатор канала. Каналы появились в API 26, но старые устройства будут просто игнорировать данный параметр при вызове конструктора NotificationCompat.Builder.
Далее формируется внешний вид и поведение уведомления через построитель NotificationCompat.Builder. Вы можете задать текст уведомления, значок, заголовок и прочие атрибуты. Для простого примера оставил минимальный набор настроек.
Выводится уведомление с помощью метода notify() — своеобразный аналог метода show() у Toast из предыдущего урока.
Не забывайте использовать вместо строк строковые ресурсы, пример лишь для знакомства.
Все примеры рассчитаны для устройств до Android 13, в котором появилось требование к разрешению. Чтобы студия не ругалась на отсутствующее разрешение, мы добавим аннотацию MissingPermission.
// Если этот код работает, его написал Александр Климов, // а если нет, то не знаю, кто его писал. package ru.alexanderklimov.notification import android.os.Bundle import android.widget.Button import androidx.appcompat.app.AppCompatActivity import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat class MainActivity : AppCompatActivity() < companion object < const val NOTIFICATION_ID = 101 const val CHANNEL_ID = "channelID" >@SuppressLint("MissingPermission") override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button: Button = findViewById(R.id.button) button.setOnClickListener < // Создаём уведомление val builder = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_action_cat_24dp) .setContentTitle("Напоминание") .setContentText("Пора покормить кота") .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_DEFAULT) val notificationManager = NotificationManagerCompat.from(this) notificationManager.notify(NOTIFICATION_ID, builder.build()) // или //with(NotificationManagerCompat.from(this)) < // notify(NOTIFICATION_ID, builder.build()) // посылаем уведомление //>> > >
package ru.alexanderklimov.notification; import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; public class MainActivity extends AppCompatActivity < // Идентификатор уведомления private static final int NOTIFY_ID = 101; // Идентификатор канала private static String CHANNEL_ID = "Cat channel"; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View v) < NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Напоминание") .setContentText("Пора покормить кота") .setPriority(NotificationCompat.PRIORITY_DEFAULT); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build()); >>); > >
Запустим пример и нажмём кнопку. В строке состояния появится значок. Раскроем уведомление и увидим текст. Уведомление можно смахнуть в сторону для удаления.


Затем можете снова нажать кнопку и создать новое уведомление. Покормили кота, снова удалили уведомление. А можете нажать несколько раз, но уведомление будет только одно. Так что, если кот научится нажимать кнопки, то не сможет создать бесконечную ленту уведомлений.
Реакция на уведомления
Нажатие на уведомление ни к чему не приведёт. Нужен дополнительный код.
Создадим новые объекты Intent и PendingIntent, которые описывают намерения и целевые действия. В нашем случае мы хотим запустить нашу активность, когда пользователь среагирует на уведомление. Присоединяем объекты через setContentIntent().
override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val intent = Intent(this, MainActivity::class.java) intent.apply < flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK >val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0) val button: Button = findViewById(R.id.button) button.setOnClickListener < val builder = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_24dp) .setContentTitle("Напоминание") .setContentText("Пора покормить кота") .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) with(NotificationManagerCompat.from(this)) < notify(notificationId, builder.build()) // посылаем уведомление >> >
button.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View v) < Intent notificationIntent = new Intent(MainActivity.this, MainActivity.class); PendingIntent contentIntent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Напоминание") .setContentText("Пора покормить кота") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(contentIntent); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build()); > >);
Теперь можно создать уведомление и затем закрыть приложение. Если нажать на уведомление, оно откроет заново ваше приложение.
Сделаем уведомление более красивым, добавив другие необязательные настройки.
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Напоминание") .setContentText("Пора покормить кота") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(contentIntent) // необязательные настройки .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.hungrycat)) // большая картинка .setTicker("Последнее китайское предупреждение!") // до Lollipop .setAutoCancel(true); // автоматически закрыть уведомление после нажатия
Теперь в уведомлении мы видим картинку. Метод setTicker() выводит сообщение в строке состояния на короткое время, а затем исчезает. Это работает только на старых устройствах и сейчас можно уже не использовать.

Как я уже упоминал, если вам нужно обновить уведомление, то просто ещё раз отправьте его устройству под этим же идентификатором, но с другим текстом и картинкой.
Если уведомления разного типа, то нужно обновлять идентификаторы. Вспомним урок по подсчёту ворон и изменим код.
// Kotlin // Объявим переменную в начале класса private var counter = 101 // Теперь у уведомлений будут новые идентификаторы notify(counter++, builder.build());
// Java // Объявим переменную в начале класса private int counter = 101; // Теперь у уведомлений будут новые идентификаторы notificationManager.notify(counter++, builder.build());
Теперь будут появляться новые уведомления. Обычно выводятся три значка для одного приложения (на новых устройствах), потом они группируются и на экране остаётся только один значок. Проверьте самостоятельно.
Совсем не обязательно запускать своё приложение, хотя это является распространённой практикой. Можете задать нужное поведение, например, запустить свой сайт по указанному адресу. Переделаем код:
// Kotlin val link = "http://developer.alexanderklimov.ru/android/" val webIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) val pendingIntent = PendingIntent.getActivity(this, 0, webIntent, 0) val button: Button = findViewById(R.id.button) button.setOnClickListener < val builder = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_action_cat) .setContentTitle("Посетите мой сайт") .setContentText(link) .setContentIntent(pendingIntent) .setDefaults(Notification.DEFAULT_SOUND) .setAutoCancel(true) with(NotificationManagerCompat.from(this)) < notify(NOTIFICATION_ID, builder.build()) >>
// Java // код для webIntent напишите самостоятельно NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setContentTitle("Посетите мой сайт") .setContentText("http://developer.alexanderklimov.ru/android/") .setContentIntent(pendingIntent) .setDefaults(Notification.DEFAULT_SOUND) .setAutoCancel(true) .setSmallIcon(R.mipmap.ic_launcher); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build());
Можно вывести индикатор прогресса, чтобы указать текущий ход выполнения задачи. Можно установить бесконечное выполнение:
.setProgress(100, 50, false)
Удаление собственных уведомлений
Вы можете из программы удалить своё уведомление, посланное по глупости (не вздумайте удалять уведомления про кормёжку кота!).
// Удаляем конкретное уведомление notificationManager.cancel(NOTIFY_ID); // Удаляем все свои уведомления notificationManager.cancelAll();
Если уведомления с указанным идентификатором не будет, то ничего страшного при удалении не произойдёт, поэтому проверку не нужно устраивать.
Использование настроек по умолчанию
Можно добавить вибрацию, звуковой сигнал или мерцание светодиодами для ваших уведомлений при помощи настроек по умолчанию. В свойстве defaults вы можете сочетать следующие константы:
- Notification.DEFAULT_LIGHTS
- Notification.DEFAULT_SOUND
- Notification.DEFAULT_VIBRATE
Чтобы к уведомлению добавить звук и вибрации по умолчанию, используйте код:
// Kotlin .setDefaults(Notification.DEFAULT_SOUND and Notification.DEFAULT_VIBRATE)
// Java notification.defaults = Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE;
Если хотите установить сразу все значения по умолчанию, задействуйте константу Notification.DEFAULT_ALL.
Звуковое сопровождение
Использование звуковых оповещений для уведомления пользователя о событиях, связанных с устройством (например, входящий звонок), стало привычным. Большинство стандартных событий, от входящих звонков до новых сообщений и низкого заряда батареи, объявляются с помощью звуковых мелодий. Android позволяет проигрывать любой звуковой файл на телефоне в качестве уведомления. Чтобы это сделать, нужно присвоить свойству sound путь URI:
notification.sound = ringURI;
Также можно использовать собственный звуковой файл, загруженный на устройстве или добавленный в проект в качестве ресурса.
Uri ringURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); notification.sound = ringURI;
notification.sound = Uri.parse("file:///sdcard/cat.mp3"); // если знаем точный путь!
Виброзвонок
Вы можете использовать функцию виброзвонка в телефоне, чтобы сопровождать ваше уведомление вибрацией для привлечения внимания пользователя.
Чтобы использовать виброзвонок, передайте в свойство vibrate объекта Notification массив значений типа long. Постройте массив, учитывая, что значения, отвечающие за продолжительность вибрации (в миллисекундах), чередуются со значениями, которые означают длину паузы между вибрациями.
Прежде чем использовать виброзвонок в своем приложении, необходимо получить нужные полномочия, прописав их в манифесте:
В следующем примере показано, как изменить уведомление, чтобы одна секунда вибрации сменялась одной секундой паузы на протяжении пяти секунд:
long[] vibrate = new long[] < 1000, 1000, 1000, 1000, 1000 >; notification.vibrate = vibrate;
В настоящее время эмулятор Android не умеет оповещать о вибрации ни визуально, ни с помощью звуковых сигналов.
Светодиодная индикация
Объект Notification включает в себя свойства для настройки цвета и частоты мерцания светодиодов устройства. Здесь стоит обратить внимание, что конкретные модели устройств могут не содержать светодиодные индикаторы или иметь другие цвета.
Свойство ledARGB может устанавливать цвет для светодиодной подсветки. Свойства ledOffMS и ledOnMS позволяют регулировать частоту и поведение светодиодов. Вы можете включить светодиоды, присвоив свойству ledOnMS значение 1, а ledOffMS – 0. Присвоив им обоим значения 0, светодиоды можно выключить.
Настроив работу со светодиодами, необходимо также добавить флаг FLAG_SHOW_LIGHTS к свойству flags объекта Notification.
В следующем фрагменте кода показано, как включить на устройстве красный светодиод:
notification.ledARGB = Color.RED; notification.ledOffMS = 0; notification.ledOnMS = 1; notification.flags = notification.flags | Notification.FLAG_SHOW_LIGHTS;
В настоящее время эмулятор Android не умеет визуально показывать активность светодиодов.
Текущие и настойчивые уведомления
Вы можете делать уведомления текущими и/или настойчивыми, устанавливая флаги FLAG_INSISTENT и FLAG_ONGOING_EVENT. Уведомления, помеченные как текущие, используются для представления событий, которые выполняются в данный момент времени (например, загрузка файла, фоновое проигрывание музыки). Текущие уведомления необходимы для сервисов, работающих на переднем плане. Пример установки флагов:
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
В расширенной статусной строке текущие события отделены от обычных, чтобы вы сразу могли их отличить.
Настойчивые уведомления непрерывно повторяют звуковые сигналы, вибрируют и мерцают светодиодами, пока не будут остановлены. Подобные уведомления, как правило, используются для событий, которые требуют немедленного и своевременного внимания, таких как входящий звонок, срабатывание будильника или время кормёжки кота. В следующем фрагменте кода показано, как сделать уведомление настойчивым:
notification.flags = notification.flags | Notification.FLAG_INSISTENT;
В методе getActivity() может понадобиться изменить флаг, например.
PendingIntent pendingIntent = PendingIntent.getActivity( context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
Существуют и другие флаги. Хотя в большинстве случаев используется просто 0.
В Android 5.0 пользователь может установить собственный уровень оповещений, нажав на кнопки увеличения громкости на домашнем экране. Появится диалоговое окно, в котором задаётся один из трёх доступных уровней.

Запустить запущенную активность
Не сразу бывает заметно, но на самом деле, когда при нажатии на уведомлении у вас запускается активность, то запускается не старая активность, которая была на экране до этого, а новая. Это можно увидеть в примере, если, например, есть текстовое поле с текстом. Введите какой-нибудь текст в активности, а потом создайте уведомление, вызывающее активность. Вы увидите, что запустится новая активность с пустыми текстовым полем, хотя мы ожидали увидеть запущенную активность. Если вам нужен именно этот вариант, то используйте флаги для намерения.
Intent intent = new Intent(context, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Либо вы можете прописать в манифесте для нужной активности атрибут android:launchMode=»singleTop».
Меняем цвет значка
По умолчанию, значок выводится в сером круге. Вы можете изменить цвет круга, вызвав новый метод setColor(), который появился в API 21:
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) . .setColor(Color.GREEN) .build();

Анимированный значок для уведомления
Покажу один фокус. Возьмём код из примера и заменим одну строчку, которая отвечает за вывод маленького значка — .setSmallIcon(android.R.drawable.stat_sys_upload):
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(android.R.drawable.stat_sys_upload) . // другой код .setAutoCancel(true); // автоматически закрыть уведомление после нажатия
Запускаем код и создаём уведомление. Вы увидите, что в строке состояния выводится анимированный значок стрелки. Такой способ стоит использовать для действительно важных сообщений, чтобы понапрасну не раздражать пользователя.

Возможно, если вы опустите метод setTicker(), то значок уже не будет анимированным, где-то работало, где-то нет. Проверяйте самостоятельно.
Вы можете попробовать поискать другие системные анимации, например, android.R.drawable.stat_sys_download или создать собственную анимацию.
На странице http://forum.xda-developers.com/showthread.php?t=1088677 энтузиасты выложили несколько готовых примеров анимации, которые можно скачать.
Расширенные возможности уведомлений
В Android 4.1 Jelly Bean появились дополнительные возможности для уведомлений через настройку стилей.
Добавьте на экран четыре кнопки.
Уведомление с тремя кнопками
Начнём с первого варианта. Теперь в уведомлениях можно размещать до трёх кнопок. Это может быть удобным, если приложение состоит из нескольких активностей или нужно предложить три разных варианта развития сценария. За появление кнопок в уведомлении отвечает метод setAction().
Intent notificationIntent = new Intent(MainActivity.this, SecondActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Посылка") .setContentText("Это я, почтальон Печкин. Принес для вас посылку") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.hungrycat)) // большая картинка .addAction(R.drawable.ic_lock_open_black_24dp, "Открыть", pendingIntent) .addAction(R.drawable.ic_refresh_white_24dp, "Отказаться", pendingIntent) .addAction(R.drawable.ic_pets_black_24dp, "Другой вариант", pendingIntent) .setAutoCancel(true); // автоматически закрыть уведомление после нажатия NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build());

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

Уведомление с длинным текстом. BigTextStyle().bigText()
Если вы внимательно смотрели на уведомление, то могли увидеть, что длинный текст, помещённый в метод setContentText(), вывелся на экран не полностью. Если информация слишком важная и вам хочется её показать в уведомлении полностью, то подойдёт вариант со стилем BigTextStyle:
// Kotlin .setStyle(NotificationCompat.BigTextStyle() .bigText("Когда кормить будут? Далее идёт очень длинный текст про бедного котика, которого морят голодом уже целых три минуты"))
// Java Intent notificationIntent = new Intent(MainActivity.this, SecondActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); String bigText = "Это я, почтальон Печкин. Принёс для вас посылку. " + "Только я вам её не отдам. Потому что у вас документов нету. "; NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Посылка") .setContentText("Это я, почтальон Печкин. Принес для вас посылку") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.hungrycat)) // большая картинка .addAction(R.drawable.ic_pets_black_24dp, "Запустить активность", pendingIntent) .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText)) .setAutoCancel(true); // автоматически закрыть уведомление после нажатия NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build());

Уведомление с большой картинкой: BigPictureStyle().bigPicture()
Пример с большой картинкой аналогичен с предыдущим примером. Только мы задаём уже другой стиль для уведомления. Вместо стиля длинного текста используется стиль BigPictureStyle().bigPicture():
// Kotlin .setStyle( NotificationCompat.BigPictureStyle() .bigPicture(BitmapFactory.decodeResource(resources,R.drawable.hungrycat)) .bigLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.table_cat)) .setBigContentTitle("Beautiful Cat") .setSummaryText("Голодный кот") )
// Java Intent notificationIntent = new Intent(MainActivity.this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentTitle("Посылка") .setContentText("Это я, почтальон Печкин. Принёс для вас посылку") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.hungrycat)) // большая картинка .addAction(R.drawable.ic_pets_black_24dp, "Запустить активность", pendingIntent) // большая картинка из ресурсов .setStyle(new NotificationCompat.BigPictureStyle() .bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.hungrycat))) .setAutoCancel(true); // автоматически закрыть уведомление после нажатия NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build());

Слишком большая картинка будет обрезана.
Уведомление в стиле InboxStyle
Есть ещё один стиль InboxStyle, напоминающий стиль писем в папке Входящие. Стиль разместит до пяти ваших строк в виде списка. Весь код приводить не буду, меняется только вызов setStyle()
// Kotlin .setStyle( NotificationCompat.InboxStyle() .addLine("This is first line") .addLine("This is second line") .addLine("This is third line") .addLine("This is fourth line") .addLine("This is fifth line") .setBigContentTitle("This is Content Title.") .setSummaryText("This is summary text.") )
// Java . .setStyle(new NotificationCompat.InboxStyle() .addLine("Первое сообщение").addLine("Второе сообщение") .addLine("Третье сообщение").addLine("Четвертое сообщение") .setSummaryText("+2 more"))

Уведомление в стиле мессенджера: MessagingStyle
Стиль MessagingStyle пригодится для отображения сообщений из мессенджера или чата. Появился в Android Nougat.
// Kotlin button.setOnClickListener < val sender = Person.Builder() .setName("Мурзик") //.setIcon(. ) // можно добавить значок .build() val messagingStyle = NotificationCompat.MessagingStyle(sender) .addMessage("Хозяин, когда кормить будут?", Date().time, sender) val builder = NotificationCompat.Builder(this, "Cat channel") .setSmallIcon(R.drawable.ic_pets_black_24dp) .setStyle(messagingStyle) val channel = NotificationChannel("Cat channel", "channel", NotificationManager .IMPORTANCE_DEFAULT).apply < description = "Feed cat" >with(NotificationManagerCompat.from(this)) < createNotificationChannel(channel) notify(notificationId, builder.build()) >>
// Java Intent notificationIntent = new Intent(MainActivity.this, SecondActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); Person murzik = new Person.Builder().setName("Мурзик").build(); Person vaska = new Person.Builder().setName("Васька").build(); NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle (murzik) .setConversationTitle("Android chat") .addMessage("Привет котаны!", System.currentTimeMillis(), murzik) .addMessage("А вы знали, что chat по-французски кошка?", System .currentTimeMillis(), murzik) .addMessage("Круто!", System.currentTimeMillis(), vaska) .addMessage("Ми-ми-ми", System.currentTimeMillis(), vaska) .addMessage("Мурзик, откуда ты знаешь французский?", System.currentTimeMillis(), vaska) .addMessage("Шерше ля фам, т.е. ищите кошечку!", System.currentTimeMillis(), murzik); NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_pets_black_24dp) .setContentIntent(pendingIntent) .addAction(R.drawable.ic_pets_black_24dp, "Запустить активность", pendingIntent) .setStyle(messagingStyle) .setAutoCancel(true); // автоматически закрыть уведомление после нажатия NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this); notificationManager.notify(NOTIFY_ID, builder.build());
В конструкторе MessagingStyle вы должны указать имя текущего пользователя, который будет видеть свои сообщения.
У класса Person есть другие полезные методы: setIcon() (значок), setData() (картинки) и др.
В setConversationTitle() указываем название беседы, удобно при разговоре двух и более котов. В поздних версиях не имеет эффекта, можно убрать.
Разговор строится через цепочку вызовов методов addMessage(), в которых указывается текст сообщения, время, отправитель. Количество сообщений может быть любым. При большом количестве (задано в MessagingStyle.MAXIMUM_RETAINED_MESSAGES) старые сообщения начнут удаляться автоматически.

Подводя итоги, следует отметить, у уведомлений очень много методов, которые можно использовать в своём приложении. Вот как может выглядеть полный набор:
new Notification.Builder(this.getApplicationContext()) .setAutoCancel(boolean autoCancel) .setContent(RemoteViews views) .setContentInfo(CharSequence info) .setContentIntent(PendingIntent intent) .setContentText(CharSequence text) .setContentTitle(CharSequence title) .setDefaults(int defaults) .setDeleteIntent(PendingIntent intent)) .setFullScreenIntent(PendingIntent intent, boolean highPriority) .setLargeIcon(Bitmap icon) .setLights(int argb, int onMs, int offMs) .setNumber(int number) .setOngoing(boolean ongoing) .setOnlyAlertOnce(boolean onlyAlertOnce) .setPriority(int pri) .setProgress(int max, int progress, boolean indeterminate) .setShowWhen(boolean show) .setSmallIcon(int icon, int level) .setSmallIcon(int icon) .setSound(Uri sound) .setSound(Uri sound, int streamType) .setStyle(Notification.Style style) .setSubText(CharSequence text) .setTicker(CharSequence tickerText, RemoteViews views) .setTicker(CharSequence tickerText) .setUsesChronometer(boolean b) .setVibrate(long[] pattern) .setWhen(long when) .addAction(int icon, CharSequence title, PendingIntent intent) .build()
- setSmallIcon() устанавливает маленький значок, который выводится в строке состояния, а также в правой части открытого уведомления.
- setLargeIcon() устанавливает большой значок, который выводится в открытом уведомлении слева.
- setWhen() определяет время для уведомления, по умолчанию время создания уведомления
- setTicker() выводит временную строку в строке состояния, которая затем исчезает. Остаётся только маленький значок (см. выше)
- setNumber() добавляет число справа от уведомления (не везде работает)
- setShowWhen() — показывать ли время в уведомлении (в Android 7.0 по умолчанию не показывается)
- setUsesChronometer() выводит счётчик вместо времени, показывающий сколько прошло от времени when. Полезно для уведомления секундомера или звонка
- setContentInfo() добавляет текст справа от уведомления (в новых версиях сверху)
- setColor() закрашивает значок и название приложения указанным цветом
- setOngoing() выводит уведомление поверх обычных уведомлений, такое уведомление нельзя закрыть или смахнуть.
- setVibrate() — виброзвонок
- setSound() — звук
- setLights() — цвет LED-индикатора
- setPriority() устанавливает приоритет от -2 (NotificationCompat.PRIORITY_MIN) до 2 (NotificationCompat.PRIORITY_MAX)
- setTimeoutAfter() (появилось в API 26) — устанавливает таймаут, после которого уведомление удалится
- setProgress() — индикатор прогресса
Приоритет
Не все уведомления одинаковы важны. Например, напоминание о том, что пора кормить кота — это сверхважное сообщение (не обсуждается). Угроза землетрясения, цунами, урагана — тоже очень важные сообщения. Новые версии программы, новое письмо и т.д. — не слишком важные уведомления, которые можно почитать после того, как покормили кота.
В API 16 появился новый метод setPriority() с константами по мере увеличения: NotificationCompat.PRIORITY_MIN, NotificationCompat.PRIORITY_LOW, NotificationCompat.PRIORITY_DEFAULT, NotificationCompat.PRIORITY_HIGH, NotificationCompat.PRIORITY_MAX.
. .setPriority(NotificationCompat.PRIORITY_HIGH) . .build();
Чем выше приоритет уведомления, тем выше он находится среди остальных уведомлений. Таким образом, важные сообщения всегда будут наверху, даже если поступили позже других менее важных сообщений. Не злоупотребляйте этой возможностью и трезво оцените важность вашего уведомления.
В Android 5.0 произошли небольшие изменения в поведении. Если установлены максимальные приоритеты Notification.PRIORITY_HIGH или Notification.MAX, то при вызове сначала уведомление появится в виде плавающего окна в верхней части экрана, а только потом закроется и останется в виде стандартного уведомления в строке состояния.

В Android 8.0 вместо приоритетов стали использовать важность — IMPORTANCE_XXX.
Напоследок дам совет — читайте документацию. Google постоянно вносит какие-то изменения и добавления. Практически в каждой новой версии Android что-то менялось. Я не в состоянии отслеживать новинки и оперативно добавлять в статью.
Пример изменений, которые произошли в API 23:
- Удалили метод setLatestEventInfo()
- Добавили новые методы getLargeIcon() и getSmallIcon()
- Добавили новое поле класса CATEGORY_REMINDER и объявили устаревшими поля icon и largeIcon.
В уведомлениях можно использовать собственный макет, используя RemoteViews. Для стилизации макета изучите классы DecoratedCustomViewStyle и DecoratedMediaCustomViewStyle. Подключается через метод setCustomContentView().
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_custom_view); remoteViews.setImageViewResource(R.id.image_icon, iconResource); remoteViews.setTextViewText(R.id.text_title, title); remoteViews.setTextViewText(R.id.text_message, message); remoteViews.setImageViewResource(R.id.image_end, imageResource); Notification.Builder builder = new Notification.Builder(context) .setSmallIcon(R.drawable.ic_phonelink_ring_primary_24dp) .setCustomContentView(remoteViews) .setStyle(new Notification.DecoratedCustomViewStyle()); .setAutoCancel(true);
В уведомлениях появилась возможность вводить собственный текст для ответа на какое-то сообщение. Для этого используется механизм Direct Reply, который использует RemoteInput API.
NotificationListenerService. Прослушка уведомлений
В API 18 (Android 4.3) появился новый класс NotificationListenerService, позволяющий следить за уведомлениями. С тех пор я не следил за этой темой. Материал был написан по горячим следам в 2015 году. Если не работает, то разбирайтесь самостоятельно.
Новый класс является службой, которая получает сигналы от системы, когда появляются или удаляются уведомления. Таким образом вы можете отслеживать не только свои уведомления (они и так вам известны), но и уведомления от других приложений. Это может быть полезным для каких-то расширений к приложениям.
Вам нужно наследоваться от данного класса, зарегистрировать его в манифесте с разрешением BIND_NOTIFICATION_LISTENER_SERVICE и включить в него специальный фильтр намерения.
У службы есть два метода onNotificationPosted() и onNotificationRemoved() с параметром StatusBarNotification, который содержит полезные методы об уведомлении.
Пользователь должен явно разрешить приложению следить за уведомлениями через Настройки | Безопасность. Если на устройстве нет приложений, которые следят за уведомлениями, то в настройках вы не увидите никаких пунктов о разрешении. Когда вы создадите такое приложение, то там появится новый пункт Доступ к уведомлениям.

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


После этого в настройках будет указано число приложений, имеющих соответствующее разрешение.

Перейдём к практической части. Подготовим разметку из нескольких кнопок и текстовой метки для вывода информации.
Создадим новую службу.
package ru.alexanderklimov.testapplication; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; public class NLService extends NotificationListenerService < private String TAG = this.getClass().getSimpleName(); private NLServiceReceiver mReceiver; @Override public void onCreate() < super.onCreate(); mReceiver = new NLServiceReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("ru.alexanderklimov.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); registerReceiver(mReceiver, filter); >@Override public void onDestroy() < super.onDestroy(); unregisterReceiver(mReceiver); >@Override public void onNotificationPosted(StatusBarNotification sbn) < Log.i(TAG, "onNotificationPosted"); Log.i(TAG, "ID :" + sbn.getId() + "\\t" + sbn.getNotification().tickerText + "\\t" + sbn.getPackageName()); Intent intent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); intent.putExtra("notification_event", "onNotificationPosted:\\n" + sbn.getPackageName() + "\\n"); sendBroadcast(intent); >@Override public void onNotificationRemoved(StatusBarNotification sbn) < Log.i(TAG, "onNOtificationRemoved"); Log.i(TAG, "ID :" + sbn.getId() + "\\t" + sbn.getNotification().tickerText + "\\t" + sbn.getPackageName()); Intent intent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); intent.putExtra("notification_event", "onNotificationRemoved:\\n" + sbn.getPackageName() + "\\n"); sendBroadcast(intent); >class NLServiceReceiver extends BroadcastReceiver < @Override public void onReceive(Context context, Intent intent) < if (intent.getStringExtra("command").equals("clearall")) < NLService.this.cancelAllNotifications(); >else if (intent.getStringExtra("command").equals("list")) < Intent notificationIntent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); notificationIntent.putExtra("notification_event", "======="); sendBroadcast(notificationIntent); int i = 1; for (StatusBarNotification sbn : NLService.this.getActiveNotifications()) < Intent infoIntent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); infoIntent.putExtra("notification_event", i + " " + sbn.getPackageName() + "\\n"); sendBroadcast(infoIntent); i++; >Intent listIntent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); listIntent.putExtra("notification_event", "Notification List"); sendBroadcast(listIntent); > > > >
В манифесте добавляем новый блок.
package ru.alexanderklimov.testapplication; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.NotificationCompat; import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.TextView; public class MainActivity extends ActionBarActivity < private TextView mInfoTextView; private NotificationBroadcastReceiver mReceiver; public void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle("NotificationListenerService Demo"); mInfoTextView = (TextView) findViewById(R.id.textView); mReceiver = new NotificationBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("ru.alexanderklimov.NOTIFICATION_LISTENER_EXAMPLE"); registerReceiver(mReceiver, filter); >@Override protected void onDestroy() < super.onDestroy(); unregisterReceiver(mReceiver); >public void onButtonClicked(View view) < if(view.getId() == R.id.buttonCreateNotification)< NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setContentTitle("Важное уведомление"); builder.setContentText("Пора кормить кота!"); builder.setTicker("Хозяин, проснись!"); builder.setSmallIcon(R.drawable.ic_launcher); builder.setAutoCancel(true); manager.notify((int) System.currentTimeMillis(), builder.build()); >else if(view.getId() == R.id.buttonClearNotification) < Intent intent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); intent.putExtra("command", "clearall"); sendBroadcast(intent); >else if(view.getId() == R.id.buttonListNotification) < Intent intent = new Intent("ru.alexanderklimov.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); intent.putExtra("command", "list"); sendBroadcast(intent); >> class NotificationBroadcastReceiver extends BroadcastReceiver < @Override public void onReceive(Context context, Intent intent) < String temp = intent.getStringExtra("notification_event") + "\\n" + mInfoTextView.getText(); mInfoTextView.setText(temp); >> >
Первая кнопка запускает уведомление, чтобы увидеть, что приложение работает. Если вы хотите увидеть, как приложение следит за другими уведомлениями, то запустите Play Market и скачайте какую-нибудь игру или программу. Во время скачивания и установки генерируются уведомления. На следующем скриншоте видны уведомления от приложения Загрузки во время скачивания (com.android.providers.downloads) и от процесса установки (com.android.vending).

Если вы помните, в предупреждающем сообщении говорилось о возможности удалять уведомления. Третья кнопка позволяет это сделать. Вот почему эта настройка относится к разделу безопасности — ваша программа может удалять поступающие уведомления без ведома владельца устройства.
Вы можете программно запустить раздел с разрешением на использование службы.
// API 22 Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); if (intent.resolveActivity(getPackageManager()) != null)
В API 19 появился более широкий доступ к элементам уведомления. Поэтому через метод sbn.getNotification() вы можете получить объект класса Notification и вытащить из него картинки (Large Icon), текст и т.д.
Notification mNotification=sbn.getNotification(); Bundle extras = mNotification.extras; String notificationTitle = extras.getString(Notification.EXTRA_TITLE); int notificationIcon = extras.getInt(Notification.EXTRA_SMALL_ICON); Bitmap notificationLargeIcon = ((Bitmap) extras.getParcelable(Notification.EXTRA_LARGE_ICON)); CharSequence notificationText = extras.getCharSequence(Notification.EXTRA_TEXT); CharSequence notificationSubText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
Что такое сервис push‑уведомлений?

Сервис push-уведомлений – это технология, которую приложение использует для отправки сообщений пользователям. Вы, вероятно, видели всплывающие уведомления приложений или оповещения на вашем мобильном устройстве. Это коммуникационные сигналы, поступающие с сервера приложений. Сервер, а не пользователь инициирует обмен push-уведомлениями. Приложения используют push-уведомления, чтобы информировать пользователей о новых предложениях, событиях приложений или другой потенциально интересной информации. Хотя мобильные push-уведомления являются наиболее распространенным типом, существуют также push-уведомления в браузере, портативных устройствах, а также голосовые push-уведомления.
В чем преимущества использования сервиса push-уведомлений?
За последние несколько лет сервисы push-уведомлений стали неотъемлемой частью мобильных приложений. В 2009 году компания Apple выпустила первую версию сервиса уведомлений Apple Push Notification (APNs) для iOS, который давал издателям приложений возможность отправлять сообщения пользователям, не требуя от них открытия приложения. Google запустила сервис push-уведомлений для Android в 2012 году. Ваши пользователи могут рассчитывать на push-уведомления как на функцию, которая может помочь и вам, и вашим пользователям.
Ниже описаны несколько преимуществ использования сервиса push-уведомлений.
Усиление взаимодействия
Push-уведомления – эффективный способ привлечь пользователей. Вы можете использовать push-уведомления, чтобы отправлять пользователям актуальную информацию, напоминать им об использовании вашего приложения и предлагать воспользоваться функциями и рекламными акциями. Многочисленные исследования показывают, что push-уведомления открываются чаще, чем электронные письма, SMS-сообщения и приложения чата.
Повышение уровня удержания пользователей
Вы можете использовать push-уведомления для создания более целенаправленных и актуальных сообщений для ваших пользователей. Например, вы можете нацеливаться на пользователей на основе их поведения в прошлом, местоположения, интересов и других факторов, чтобы повысить уровень удержания клиентов и увеличить доход.
Улучшение контроля для пользователей
Ваши клиенты могут предпочесть push-уведомления другим способам коммуникации, потому что ими легко управлять как пользователю. Ваши пользователи могут подписаться на получение push-уведомлений и отказаться от них, когда захотят.
Как можно использовать сервис push-уведомлений?
Вот несколько наиболее распространенных способов использования push-уведомлений в организациях.
Маркетинговые кампании
Push-уведомления часто используются для маркетинговых кампаний. Сведения об акциях, скидках, выпуске новых продуктов и других важных обновлениях отправляются непосредственно на устройства ваших пользователей.
Push-уведомления – это эффективный способ продвигать кампании и побуждать пользователей к действиям. Например, вы можете предложить им загрузить новое приложение, подписаться на сервис или приобрести понравившиеся продукты.
Успешные кампании push-уведомлений также помогают вернуть прежних клиентов.
Уведомления о транзакциях
Push-уведомления могут предупреждать пользователей о таких транзакциях, как финансовая активность на их счетах. Например, банки используют push-уведомления для оповещения клиентов о депозитах, снятии средств, обновлениях баланса счетов и других изменениях на счетах.
Push-уведомления помогают клиентам следить за своими финансами и быть в курсе любых подозрительных действий. Вы также можете использовать push-уведомления для отправки клиентам напоминаний об оплате и других уведомлений о предстоящих транзакциях.
Одноразовые пароли
Push-уведомления могут содержать одноразовые пароли (OTP), позволяющие проверять аккаунты пользователей. Например, когда пользователь пытается войти в систему или совершить покупку, вы можете отправить одноразовый пароль на мобильное устройство пользователя. Затем клиент введет одноразовый пароль, чтобы выполнить действие. Так вы сможете подтвердить личность пользователя, не сохраняя конфиденциальную информацию. В более общем плане OTP – это один из аспектов многофакторной аутентификации (MFA).
Экстренные оповещения
Государственные организации могут использовать push-уведомления для оповещения общественности о чрезвычайных ситуациях. Это могут быть оповещения о стихийных бедствиях, а также о других чрезвычайных ситуациях, таких как предупреждения о похищении детей (AMBER Alert), террористические угрозы и предупреждения об экстремальных погодных условиях.
Медицинские сервисы
Push-уведомления могут отправлять пользователям медицинскую информацию, такую как новости о назначении врача, напоминания о приеме лекарств и результаты анализов. Кроме того, push-уведомления могут напоминать пациентам о необходимости принимать лекарства или записываться на повторные визиты.
Обновления по доставке
Как розничный продавец или поставщик вы можете отправлять push-уведомления, чтобы держать своих клиентов в курсе статуса их заказов. Вы можете уведомлять своих клиентов о любых изменениях в статусе доставки заказа, например, когда заказ отгружен, когда он готов к доставке и когда он будет доставлен. Push-уведомления также могут содержать номера для отслеживания и приблизительные сроки доставки.
Отзывы клиентов
Push-уведомления могут побудить ваших клиентов оставить отзыв о продукте или сервисе. Вы можете попросить клиентов оценить качество обслуживания или оставить комментарии о продуктах или сервисах. Кроме того, вы можете использовать push-уведомления для проведения опросов и получения информации о предпочтениях и мнениях ваших клиентов.
Социальные сети
Если вы запустите приложение для социальных сетей, то можете отправлять своим пользователям push-уведомления об обновлениях, таких как новые публикации и комментарии. Когда вы отправляете пользователям уведомления о появлении нового контента, они могут быстро получить доступ к последним новостям и ознакомиться с ними, не проверяя обновления вручную.
Какие существуют типы push-уведомлений?
Существует два основных типа push-уведомлений: «от приложения пользователю» (A2P) и индивидуальные сообщения (P2P).
A2P
Push-уведомления A2P отправляются из приложения или сервиса отдельному пользователю. В качестве примеров можно привести новости и оповещения, рекламные сообщения и сообщения о транзакциях.
P2P
P2P-уведомления отправляются напрямую от одного пользователя другому, обычно для уведомления получателя о событии или действии. Сюда входят сообщения в чате, запросы на добавление в друзья, напоминания и обновления местоположения.
Как работают push-уведомления?
Чтобы push-уведомление появилось, издатель должен его отправить, пользователь – получить, а сервис – соединить обе стороны. Отношения между этими тремя сторонами выглядят следующим образом:
- Издатель использует сервер приложений для отправки уведомлений своим пользователям.
- Пользователь соглашается получать уведомления, подписавшись на них со своего устройства.
- Сервис push-уведомлений для конкретной платформы связывает издателя с его пользователями.
Если вы разрабатываете мобильное приложение, обратите внимание на основные действия, которые необходимо предпринять, если вы хотите начать отправлять push-уведомления.
Зарегистрируйте приложение с помощью сервиса push-уведомлений
Вам следует подключиться к сервису push-уведомлений отдельных платформ, на которые вы хотите настроить нацеливание. Например, вам следует спроектировать приложение так, чтобы оно подключалось к сервису Apple Push Notification (APNs) для пользователей iOS и Firebase Cloud Messaging (FCM) для пользователей Android. Вы можете зарегистрировать свое приложение в сервису push-уведомлений, чтобы получить доступ к API сервиса.
Обработайте сервисные токены
Сервисы push-уведомлений поддерживают связь с каждым приложением и связанным мобильным устройством, зарегистрированным в их сервисах. Когда приложение и мобильное устройство регистрируются, сервис push-уведомлений возвращает токен устройства. Как разработчик вы можете обрабатывать и использовать токены для отправки уведомлений своим пользователям.
Начните отправлять уведомления
Сервер приложений отправляет содержимое уведомления и токены устройства в API сервиса push-уведомлений. Как только сервер отправит запрос в сервис push-уведомлений, сервис отправляет уведомление на устройства ваших пользователей.
Уведомление отображается на экранах блокировки устройств, в центрах уведомлений или значках приложений в зависимости от настроек пользователей. Затем пользователи могут нажать на уведомление, чтобы перейти в ваше приложение или на веб-сайт.
Как AWS может удовлетворить ваши требования к push-уведомлениям?
В Amazon Web Services (AWS) мы предлагаем Простой сервис уведомлений Amazon (Amazon SNS) для удовлетворения ваших требований к push-уведомлениям.
Amazon SNS – это полностью управляемый сервис отправки сообщений по модели «издатель – подписчик» (pub/sub) для обмена сообщениями между приложениями (A2A), а также между приложениями и пользователями (A2P). Он выступает в роли посредника между вашим приложением и различными сервисами push-уведомлений на мобильных платформах, что позволяет быстрее масштабироваться.
Для обмена сообщениями A2A Amazon SNS обеспечивает обмен push-уведомлениями между распределенными системами, микросервисами и бессерверными приложениями на основе событий по модели «многие-ко-многим». Для обмена сообщениями A2P Amazon SNS дает возможность общаться с клиентами с помощью SMS-сообщений, push-уведомлений и электронных писем.
Ниже перечислены другие преимущества использования Amazon SNS.
- Интеграция приложений с передачей сообщений по принципу «первым получено – первым отправлено»
- Безопасное шифрование оповещений при доставке
- Сбор и рассылка данных о событиях для более 60 сервисов AWS
- Отправка push-уведомлений и SMS-сообщений клиентам по всему миру
Начните работу с push-уведомлениями на AWS, создав бесплатный аккаунт AWS уже сегодня.