Как мы Алкосканер делали

в 6:23, , рубрики: android, appstore, dev story, Google Play, iphone, алкоголь, приложение, я пиарюсь, яндекс, метки: , , , , , , ,

Как мы Алкосканер делали

Привет.

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

Приложение – Алкосканер, сделали под iPhone и Android (от 4.0 пока, релиз под двойки готовится к выпуску). Полностью бесплатен. Краткая суть – аггрегатор акций и спецпредложений на покупку алкоголя. На данный момент работает в Москве (города-миллионники будут постепенно добавляться), поддерживается 2500 магазинов, в базе порядка 70 000 спецпредложений.

Изначально все планировалось в одном виде, в результате к релизу приложение хорошо эволюционировало, но это же породило и определенные проблемы.

Итак, начнем с сервера

image

1. Скорость работы

Вообще очень ключевой фактор, который особенно актуален на мобильниках (никто не будет долго ждать ответа, особенно, когда выпить хочется сейчас). Изначально все было хорошо, но после загрузки первой боевой базы оказалось, что данных настолько много, что они до клиента будут идти вечность, особенно на радиусе Москва. Пришлось переписывать вывод, что повлекло за собой значительные тормоза на серверной стороне. Оптимизировали-оптимизировали и выоптимизировали. На выделенном сервере у Hetzner все работало достаточно быстро и адекватно, но потом мы переехали на Селектел…

2. Серверная платформа

Выбирая хостинг-провайдера, мы руководствовались близостью серверов к Москве для уменьшения пинга в первую очередь и масштабированием (желательно автоматическим), во вторую, потому что в пиковые периоды рассылки у нас бывают сотни пользователей одновременно, а запросы, связанные с геопозицией, довольно дорогие. Скалакси почему-то так и не ответили на звонок ни разу, выбрали Селектел (кстати, я как-то даже не знаю других российских облаков, может, подскажете?). Но не сложилось – постоянные самостоятельные падения сервера без нагрузки, взлетающая под 200% загрузка процессора, когда никого нет в приложении и т.д. убедили вернуться на Хетцнер.

3. Итог

Пока бегает все на PHP+MySQL (фреймворк CodeIgniter с собственной надстройкой над ним). Планируем часть MySQL вынести в Redis, т.к. архитектура постоянно эволюционирует и запросы становятся все более дорогими.

Айфон-часть

Как мы Алкосканер делали

1. Интеграция ЯндексМапКит.

Скачиваем с гитХаба библиотечку. Запускаем пример — все отлично работает, много чего умеет.
Интегрируем в проект, используя небольшое описание на гите, как это делать. Скомпилилось и заработало все с пол-пинка. Но спустя пару недель работы с ЯК ни с того, ни с сего апп стал крашиться на добавлении аннотаций на карте. Дебаг как основное орудие поиска багов ничего не дал, поиски описаний аналогичных ситуаций в инете тоже не принесли успеха. На issue в ГитХабе до сих пор отвечают ) github.com/yandexmobile/yandexmapkit-ios/issues/95
Решили все в итоге тем, что снесли всю либу из проекта и заново заимпортили, даже код переписывать не пришлось. Все пошло вновь как по маслу, но не один час времени ушел как вода в песок, да и осадочек остался.

2. Дополнительные точки и кластеризация.

Погуглив немного и не найдя и намека на такие задачи, без особой надежды отписались в те же issues на ГитХабе с просьбой подсказать, как реализовать на ЯК кластеризацию. github.com/yandexmobile/yandexmapkit-ios/issues/109 Почти сразу кто-то из ребят, которые уже юзали ЯК, ответил, что делал такое руками. За что, засучив рукава, приступили и мы. Набросав несложный, но витиеватый алгоритм, получилось достигнуть требуемого результата — при зуме дополнительные точки превращались в полноценные баллуны, а при отдалении – вновь переходили в маленькую красную метку.

3. Построение маршрута

И снова ЯК. Хотите маршрут от точки к точке? Апи это делать не умеет. Запускайте из своего Аппа ЯндексНавигатор и передавайте туда координаты, причем строить он будет только автомобильный маршрут. Хотите использовать Яндекс.Карты для построения пешего? Используйте Андроид! Он это умеет, iOS-кит – нет.

Андроид

Как мы Алкосканер делали

1. Обмен данными с сервером

