Русский
Русский
English
Статистика
Реклама

Push-уведомления

Браузерные Push-уведомления на Javascript и PHP

10.06.2021 02:22:28 | Автор: admin

Предисловие

В попытке найти годную статью по настройке уведомлений в браузере, я получал только статьи где в основном описывалось использование совместно с Firebase, но мне такой вариант не особенно то и подходил.

В данной статье не будут "размусолены" принципы работы и тонкости Push уведомлений, только код, только хардкор.

Важные замечания

Push-уведомления работают только с HTTPS.
К слову, в добавок с HTTPS должен присутствовать валидный SSL сертификат, подойдет и Let's Encrypt

Для разработки подойдёт localhost. Проблем возникнуть не должно, но если все же возникли данная статья поможет разобраться с ними.

Да будет код

Авторизация (VAPID)

Для начала стоит установить библиотеку WebPush в ваш php проект:

$ composer require minishlink/web-push

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

Чтобы сгенерировать несжатый публичный и приватный ключ, закодированный в Base64, введите следующее в свой Linux bash:

$ openssl ecparam -genkey -name prime256v1 -out private_key.pem$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> private_key.txt

Так же автор библиотеки предоставляет генерацию vapid ключей с помощью встроенного метода:

$vapidKeysInBase64 = VAPID::createVapidKeys();

Подписка

Этап 1 (JS)

В начале стоит проверить наличие поддержки ServiceWorker, PushManager, а так же showNotification в браузере:

app.js

function checkNotificationSupported() {return new Promise((fulfilled, reject) => {  if (!('serviceWorker' in navigator)) {      reject(new Error('Service workers are not supported by this browser'));      return;    }    if (!('PushManager' in window)) {      reject(new Error('Push notifications are not supported by this browser'));      return;    }    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {      reject(new Error('Notifications are not supported by this browser'));    return;    }        fulfilled();  })}

Создаем файл sw.js и далее регистрируем его:

app.js

navigator.serviceWorker.register('sw.js').then(() => {      console.log('[SW] Service worker has been registered');    }, e => {      console.error('[SW] Service worker registration failed', e);    }  );

Так же нам понадобится функция для проверки состояния подписки:

app.js

function checkNotificationPermission() {    return new Promise((fulfilled, reject) => {        if (Notification.permission === 'denied') {            return reject(new Error('Push messages are blocked.'));        }        if (Notification.permission === 'granted') {            return fulfilled();        }        if (Notification.permission === 'default') {            return Notification.requestPermission().then(result => {                if (result !== 'granted') {                    reject(new Error('Bad permission result'));                } else {                    fulfilled();                }            });        }        return reject(new Error('Unknown permission'));    });}

С сервера нам нужно получить публичный ssh ключ сгенерированный выше:

<script>window.applicationServerKey = '<?= $yourPublicKeyFromServer ?>'</script>

Далее на ваше усмотрение, вешаем вызов окна на разрешение получение уведомлений. В моем примере человек через 10 секунд получает предложение подписаться.

app.js

document.addEventListener('DOMContentLoaded', documentLoadHandler);function documentLoadHandler() {    checkNotificationSupported()        .then(() => {          setTimeout(() => {            serviceWorkerRegistration.pushManager.subscribe({                    userVisibleOnly: true,                    applicationServerKey: urlBase64ToUint8Array(window.applicationServerKey),                })                .then(successSubscriptionHandler, errorSubscriptionHandler)          }, 10000);         },         console.error      );}function urlBase64ToUint8Array(base64String) {    const padding = '='.repeat((4 - (base64String.length % 4)) % 4);    const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');    const rawData = window.atob(base64);    const outputArray = new Uint8Array(rawData.length);    for (let i = 0; i < rawData.length; ++i) {        outputArray[i] = rawData.charCodeAt(i);    }    return outputArray;}function errorSubscriptionHandler(err) {    if (Notification.permission === 'denied') {        console.warn('Notifications are denied by the user.');    } else {        console.error('Impossible to subscribe to push notifications', err);    }}

Далее если процесс получения разрешения подписки прошел успешно вызываем функцию successSubscriptionHandler

Формируем данные пользователя для дальнейшей отправки уведомлений.

app.js

function successSubscriptionHandler(subscriptionData) {    const key = subscription.getKey('p256dh');    const token = subscription.getKey('auth');    const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];    const body = new FormData();    body.set('endpoint', subscription.endpoint);    body.set('publicKey', key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null);    body.set('authToken', token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null);    body.set('contentEncoding', contentEncoding);    return fetch('src/push_subscription.php', {      method,      body,    }).then(() => subscription);  }

