Продолжение предыдущего поста.
Чего же придумали эти хитропопые инженеры в попытке нейтрализовать негативные эффекты, возникающие вследствие взаимодействия мобильных приложений с внешним миром? А давайте, говорят, введём в цепь обмена данными между клиентом и сервером двух посредников: специальную системную службу на телефоне и специальный "центральный" сервер, с которым будет соединяться та самая специальная системная служба. Так появился Push.
Другими словами, пока приложение работает в foreground-е (то бишь активно), оно может без ограничений общаться с кем хочет, когда хочет и в любых количествах. Но как только оно уходит в background (в "фон") неважно по какой причине, "засыпает". А заснув, лишается возможности что-либо отправлять или принимать. Но при этом может попросить системную службу "разбудить" себя либо по таймеру (например, через N секунд), либо по событию (что-то пришло снаружи).
Но как оно получит что-то снаружи, ежели само спит? А очень просто. Отправитель сообщения сперва передаст его на сервер Google / Apple / Microsoft в зависимости от марки устройства. Тот, в свою очередь, найдёт заранее установленную TCP-сессию до push-службы телефона и передаст сообщение ей. А уже push-служба, в свою очередь, разбудит приложение, передаст ему управление и скажет что надо делать.
Вроде бы как всем хорошо. Покуда экран аппарата выключен, все спят. Только шофёр push-агент не спит. Он героически "держит" одну-единственную TCP-сессию с одним-единственным сервером, причём без большой и малой нужды никакие данные через неё не гоняет. Таким образом, и радиомодули тоже спят. А push-сервер тоже, сцуко, хитрый. Если на него придёт ряд однотипных сообщений, то он сначала выжидает минуту-другую, дабы поднакопить их. А потом "выстреливает" всё это на клиентское устройство одной большой пачкой, чтобы избежать серии последовательных включений-выключений радиопередатчика. Это называется "throttling" (не путать с троллингом). Казалось бы, идиллия и рай на Земле.
Но нет. Как всегда, здесь появляется куча разных побочных эффектов.
- Некоторые производители в дополнение к "стандартным" технологиям энергосбережения добавляют свои собственные. Особенно этим любят баловаться Sony и Samsung. Что именно они "доделывают", доподлинно неизвестно. То ли отправляют в глубокий сон самого push-агента, то ли принудительно отключают всю связь. Но факт остаётся фактом. Меньше всего проблем с Push-ем наблюдается на "родных" Nexus-ах, больше всего — на изделиях вышеупомянутых брендов.
- На устройстве либо где-нибудь "по дороге" могут присутствовать антивирусы / файрволлы, которые вмешиваются в работу push-механизма и частенько ломают его.
- Хотя чисто формально для работы push от пользователя не требуется наличия зарегистрированного на сервисах Google активного аккаунта, некоторые утверждают, что тем не менее без него "почему-то" push не работает.
- Если у аппарата присутствуют и Wi-Fi, и GPRS модули, то push будет всегда предпочитать GPRS ввиду его бо́льшей энергоэффективности для обмена небольшим количеством данных. Часто даже случается, что телефон ингорирует настройку "передача данных через сотовые сети" и всё равно начинает использовать GPRS, даже если пользователь его отключил. Иногда это становится источником заметных непредвиденных финансовых расходов в роуминге. А бывает и наоборот. Допустим, имеется стабильный приём сигнала по Wi-Fi и очень неустойчивый через GPRS. Пользователь наивно полагает, что всё хорошо и он на связи, только вот push-и скорее всего не дойдут. А вместе с ними "потеряется" и уведомление о каком-нибудь важном входящем сообщении.
- Push-агент и push-сервер периодически пингуют друг друга с некоторым интервалом дабы убедиться, что TCP-сессия ещё жива-здорова. Это называется "heartbeat". В андроиде пресловутый интервал равен 15-ти минутам для Wi-Fi и 28 минутам для GPRS. И вот тут начинаются всякие интересности. Если, например, домашний маршрутизатор, либо NAT интернет-провайдера оборвёт неактивную TCP-сессию раньше, чем пройдёт 15 минут, то с момента разрыва до окончания этого интервала push гарантированно перестанет работать. То же самое произойдёт, и в том случае, если после "пробуждения" беспроводного интерфейса DHCP-сервер выдаст телефону другой IP-адрес.
- А ведь операторы сотовой связи (ОПСОСы) в России тоже выдают исключительно "серые" адреса, трафик с которых потом и фильтруют, и маскарадят (NATят). Каким образом они это делают, какие ограничения по количеству одновременно открытых TCP-сессий и какие тайм-ауты неактивности выставляют на своём оборудовании — никто кроме них самих не знает. То есть, вполне может произойти, что при перемещении из одной соты в другую либо через 5...10 минут все установленные с вашего устройства сессии принудительно оборвут на стороне оператора. Что при этом случится с push-ем, наверное, сами догадаетесь.
Таким образом выясняется, что механизм push — крайне ненадёжный и ломается от малейшего дуновения. И одно дело, когда таким образом тебе сообщают о погоде на завтра или выходе нового ролика на Youtube. И другое дело, когда ты ждёшь важное сообщение в электропочту или мессенджер. Естественно, программистов такой расклад совершенно не устраивает, и они начинают лепить и вставлять костыли различной степени корявости, так или иначе приводящие к увеличению расхода трафика и ускорению разряда батареи. Что они могут предпринять.
- Принудительно запретить телефону гасить радиомодули и выгружать приложение. Тут понятно, это совсем жесть.
- Не надеясь на push, установить и поддерживать отдельное постоянное TCP-соединение со своим собственным сервером.
- Как разновидность предыдущего варианта, периодически (например, раз в минуту) "будить" собственное приложение по системному таймеру, пинговать свой сервер для проверки наличия отклика и актуальности TCP-сессии, после чего "засыпать" обратно. То есть best practise violation в чистом виде.
- Собирать статистику по задержкам поступления push-сообщений. Если они находятся в разумных пределах, то продолжать использовать push. Если нет — применить какой-то из вышеописанных способов.
Эти костыли такие костыли. Но конкретно меня больше всего огорчает даже не это. Допустим, есть некая SIP-трынделка. Если нужный нам SIP-регистратор не поддерживает TCP, то это вообще клиника. Регистрация сто пудово отвалится очень быстро. Либо придётся постоянно держать включёнными радиомодули. Но предположим, что всё не так плохо.
В таком случае в целях экономии батарейки было бы очень круто постоянно "спать", а информацию о входящем вызове получать через push. Та же самая Bria, кстати, умеет так делать. Но после осознания механизмов, лежащих в основе push-а стало понятно, что это ни разу не надёжный метод. Пропущенные звонки практически гарантированы. Остаётся только всегда поддерживать собственную TCP-сессию, периодически "подновляя" её. Другими словами, запущенный на смартфоне VoIP-клиент — это всегда "до свиданья" аккумулятору, как ни крути.
И теперь мне стало понятно, почему иногда сообщения в WhatsApp приходят с большой задержкой, иногда до получаса. Но программисты в этом не виноваты. Против нас выступает само мироздание. А также ОПСОСы.
На всякий случай повторюсь, что мои умозаключения справедливы для Android, других вендоров я не рассматривал. Но сомневаюсь, что там будут какие-то прям кардинальные отличия. Ведь основополагающие принципы работы везде одни и те же.
При написании этого псто использовались следующие статьи: раз, два, три, четыре, пять.