Никто и подумать не мог, что на большом объеме данных старый добрый JSON превращается в тормознутую старушку…
Первой реализацией парсера был нативный андроидовский — JSONParser, который показал себя не с лучшей стороны ещё до того, как загрузили реальный объем данных. Но когда загрузили… Получили от 15-90 сек парсинга, что было ой как не хорошо. Приэтом JSONParser съедал не так уж и мало памяти, порою заваливая приложение. Первым выходом из этого положения было увеличение «кучи» приложения, с помощью атрибута «Large heap». Но это не решало проблему со скоростью.
Гуру андрода направили на путь истинный и скинули ссылку на репозиторий github.com/johnkil/Android-JSONCompare. На графиках и в семплах была приведена потенциальная скорость парсинга, но только рок — только хардкор! Для начала взялись за JSON.simple который по реализации внутри объектов не очень отличался от нативного, позволяя использовать его на лету без значительных изменений в коде. Результат оказался удручающим, как и следовало ожидать, при значительной нагрузке в 1000+ записей, simple начинал тупить ещё похуже JSONParser. Далее выбор пал на GSON, который в свою очередь по реализации то же был очень похож на нативный, что сразу же насторожило, учитывая предыдущий провал. Но все оказалось не так плохо! 90 сек превратились максимум 30. Но и это не устраивало никого, поэтому оставался один выбор — Jackson. Парсер своеобразный, в сравнении с предыдущими, но, методом проб и ошибок, мы всё-таки заставили его работать на себя. И были в шоке.
Он показал себя даже лучше, чем во сне могло присниться. Предыдущие 30 сек канули в небытие и всплыли 3-5 сек.

2. О, эти Яндекс.Карты

Учитывая, что приложение для России, где Гугл и Эппл-карты еще очень печальны, выбор карты пал на Яндекс.карты. Для начала вот сам репозиторий github.com/yandexmobile/yandexmapkit-android. Использовать карты начал достаточно легко, но «чем глубже в лес, тем злее дятлы». Первым тормозным пунктом стала установка точек на карте, точки как и балуны — кастомные, устанавливались они прекрасно, но когда их больше 50+ на карте, начинаются значительные утечки в памяти, из-за которых начинало систематично падать приложение. Сначала мы предположили, что утечки где-то у нас в коде. Пройдясь не однократно по коду, оптимизировав картинку которую используем вместо флажка, перекодировав и сжав её, обнаружили, что ничего не изменилось… В результате, пришлось ограничиться рандомным процентом данных (каждая 3 запись) попадавших в определённый радиус вокруг центра карты. Но к этому моменту уже очень хотелось идти тестировать приложение в поле, ибо фантастика становилась далеко не научной. Дальше – спокойнее, до того момента, когда вылезли два явных и очень заметных бага: черные тайлы или же тайлы ведущие себя очень странно (https://github.com/yandexmobile/yandexmapkit-android/issues/105 ) или же спонтанные краши по нажатию на балуны(https://github.com/yandexmobile/yandexmapkit-android/issues/122). При этом всём библиотека не обновлялась уже порядком почти год, учитывая, сколько багов там было обнаружено и правятся как пользователями, так и разработчиками… В общем, вооружившись мозгами и шаманским бубном, мы заставили работать эту либу без падений (частых ), но менеджер проекта в какой-то момент начал всерьез задумываться о написании своего мапкита для Яндекс.Карт.

3. Порт на 2.2+

Для скорого преобразования было решено использовать библиотеку Sherlock. Первое её применение оставило приятный осадок на душе. Переделать приложение с 4.0 на 2.2 заняло 15 мин времени, при этом весь функционал остался не тронутый, были только небольшие проблемы со стилями, а точнее с стандартными андроидовскими отступами по стилю, но это решилось 5 мин, поиска.
А дальше – больше. Тесты на всех телефонах, которые были под рукой (2.2, 2.3) не показывают никаких проблем, тесты у друзей и знакомых, которые находятся за сотни километров – краш, краш, краш…
Здесь тесты еще продолжаются, предложения по причинам падений на всяких HTC Desire принимаются

4. Анимация

Анимация, как действие в системе, очень простой процесс, с точки зрения реализации. Но наблюдения и тест показали интересную картину. При вызове асинхронного запроса на сервер, появляется анимация, которая и с виду, и в реализации очень простая. Хоть и данные грузятся асинхронно, парсинг проходит как то странно долго, с задержкой. Получив данные, мы сразу парсим в doInBackground, а уже в onPostExecute их выводим на отображение (как и должно). Создаётся впечатление, что простая анимация сильно нагружает UI-поток, который в последствии грузит приложение. Замеры с анимацией, что анимация замедляет парсинг анных на примерно 3 секунды (SGS3). На разных устройствах это время задержки разное, что оставляет вопрос открытым и сейчас.

Данные

Вся эта работа имела смысл, только если мы сможем наполнять апп актуальной информацией о скидках в магазинах. Сейчас открою небольшой секрет: нет, мы не подсосались к какой-то секретной базе данных, как думают многие, кто пользовался приложением. Есть магазины, от которых мы получаем данные напрямую, но их пока немного. В ходе разработки процесса сбора, мы пришли к тому, что единый процесс дает результаты нестабильного качества, и что надо изучать каждую торговую сеть, и теперь у нас не один процесс, а фактически 20+ процедур сбора, по числу сетей, и эти процедуры — важная часть работы приложения. Наши методы не могут дать 100% точности, но по нашим собственным тестам, точность 80% мы имеем. Там бывают еще разные смешные проблемы, вплоть до бойцовской схватки с охранниками и блокировки сборщика в помещении, вопреки ст. 29 Конституции РФ (что подчеркивает ее авторитет в стране), но в целом мы с этими казусами справляемся, при необходимости изобретая разные хитрости.

Будем рады ответить на вопросы и уточнения в комментариях.

Автор: NightWriter

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js