Так же нам нужно сформировать отправляемое уведомление

Вы можете манипулировать данными уведомления при помощи Post Message API

self.addEventListener('push', function (event) {    if (!(self.Notification && self.Notification.permission === 'granted')) {        return;    }    const sendNotification = body => {        const title = "Заголовок уведомления";        return self.registration.showNotification(title, {            body,        });    };    if (event.data) {        const message = event.data.text();        event.waitUntil(sendNotification(message));    }});

Этап 2 (PHP)

Необходим php версии 7+

Далее в файле subscribeUserToPushNotifications на который мы сделали запрос с фронта при получении разрешения на подписку, мы формируем данные пользователя

subscribeUserToPushNotifications.php

<?php $subscription = $_POST;if (!isset($subscription['endpoint'])) {    echo 'Error: not a subscription';    return;}// save subscription from => $subscription 

На данном этапе мы можем записать данные пользователя в Базу данных (Ну или что у вас там), для последующей отправки уведомлений.

Непосредственно сама отправка происходит следующим образом

Достаем юзера с места его сохранения, и далее создаем объект подписчика:

pushNotificationToClient.php

<?php use Minishlink\WebPush\WebPush;use Minishlink\WebPush\Subscription;$subscription = Subscription::create($subscriptionData);

Далее формируем VAPID для авторизации:

pushNotificationToClient.php

<?php $auth = array(    'VAPID' => array(        'subject' => 'https://your-project-domain.com',        'publicKey' => file_get_contents(__DIR__ . '/your_project/keys/public_key.txt'),        'privateKey' => file_get_contents(__DIR__ . '/your_project/keys/private_key.txt'),     ));

После того как сформировали нужные данные, создаем новый объект WebPush:

pushNotificationToClient.php

<?php$webPush = new WebPush($auth);

Ура! Наконец мы можем отправить запрос на отправку Push уведомления

<?php$report = $webPush->sendOneNotification(  $subscription,  "Тело пуш уведомления, оно поступило в тело sw.js");

Важное замечание

Для отправки уведомлений в итерации, стоит использовать функцию, с теми же параметрами, что и в функции выше:

$webPush->queueNotification

Полезные источники

  1. О технологии push

  2. О WebPush от хабровчанина

  3. Библиотека WebPush

  4. Пример использования от разработчика библиотеки

Подробнее..

Способы взаимодействия сервисов друг с другом. Пулингпуш. Достоинстванедостатки. Выбор

28.04.2021 18:07:33 | Автор: admin

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

  • Курьер доставил заказ. По смене статуса заказа надо уведомить заинтересованные стороны об этих событиях.

  • Клиент отправляет сообщение в чат поддержки. Нужно уведомить сервисы поддержки о поступивших данных от клиента.

  • Построение отчёта завершено. Ожидающий отчёт пользователь может его загрузить. Надо его уведомить об этом.

Знакомые/типовые ситуации. Одному сервису надо уведомить другой (другие) о происшедших событиях.

Давайте немного усложним:

Сервер - находится в нашей юрисдикции. Мы следим за тем, чтоб ресурсов ему на всё хватало. Добавляем ноды в кластер и т.п.

Заинтересованные в событиях стороны - находятся "где-то". На серверах клиентов. Которые экспериментируют, экономят на железе. Используют программные решения, не предназначенные для нагрузки, и т.п.

Какие способы уведомления есть?

Активность со стороны сервера

Это, в общем-то, типовое решение. Сервер держит список заинтересованных сторон. По мере появления событий выполняет HTTP-запросы к клиентам.

Подвариант этого решения: Websocket. Сервер отправляет события в сокеты всем подписанным сторонам.

Повторы, обработка ошибок

Рано или поздно любой TCP/HTTP-канал сталкивается с недоступностью другой стороны. Что делать после возникновения ошибки? Повторять запросы? Что делать с вновь поступающими запросами? Ждать, пока успешно выполнятся предыдущие?

