Всех приветствую.
Сегодня расскажу о том, как можно быстро и легко отправить произвольное push сообщение с сервера на мобильное устройство на базе Android (на iOS нет возможности попробовать, на Windows Phone этот метод работать не будет, по крайней мере пока).
Чтобы пример был осмысленным, внесем немного ясности. Допустим есть некая конфигурация, которая крутится на неком ПК - назовем это Сервер. Есть необходимость в том, чтобы при наступлении какого-либо события на Сервере была инициирована, например, синхронизация с одним или несколькими телефоном/ТСД (далее просто мобильное устройство или МУ). На самом деле можно придумать любой другой пример, но суть в том, что инициатива какого-либо действия будет именно со стороны Сервера а не МУ. Сигналом к действию для МУ и будет выступать push сообщение.
Вот такая вот небольшая задача, которую надо решить.
Нам потребуется:
- Платформа 8.3.6 и выше
- Мобильная платформа 8.3.6 и выше
- Аккаунт Google
- Около 20 мин свободного времени )
О том, как развернуть мобильную платформу для разработчика на своем устройстве писать не буду, информации много (я лично слушал бесплатный курс многоуважаемого DitriX).
Ну, приступим. Откроем конфигурацию Сервера и создадим в ней общую форму, из которой будем отправлять пуши клиенту. На форме создадим одну команду, которая и будет отправлять сообщение. Теперь разберемся, каким объектом на уровне платформы представлено push сообщение. СП подскажет нам, что это ДоставляемоеУведомление. Его инициализация довольно простая, объект доступен везде, кроме Вэб-клиента, что нас вполне устраивает. Поместим следующий код в обработчик нажатия команды общей формы. Контекст выполнения в данном случае не важен. Сразу оговорюсь, что дальше по тексту не будет каких либо проверок на корректность данных, это и так все понятно.
Уведомление = Новый ДоставляемоеУведомление;
Уведомление.Заголовок = "Необходимо выполнить синхронизацию";
Уведомление.Текст = "Новые задания ожидают загрузки на устройство.";
Уведомление.Данные = "ВыполнитьСинхронизацию";
Уведомление.ЗвуковоеОповещение = ЗвуковоеОповещение.ПоУмолчанию;
Если бы мы хотели использовать локальные уведомления мобильной платформы на самом МУ, то такой инициализации было бы вполне достаточно, но у нас задача прислать пуш от сервера. Поэтому придется разобраться с еще одним свойством объекта ДоставляемоеУведомление, а именно Получатели. Это массив, то есть при желании можно отправить сообщение сразу нескольким МУ (например, если документ уже был выгружен на МУ1, а затем менеджер решил, что документ должен быть на МУ2). Тип данных элементов этого массива ИдентификаторПодписчикаДоставляемыхУведомлений, и опять же смотрим СП и видим, что о нас позаботились разработчики 1С и придумали замечатльный метод ПолучитьИдентификаторПодписчикаУведомлений, который возвращает как раз то, что нам нужно. Казалось бы практически все готово, но тут начинается самое интересное. Смотрим, что данный метод доступен только на мобильном клиенте, и вдобавок в качестве параметра необходимо указывать какой-то НомерПриложенияGoogleCloud. Получить его довольно просто. Для этого идем в Консоль разработчика Google, авторизуемся и попадаем на стартовую страницу. Тут необходимо создать новый проект.
Мастер создания проектов попросить придумать проекту какое-либо имя (оно будет нужно только для того, чтобы отличать проекты в самой консоли) и согласиться с условиями гугла. После создания проекта уже можно посмотреть необходимый нам НомерПриложенияGoogleCloud. Для этого перейдем в панель администрирования проекта, а затем в настройки.
Попробуем теперь получить ИдентификаторПодписчикаДоставляемыхУведомлений, который доступен через менеджера доставляемых уведомлений, но вспомним, что это все доступно только на мобильном клиенте. Поэтому надо решить, как мы будем передавать полученный ИдентификаторПодписчикаДоставляемыхУведомлений на Сервер. Способов на самом деле достаточно, но я предлагаю использовать http-сервис. Естественно он должен быть создан на Сервере. Итак, создадим http-сервис, с корневым URL testpush и одним методом типа GET, в модуль которого поместим следующий код
ПараметрыЗапроса = Запрос.ПараметрыЗапроса;
Если ПараметрыЗапроса.Количество() <> 1 Тогда
Ответ = Новый HTTPСервисОтвет(400);
Ответ.Заголовки.Вставить("Content-Type","text/text; charset=UTF-8");
Ответ.УстановитьТелоИзСтроки("Неверное количество параметров.");
Возврат Ответ;
КонецЕсли;
IDПодписчика = ПараметрыЗапроса.Получить("reginfo");
Константы.IDПодписчика.Установить(Новый ХранилищеЗначения(IDПодписчика, Новый СжатиеДанных(9)));
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type","text/text; charset=UTF-8");
Ответ.УстановитьТелоИзСтроки("Все прошло успешно.");
Возврат Ответ;
Что здесь происходит? Так как мы планируем, что этот сервис будет вызываться с МУ, то ожидаем от МУ 1 параметр reginfo (можно его сделать обязательными, или вообще использовать POST и передавать все в теле, но сейчас не про это) - это сериализованный объект ИдентификаторПодписчикаДоставляемыхУведомлений. Поместим все это в Хранилище значения и запишем в константу, предварительно создав ее. Теперь наконец-то на сервере у нас будет нужный нам параметр для отправки, но пока у нас до сих пор нечего отправлять, да и неоткуда. Займемся этим вопросом и начнем делать конфигурацию для МУ. Так как объект ИдентификаторПодписчикаДоставляемыхУведомлений имеет переменчивую природу и реккомендуется его переодически обновлять то поступим следующим образом. В модуле управляемого приложения поместим следующий код (конфигурация для МУ)
Процедура ПриНачалеРаботыСистемы()
ОбновитьIDПодписчика();
ПодключитьОбработчикОжидания("Подключаемый_ОбновитьIDПодписчика", 7200);
КонецПроцедуры
Процедура Подключаемый_ОбновитьIDПодписчика() Экспорт
ОбновитьIDПодписчика();
КонецПроцедуры
Процедура ОбновитьIDПодписчика()
#Если МобильноеПриложениеКлиент Тогда
НомерПроекта = ОбщийМодульВызовСервера.ПолучитьНомерПроекта();
Если ПустаяСтрока(НомерПроекта) Тогда
Возврат
КонецЕсли;
Попытка
IDПодписчика = ДоставляемыеУведомления.ПолучитьИдентификаторПодписчикаУведомлений(НомерПроекта);
Исключение
Возврат
КонецПопытки;
ПараметрыЗапроса = Новый Структура("АдресСервера, ИмяБазы, IDПодписчика");
// указываем IP Сервера, он может быть как локальный, так и нет
ПараметрыЗапроса.АдресСервера = "192.168.0.11";
// имя базы, под которым она была опубликована на вэб сервере
// о том, как выполнить публикацию информации в Интернете информации предостаточно
ПараметрыЗапроса.ИмяБазы = "base";
ПараметрыЗапроса.IDПодписчика = IDПодписчика;
ОбщийМодульВызовСервера.ОтправитьIDНаСервер(ПараметрыЗапроса);
#КонецЕсли
КонецПроцедуры
Для того, чтобы IDПодписчика всегда был актуальным на Сервере, подключим обработчик ожидания, который будет получать "свежий" ИдентификаторПодписчикаДоставляемыхУведомлений и отправлять его на Сервер, кроме того получение и отправку будем вызывать при самом старте системы. Все эти операции можно выполнять в фоне, чтобы это было незаметно для пользователя. Важным моментом является то, что при вызове метода ДоставляемыеУведомления.ПолучитьИдентификаторПодписчикаУведомлений будет происходить обращение к серверам Google, поэтому на МУ должен быть доступен Интернет. Стоит заметить, что у меня не вышло получить таким образом IDПодписчика на вирутальном устройстве, все вываливалось в ошибку неправильного параметра НомерПроекта (может это как-то связано с ограничением бесплатного функционала Genymotion), но на реальном устройстве все прекрасно отрабатывает. В общих модулях поместим следующий код.
ОМ Вызов сервера
Функция ПолучитьНомерПроекта() Экспорт
Возврат Константы.НомерПроекта.Получить();
КонецФункции // ПолучитьНомерПроекта()
Процедура ОтправитьIDНаСервер(ПараметрыЗапроса) Экспорт
ОбщийМодульСервер.ОтправитьIDНаСервер(ПараметрыЗапроса);
КонецПроцедуры
ОМ Сервер
Процедура ОтправитьIDНаСервер(ПараметрыЗапроса) Экспорт
ПараметрыЗадания = Новый Массив();
ПараметрыЗадания.Добавить(ПараметрыЗапроса);
ФоновыеЗадания.Выполнить("ОбщийМодульСервер.ОтправитьIDНаСерверВФоне", ПараметрыЗадания, , "ОтправкаIDПодписчика");
КонецПроцедуры
Процедура ОтправитьIDНаСерверВФоне(ПараметрыЗапроса) Экспорт
Попытка
Соединение = Новый HTTPСоединение(ПараметрыЗапроса.АдресСервера);
// первым параметров HTTPЗапрос является адрес ресурса
// в данном случае сначала идет имя серверной базы, под которым она опубликована на вэб-сервере
// т.к. мы использовали http-сервис, то после имени идет служебный параметр hs
// при создании http-сервиса мы указывали коревой URL - testpush
// т.к. в сервисе 1 нас всего один метод, и он имеет тип GET, то через знак ? укажем имя параметра reginfo
// и далее сериализованный IDПодписчика
// если все ок, то в браузере можно набрать полученный URL и на нашем Сервере все отработает как нужно
Запрос = Новый HTTPЗапрос(ПараметрыЗапроса.ИмяБазы + "/hs/testpush?reginfo="+Сериализовать(ПараметрыЗапроса.IDПодписчика));
Запрос.Заголовки.Вставить("Content-Type","text/xml; charset=UTF-8");
Соединение.ВызватьHTTPМетод("GET", Запрос);
Исключение
КонецПопытки;
КонецПроцедуры
Функция Сериализовать(Данные)
ОбъектXDTO = СериализаторXDTO.ЗаписатьXDTO(Данные);
ЗаписьXML = Новый ЗаписьXML;
ПараметрыЗаписиXML = Новый ПараметрыЗаписиXML("UTF-8", "1.0", Ложь);
ЗаписьXML.УстановитьСтроку(ПараметрыЗаписиXML);
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, ОбъектXDTO);
Возврат ЗаписьXML.Закрыть();
КонецФункции // Сериализовать()
Теперь у нас все готово чтобы на Сервере наконец-то оказался ИдентификаторПодписчикаДоставляемыхУведомлений, запускаем мобильное приложение, указываем наш номер проекта, перезапускаем приложение и вуаля, на Сервере константа IDПодписчика теперь хранит что-то типа
<DeliverableNotificationSubscriberID xmlns="http://v8.1c.ru/8.3/data/ext" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<subscriberType>GCM</subscriberType>
<deviceID>APA91bHaICui92Jdsbv9vrBQsmebjhXvEhUgswKRkCoFwFVHcrCR6RlRH0OqTYRNWUUoM19nv_emkL-OuLfuAwVZfz8u07SDLTwffphWJ71yzTn3YRAKL6M</deviceID>
<applicationID>com.e1c.mobile</applicationID>
<databaseID>7e874fd6-e97e-6c1f-ac33-683d141e189d</databaseID>
</DeliverableNotificationSubscriberID>
Сервер уже знает практически все, чтобы отправить пуш, поэтому вернемся к нему. В общей форме добавим следующий код
Уведомление.Получатели.Добавить(Десериализовать(Константы.IDПодписчика.Получить().Получить()));
Получаем из константы хранилище значения, а из него уже нашу сериализованную строку и десериализуем ее.
Функция Десериализовать(Данные, ТипПреобразования = Неопределено)
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(Данные);
ПрочитанныеДанные = СериализаторXDTO.ПрочитатьXML(ЧтениеXML, ТипПреобразования);
ЧтениеXML.Закрыть();
Возврат ПрочитанныеДанные;
КонецФункции // Десериализовать()
Теперь у нас есть готовое к отправке пуш сообщение. Для отправки необходимо использовать метод глобального контекста ОтправкаДоставляемыхУведомлений.Отправить. В качестве первого параметра необходимо передать сформированный объект ДоставляемоеУведомление, а вот вторым параметром идет строка с ключом авторизации для подключения к службе доставки "Google Cloud Messaging" (из СП). Стоит заметить, что Google Cloud Messaging уже не получится использовать, т.к. гугл использует более новую технологию Firebase Cloud Messaging или просто FCM. Таким образом нам нужно получить этот заветный ключ авторизации у гугла. Для этого вернемся в консоль разработчика Google и перейдем по ссылке.
Перед нами откроется страница Firebase Cloud Messaging, там много чего интересно, но нам надо перейти в консоль.
Добавим новый проект, указав имя, которое мы указывали при создании проекта в консоли разработичка гугл. После ввода первых символов мастер создания предложить выбрать этот проект. Соглашаемся, также выбираем страну и нажимаем Добавить FireBase. Псоле этого возвращаемся в консоль разработчика гугл (теперь панель управления консоли примет иной вид нежели ранее) и идем в раздел Учетные данные. Тут нас будет интересовать ключи API, а именно ключ сервера.
Копируем его, ведь именно он нам и нужен для отправки пуша, то есть это и есть ключ авторизации для подключения к службе доставки. Возвращаемся в конфигуратор Сервера и немного добавим кода в общей форме. В итоге код отправки push сообщения будет выглядеть следующим образом
Уведомление = Новый ДоставляемоеУведомление;
Уведомление.Заголовок = Заголовок;
Уведомление.Текст = ТекстСообщения;
Уведомление.Данные = Данные;
Уведомление.ЗвуковоеОповещение = ЗвуковоеОповещение.ПоУмолчанию;
Уведомление.Получатели.Добавить(Десериализовать(ПолучитьIDПодписчика()));
ОтправкаДоставляемыхУведомлений.Отправить(Уведомление, КлючСервера);
Где Заголовок, ТекстСообщения, Данные, КлючСервера - это реквизиты формы, а функция ПолучитьIDПодписчика возвращает значение одноименной константы.
Настало время первого теста. Запускаем приложение на МУ, блокируем экран или сворачиваем приложение, открываем в режиме Предприятие конфигурацию Сервер и жмем Отправить.
Ура, МУ прожужжал/пропищал или что-то в этом роде, в общем, все получилось, сообщение доставлено.
Остался еще один небольшой нюанс. Пока толку от такого сообщения немного, приложение ничего не делает при получении пуша. Исправим этот момент. В конфигурации МУ, в модуле управляемого приложения в событие ПриСтартеСистемы добавим следующий код
#Если МобильноеПриложениеКлиент Тогда
ДоставляемыеУведомления.ПодключитьОбработчикУведомлений("Подключаемый_ОбработкаУведомления");
#КонецЕсли
Процедура Подключаемый_ОбработкаУведомления
Процедура Подключаемый_ОбработкаУведомления(Уведомление, Локальное, Показано) Экспорт
// просто сообщим тест пуша
Сообщить(Уведомление.Текст);
КонецПроцедуры
Теперь после отправки сообщения с сервера можно выполнить любые действия на клиенте. В данном случае мы просто покажем текст сообщения.
Вместо заключения
Firebase это не только отправка push сообщений, но как мимнимум позволяет работать с рекламой в мобильных приложениях (с этим пока не разбирался), и, наверно еще много чего интересного, так что есть чем заниматься, господа!
p.s. в архиве 2 конфигурации - Сервер и Клиент, код которых на 90% изложен в этой публикации.