Рассмотрим виды ошибок:

  1. Сетевые

  2. Устранимые (после повтора могут исчезнуть) HTTP (500, 502, 504, и т.п.)

  3. Неустранимые (4xx)

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

Идя по этому пути, надо постоянно и внимательно следить за мониторами таких ошибок. Анализировать трафик на тему "почему возникла неустранимая ошибка?" и "можно ли жить дальше с этой ошибкой".

Но это не самая большая проблема.

Более интересными являются проблемы:

  • повторов

  • 500-х ошибок

500-е ошибки

Мы выполняем запрос-передачу данных для сервера X. Происходит 500-я ошибка. Что это?

Возможны два варианта:

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

  2. В сервисе допущена ошибка, приводящая к 500. В этом случае, сколько бы повторов мы ни сделали, до исправления кода в приёмнике ситуация не изменится.

То есть, по повторяемости запросов ошибки у нас делятся на три вида:

  1. Те, которым повтор поможет (сетевые, устранимые 500-ки).

  2. Те, которым повтор не поможет, но выглядят как те, которым поможет (неустранимые 500-ки).

  3. Те, которым повтор не поможет (например 40x-ки).

Разрабатывая политику повторов, помимо указанной проблемы, имеем ещё множество других проблем:

  1. Как часто повторять запросы?

  2. Не будем ли мы "укладывать" внешний сервис, повторяя запросы?

  3. Не будем ли сами "укладываться", если одна из внешних систем по какой-то причине имеет некорректный TCP-стек (iptables DROP)?

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

Подытожим:

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

плюсы:

  • минимальный лаг доставки

  • минимальная нагрузка на хранилище сообщений;

минусы:

  • необходимость повторов в случае неуспеха доставок

  • необходимость ведения реестра, кому что доставлено и кому что нужно доставить

  • двусмысленность некоторых ошибок: непонятно, можно (нужно) ли повторять, или нет

  • зависимость от стека TCP на стороне клиентов (iptables -j DROP занимает слот отправки вплоть до таймаута)

  • система повторов может быть причиной DDoS для клиентских сервисов.

Также есть некоторое количество организационных минусов:

  • После того, как клиент прекратил де-факто работу (тут два варианта: сервера выключены, сервера не выключены), система продолжает доставлять ему уведомления.

Вебсокет в режиме клиент-сервер

Часть описанных проблем решает постоянное соединение, инициируемое клиентом. Однако именно часть.

Необходимость повторов и двусмысленность ошибок - снимаются. Однако, необходимость ведения реестра, кому и что нужно доставить (если мы говорим о системе сообщений "без потерь") остаётся. Зависимость от стека TCP на стороне клиента снижается, но не до нуля. Система также может быть причиной DDoS для клиентских сервисов.

Пулинг

Достоинства пулинга

  • Если у клиента проблемы со связью, нагрузкой - он просто не делает запросы

  • После того, как клиент отключается (организационный момент), - он перестаёт делать запросы

  • Максимально быстрое восстановление работоспособности после факапов.

Недостатки пулинга

  • минимальный лаг доставки сообщений равен интервалу пулинга, который обычно выбирается ненулевым

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

Ещё один неочевидный, организационный недостаток пулинга: часто способ получения новой порции данных связан со структурой хранения данных.

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

  • отсутствие двусмысленности, описанной выше

  • наиболее быстрое восстановление работоспособности после сбоя

  • максимальная независимость от сетевого стека TCP на клиенте

  • нет необходимости хранить/майнтенить список клиентов.

Лаг доставки

Для чего вводят интервал пулинга? Каждый клиент, делающий 1 запрос за данными в секунду, - это 1RPS нагрузки. Почему нельзя пулить, не используя интервал (делать запрос сразу после получения результатов предыдущего)? Потому что обычно запрос за данными является сравнительно дорогим. А дорогим он является потому, что, как правило, некорректно спроектирован.

Как правило, запросы для пулинга формулируются как "есть ли данные для меня; если есть, то какие?". Такие запросы (в случае, если они некорректно спроектированы) зачастую имеют следующие проблемы:

  • запрос неиндексирован

  • при перегрузках количество данных в ответе может расти, или время выполнения запроса ухудшаться.

В случае, если получение очередной порции данных сопровождается простой выборкой из BTREE индекса, то и ответ на вопрос "есть ли данные?", как правило, сравнительно бесплатен. Об индексах поговорим ниже.

А сейчас давайте рассмотрим алгоритм работы традиционного пулера.

  1. Первичная инциализация пулера. index := 0. index - это обобщённая переменная, указывающая на позицию запрашиваемых данных.

  2. Выполняется запрос limit данных с позиции index.

  3. Обрабатываем полученные данные

  4. index := index + 1

  5. Пауза соответствующая интервалу

  6. Перейти к шагу 2

Если рассматривать этот алгоритм, то видим, что переменная index и есть то, что связывает нас со структурой хранения данных.

Такой алгоритм, как правило, используют новички и... приводят себя к трудноустранимой проблеме: запросы с большими значениями index сделать индексируемыми крайне сложно. Почти невозможно.

Почему разработчик попадает в такую ситуацию? Потому что проектирует БД и API отдельно друг от друга. А нужно посмотреть на все компоненты в целом и на влияние их друг на друга.

Проблема состоит в том, что в БД, как правило, данные хранятся в виде плоских таблиц. Когда мы получаем очередную порцию данных с одними и теми же условиями фильтрации, то приходится делать что-то вроде следующего:

SELECT    *FROM    "table"WHERE    "somefield" = $1LIMIT    100OFFSET    $2

То есть, index из алгоритма пересчитывается в смещение ($2). Такой запрос из БД имеет всё более ухудшающийся план выполнения по мере роста смещения (которое растёт с ростом index).

Как сделать план независящим от положения смещения? Использовать вместо смещения выборку из индекса:

SELECT    *FROM    "table"WHERE    "id" > $1ORDER BY    "id"LIMIT    100

В этом случае алгоритм клиента должен поменяться таким образом, чтобы каждой итерации цикла использовать значение index, взятое от предыдущего шага. Перепишем алгоритм:

  1. Первичная инициализация. index := 0

  2. Выполняем запрос limit данных, передавая в запрос index

  3. Вычисляем новое значение index, как максимум от id в ответе

  4. Обрабатываем данные

  5. Пауза, соответствующая интервалу

  6. Перейти к шагу 2

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

Но давайте ещё порефлексируем над архитектурой. Что плохого в ней?

  • Алгоритм привязан к структуре данных

  • Выполняется практически полностью на стороне клиента

  • Вследствие предыдущей проблемы сложно, например, централизованно модифицировать его на иную работу после факапов/проблем.

  • Пользователь может сам подставлять в index произвольные значения. Иногда это может быть нежелательно или приводить к багам, которые разработчику сервера сложно понять.

Давайте ещё раз модифицируем алгоритм. Заменим index на state и управлять им будем с сервера:

  1. Первичная инициализация. state := null.

  2. Выполняем запрос limit данных, передавая в запрос значение state

  3. В каждом ответе, помимо данных, сервер возвращает new_state. state := new_state

  4. Обрабатываем данные

  5. Пауза-интервал

  6. Перейти к шагу 2

Что мы получили? Гибкость.

  • Переменная state определяется только сервером и не обязана быть привязанной к числу смещения. При желании в этой переменной можем хранить JSON со многими полями.

  • При желании можем ограничить возможности пользователя "хачить" запросы (использовать другие значения index, помните выше мы об этом говорили?). Этого можно достичь, например криптоподписывая state.

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

  1. Первичная инициализация. cursor := null, filters = значения_фильтров.

  2. Выполняем запрос limit данных, передавая в запрос значение cursor, filters.

  3. В каждом ответе, помимо данных, сервер возвращает cursor. cursor := response.cursor

  4. Обрабатываем данные

  5. Если данные были, перейти к шагу 2

  6. Пауза-интервал

  7. Перейти к шагу 2

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

Рекомендации по работе с курсорами:

  • Поскольку хранением курсора между запросами озадачен клиент, то имеет смысл хранить в курсоре и версию ПО сервера. В этом случае можно написать дополнительный код, обеспечивающий обратную совместимость (конвертацию форматов курсоров).

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

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

Пример. Изменение алгоритма после сбоя.

Любая система гарантированной доставки сообщений из точки А в точку B в случае факапов будет накапливать пул недоставленных сообщений. После восстановления работоспособности будет период времени, когда приёмник данных сильно отстаёт от источника.

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

То есть, limit делим, например, пополам. Половину limit'а заполняем данными из обычного курсора. А во второй половине начинаем передавать данные, начиная от id, с небольшим отставанием.

Таким образом, пользователи, запросившие отчёт прямо во время факапа, продолжат его ждать (и дождутся). А пользователи, запросившие отчёт после факапа, получат его с небольшой задержкой.

Пример алгоритма серверной стороны, включающего второй поток в случае сильного отставания, приведён на рисунке.

Пофантазировав, схему можно дополнить не одним, а несколькими фолбеками.

Курсорная репликация

Описанные курсоры можно использовать для репликации данных с сервиса на сервис.

Часто один сервис должен иметь у себя кеш/реплику части данных другого сервиса. При этом требований синхронности к этой реплике нет. Поменялись данные в сервисе A. Они должны максимально быстро поменяться и в сервисе B.

Например, мы хотим реплицировать табицу пользователей с сервиса на сервис.

Для такой репликации можно использовать что-то готовое из инструментария баз данных, а можно сделать небольшой "велосипед". Предположим, что пользователи хранятся в БД PostgreSQL. Тогда делаем следующее:

  • создаём дополнительный столбик SERIAL/BIGSERIAL в таблице users, назовём его lsn (Last sequence number).

  • модифицируем изменяющие пользователей запросы, чтобы на каждое изменение записи пользователя значение lsn устанавливалось бы из растущей последовательности

  • строим по полю lsn (уникальный) BTREE индекс.

В этом случае обновление записи пользователя будет выглядеть примерно так:

UPDATE   "users"SET   "name" = $1,   ...   "lsn" = DEFAULT /* последовательность */WHERE   "user_id" = $21

А запрос для работы курсора будет выглядеть как-то так:

SELECT    *FROM    "users"WHERE    "lsn" > $1ORDER BY    "lsn"LIMIT    $2

Каждое обновление пользовательской записи будет перемещать её в конец списка lsn. При этом общий размер отставания никогда не превысит размер таблицы пользователей.

Итоги

  1. Почти во всех случаях, когда применяется активная система уведомлений зависимых сервисов, её можно заменить описанной курсорной подпиской.

  2. При этом проблемы доступности клиентов, настроек, работоспособности TCP-стека останутся у клиентов

  3. Максимально быстрое и простое восстановление после простоя/сбоев. Отсутствие двусмысленностей в кодах ошибок.

Подробнее..

Как устроен Push Kit от Huawei

28.09.2020 10:23:58 | Автор: admin

Привет, Хабр! В мае прошлого года, оставшись без сервисов и API гугла, мы основательно взялись за работу над своей платформой Huawei Mobile Services. Это наш давний проект, к которому мы вернулись после введения американских санкций. Проблема отрезанных сервисов постепенно сошла на нет. Мы добавили недостающие компоненты: почтовый клиент, свои карты и другие сервисы, и HMS Core набор API, SDK и прочих сервисов для разработки, и улучшения приложений. В этот набор входит Push Kit облачная служба для рассылки уведомлений и не только. Под катом расскажем, как устроен этот инструмент, чем выделяется и как его можно использовать.

Главная функция Push Kit (об остальных поговорим дальше) доставлять на устройство пользователя уведомления от приложений. Этот процесс организован так:

  • сервер приложения подключается к API-интерфейсу Push Kit и загружает туда зашифрованные сообщения;

  • из облака Push Kit сообщения пересылаются на устройство с целевым приложением.

Кроме того, у Push Kit есть множество интересных и полезных фич:

  • Таргетирование рассылки по темам сообщений и другим критериям, в том числе с помощью Huawei Analytics Kit;

  • Отправка пуш-сообщений на основе сценариев одному или нескольким пользователям одновременно, рассылка по расписанию;

  • Отправка пуш-сообщений через консоль в интерфейсе AppGallery Connect.

  • Доступ к серверу Push Kit через HTTPS;

  • Отправка пуш-сообщений пользователям, которые пользуются разными профилями на одном Android-устройстве;

  • Отправка пуш-сообщений на устройства Android/iOS и веб-приложения;

  • Автоматический выбор языка пуш-сообщения в зависимости от языка системы устройства получателя;

  • Кэширование и повторная отправка в случае, если пуш-сообщение не доставлено из-за отсутствия сети.

Что и как можно послать через Push Kit

С помощью Push Kit можно не только доставлять уведомления, но и передавать данные напрямую в приложение. В этом случае на пользовательское устройство приходит послание, которое активирует в программе-адресате определённое действие. Функция может оказаться полезной, например, для соцсетей, когда вы получаете запрос добавиться в друзья или входящие вызовы по VoIP. Кроме того, с её помощью можно загружать в сообщения в мессенджеры, чтобы они уже были внутри, когда вы запустите программу.

Но всё-таки главной задачей является доставка пуш-уведомлений, внешний вид которых настраивается через интерфейс Push Kit. Это может быть обычный текст, текст с большой или маленькой картинкой, с иконкой приложения и т. д. Можно менять размеры элементов и их расположение, выбирать тип звукового сигнала, а также программировать сценарии действия, которые выполняются, когда пользователь открывает уведомление.

Если группа уведомлений от одного приложения в статус-баре свёрнута, по умолчанию в нём будут показаны первые два пуша их количество можно расширить до восьми. Такая группа может содержать до 24 пуш-уведомлений в EMUI 10 и до 49 в EMUI 9. Если сообщений больше, будет виден счётчик оставшихся уведомлений и следующее появится только после удаления одного из отображаемых пушей.

Имя пакета, отправляемого Push Kit, может содержать до 128 байт, а максимальный размер сообщения составляет 4 Кб.

Если у целевого устройства нет проблем с сетью, доставка пушей должна происходить не дольше, чем за 1 секунду. Если же девайс получателя не в сети, Push Kit кеширует сообщение и отправляет его после того, как устройство переходит в онлайн.

Особенности Push Kit

Уведомления через Push Kit приходят быстро и снижают энергопотребление устройства. Операционки большинства мобильных девайсов автоматически закрывают приложения, которые долгое время не проявляют активности. Но чтобы программа могла показывать свои пуши, зачастую ей нужно работать хотя бы в фоне. Так что системе приходится перезапускать нужный ей софт, чтобы вывести пуш на экран.

Push Kit позволяет этого не делать: сообщения попадают в его центр уведомлений (NC), минуя целевые приложения. За это отвечает специальный компонент под названием Push Service. В итоге программа запускается только тогда, когда это предусмотрено сценарием, например после нажатия на уведомление.

Отсюда и высокая скорость доставки: благодаря Push Service пользователь может прочитать сообщение практически сразу после того, как оно попадёт на телефон.

Push Kit предоставляет канал системного уровня, способный одновременно отправлять десятки миллионов сообщений в секунду и десятки миллиардов в день. При этом 99 % пушей доставляются пользователям в течение 10 минут, даже если приложение не запущено. По сути, уведомления не доходят, только если устройство получателя выключено или находится вне сети.

Push Service устанавливается на все мобильные устройства с EMUI, так что на них уведомления отображаются в унифицированном виде.

Служба Центр уведомлений доступна на некоторых устройствах Huawei с EMUI 4 и на всех начиная с EMUI 5. А вот на гаджетах сторонних производителей эта функция недоступна, но если установить на них HMS Core, эти телефоны тоже научатся принимать уведомления, отправленные через Huawei Push Kit. Увы, из-за системных ограничений скорость доставки на телефоны других марок может снизиться.

Push Kit поддерживает основные кроссплатформенные среды разработки мобильных приложений, включая React Native, Cordova, Xamarin и Flutter. Чтобы разрабатывать на одном языке приложение, которое будет работать и на iOS, и на Android, нужно только интегрировать соответствующий пакет подключаемого модуля SDK.

После получения сообщений через Push Kit пользовательские устройства информируют об этом ваш сервер, помогая отслеживать ход рассылки в режиме реального времени. Благодаря этому можно быть в курсе статуса каждого уведомления и иметь возможность оперативно реагировать на неудачную доставку. Например, отправить SMS человеку, до которого не доходят пуши.

Push Kit автоматически показывает пуши на языке, который установлен в устройстве пользователя. Охват Push Kit более 200 стран и регионов, поэтому такая функция будет очень полезна.

Под капотом Push Kit: немного практики

Чтобы воспользоваться сервисом, надо зарегистрировать аккаунт разработчика на сайте Huawei Developers. Кроме этого понадобятся:

  • компьютер на Windows с установленным пакетом Java JDK 1.8, Android Studio и Android SDK;

  • смартфон Huawei с USB-кабелем и установленным HMS Core не ниже 4-й версии.

На схеме показан процесс разработки приложения под Android. iOS и веб-приложения поддерживаются в бета-режиме.

Первым делом:

  • создаём приложение в AppGallery Connect и проект в Android Studio;

  • генерируем криптографический ключ SHA-256;

  • сохраняем файл конфигурации приложения;

  • подключаем к приложению модуль AppGallery Connect;

  • добавляем репозиторий Maven в файл build.gradle.

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

Важный момент: чтобы использовать Push Kit для веб-приложений, нужно настроить его через приложение для Android. Кроме того, существуют Quick Apps приложения, не требующие установки, которые можно использовать на мобильных телефонах более 12 крупных производителей в Китае. Все они доступны в AppGallery и поддерживают Push Kit.

Чтобы использовать Push Kit, укажите место для хранения данных вашего проекта. Если его не задать, будут недоступны таргетирование по темам сообщений, рассылка через агент WebPush и работа с iOS.

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

Чтобы уменьшить задержку, есть смысл хранить данные неподалёку от пользователей. Например, если вы в России разрабатываете приложение для жителей из Евросоюза, оптимальным местом для сервера будет Германия.

Интеграция HMS Core SDK

Добавим файл конфигурации вашего приложения в AppGallery Connect.

  1. В AppGallery Connect кликаем My project.

  2. Находим и выбираем приложение, в которое хотим интегрировать HMS Core SDK.

  3. Переходим в Project Setting General information. В App information загружаем файл agconnect-services.json.

  4. Копируем файл agconnect-services.json в корневой каталог приложения нашего проекта Android Studio.

Добавим связи сборки. Для этого открываем файл build.gradle в каталоге приложения.

Прописываем в разделе dependencies:

dependencies {    implementation fileTree(dir: 'libs', include: ['*.jar'])    implementation 'androidx.appcompat:appcompat:1.0.2'    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'    testImplementation 'junit:junit:4.12'    androidTestImplementation 'androidx.test:runner:1.1.1'    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'    // Add the following line    implementation 'com.huawei.hms:push:5.0.0.300'}

5.0.0.300 заменим на последнюю версию HMS Core Push SDK.

Нажимаем Sync Now, чтобы синхронизировать файл build.gradle.

Если отображается synced successfully, всё прошло хорошо.

Настройка файла Manifest

Чтобы устройства могли получать токен push и сообщения, отправленные сервером Push Kit, нужно изменить файл AndroidManifest.xml. Определяем класс MyPushService, он наследуется от класса HmsMessageService.

<service    android:name=".MyPushService"    android:exported="false">    <intent-filter>        <action android:name="com.huawei.push.action.MESSAGING_EVENT" />    </intent-filter></service>

В этом примере кода нужно только изменить MyPushService в android: name = ". MyPushService" на имя класса, наследуемого от класса HmsMessageService. Всё остальное сохраняем.

Настройка сценариев шифрования

Открываем файл конфигурации обфускации proguard-rules.pro проекта Android Studio. Добавляем параметры, исключающие из этого процесса HMS Core SDK.

-ignorewarnings-keepattributes *Annotation*-keepattributes Exceptions-keepattributes InnerClasses-keepattributes Signature-keepattributes SourceFile,LineNumberTable-keep class com.hianalytics.android.**{*;}-keep class com.huawei.updatesdk.**{*;}-keep class com.huawei.hms.**{*;}

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

"R.string.agc*","R.string.hms*","R.string.connect_server_fail_prompt_toast","R.string.getting_message_fail_prompt_toast","R.string.no_available_network_prompt_toast","R.string.third_app_*","R.string.upsdk_*","R.layout.hms*","R.layout.upsdk_*","R.drawable.upsdk*","R.color.upsdk*","R.dimen.upsdk*","R.style.upsdk*"

Разрабатываем демо

Ниже показан общий код проекта и структура ресурсов.

Открываем наш проект в Android Studio, создаём виджет и разворачиваем TextView в MainActivity, чтобы отобразить токен. Там же, в MainActivity, программируем метод обновления токена в TextView.

public class MainActivity extends AppCompatActivity {    private TextView tvToken;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tvToken = findViewById(R.id.tv_log);        MyReceiver receiver = new MyReceiver();        IntentFilter filter=new IntentFilter();        filter.addAction("com.huawei.codelabpush.ON_NEW_TOKEN");        MainActivity.this.registerReceiver(receiver,filter);    }    public class MyReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            if ("com.huawei.codelabpush.ON_NEW_TOKEN".equals(intent.getAction())) {                String token = intent.getStringExtra("token");                tvToken.setText(token);            }        }    }}

Для подачи заявки на токен объявляем <meta-data> в файле AndroidManifest.xml.

<application    <meta-data        android:name="push_kit_auto_init_enabled"        android:value="true" /></application>

Name и value в meta-data копируем без изменений. Push SDK будет подавать заявку на получение токена при запуске приложения.

Токен получается методом onNewToken в thMyPushService*ce class.

MyPushService.java

public class MyPushService extends HmsMessageService {    private static final String TAG = "PushDemoLog";    @Override    public void onNewToken(String token) {        super.onNewToken(token);        Log.i(TAG, "receive token:" + token);        sendTokenToDisplay(token);    }    private void sendTokenToDisplay(String token) {        Intent intent = new Intent("com.huawei.push.codelab.ON_NEW_TOKEN");        intent.putExtra("token", token);        sendBroadcast(intent);    }}

Класс MyPushService наследуется от HmsMessageService. Он, в свою очередь, переопределяет метод для подтверждения онлайн-статуса устройства onNewToken.

Сборка, загрузка и отладка приложения

Подключаем телефон Huawei к компьютеру.

Способ 1. Нажимаем Play (зелёный треугольник), чтобы начать компиляцию и сборку. Устанавливаем APK на телефон.

Способ 2. Используем Android Studio, чтобы упаковать APK.

Затем используем инструмент ADB, чтобы установить APK на телефон для отладки.

adb install D:\WorkSpace\CodeLab\pushdemo1\app\release\app-release.apk

После запуска демо убеждаемся, что телефон подключён к сети. Метод onNewToken вызываетсядлявозврататокенаAFcSAHhhnxdrMCYBxth2QOG9IgY2VydAM61DTThqNux3KBC_hgzQQT*******.

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

Отправка пуш-уведомлений

В интерфейсе Push Kit в AppGallery Connect можно редактировать сообщения и отправлять их на устройства, подключённые к сети. Перед отправкой уведомлений находим приложение по названию. В этом примере имя APK com.huawei.codelabpush. Подробнее можно прочитать здесь.

Если всё прошло удачно, на экране телефона увидим уведомление.

Коммерческие возможности Push Kit

Почти все известные нам российские кейсы связаны с банковскими учреждениями.

Первой отечественной и второй в мире компанией, интегрировавшей наш Push Kit, стала mfms: их решения по доставке пуш-уведомлений используют ВТБ, Альфа-Банк, Сбер и другие финансовые учреждения России.

Например, в ВТБ пушами клиентам сообщают о денежных операциях в реальном времени. Такие уведомления стоят сильно дешевле SMS, а затраты на внедрение минимальны: с SDK, которое предоставляет mfms, не надо интегрировать APNs, FCM и Huawei Push Kit с нуля.

Пуши используются не только для рассылки. На основе Push Kit в mfms разработали фичу, которая позволяет клиентам банков получать уведомления с подтверждением денежных операций. Такие сообщения рассылаются через SDK и заменяют SMS с кодами для оплаты. Более того, mfms научились превращать эти пуши в красивые уведомления на русском языке с брендированным названием, логотипом и категорией магазина, в котором пользователь совершил оплату.

Какие ещё сферы применения можно найти для пуш-уведомлений и как извлечь из Push Kit больше пользы вопросы не такие уж и сложные, но для нас чрезвычайно интересные. Давайте их обсудим в комментариях: ждём ваши идеи, варианты использования платформы или, возможно, предсказания о будущем пуш-уведомлений.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru