Рассказ о том, как удалось портировать сервис машинного перевода на мобильное устройство, какие сопутствующие задачи пришлось решить и какие грабли поджидали на пути. Обсудим также, насколько современные мобильные приложения зависимы от наличия сети и как проектировать архитектуру так, чтобы в будущем не набить шишек при добавлении офлайн-режима.
4. Web of Apps
• Приложения стали практически
неразделимы от веба, стали
"представлением" веб-сущности на
устройстве.
• Разработчики подчас строят бизнес-
логику вокруг получения данных от веб-
сервиса.
5. Где нужен офлайн?
• В путешествиях. Например, перевод
больше всего нужен там, где обычно
недоступен интернет.
• При плохом подключении (метро).
• Для экономии трафика.
6. Я.Перевод
• 16 марта 2011 – бета-версия онлайн-
сервиса Яндекс.Перевод.
• В декабре 2012 – первое мобильное
приложение – для iPhone, спустя полгода
Android, спустя год Windows Phone.
• Весна-лето 2014 – версия 2.0 для iOS с
офлайн-переводом.
10. Что нужно было
сделать
• Сжать/урезать переводные модели и
измерить качество.
• Портировать серверный код на
устройство.
• Зарефакторить приложение.
11. Что было
• Система, спроектированная в краткие
сроки для работы в онлайн-режиме.
• Довольно высокая связность.
• Накопившиеся поправки для логики
работы приложения.
12. Как рефакторили
• Четко определили и описали
проблемную область.
• Понизили связность системы (помогла
абстракция над сервисами).
• Определили, описали и реализовали
юзкейсы, обложили их тестами.
13. Проблемная область
YTR Domain
LanguagePair
Translation
Prediction
Dictionary
Errors
Business
Logic
<I> DAL
16. Проектирование
логики
• Нужно было объединить и описать в
спецификациях все юзкейсы.
• Был объявлен базовый класс,
представляющий составной запрос
(гибрид Strategy и Command).
• Написаны тесты на конкретные
реализации этого класса.
17. Юзкейсы
• Fail-over: переход к офлайн-модели, если
веб-сервис недоступен.
• Логика синхронного перевода.
• "Сборка" ответов разных сервисов в
составной ответ.
18. Загрузчик
• Один из самых важных и сложных
компонентов – загрузчик.
• Две реализации: для iOS 6 и iOS 7,
фоновая загрузка на iOS 7.
• Оказался многофакторным
компонентом.
19. Выводы
• Построенная вокруг концепции
абстрактных сервисов архитектура
оказалась самым подходящим решением.
• Нельзя недооценивать сложность
сопутствующих задач (загрузчик).
21. Грабли: NSURLSession
• Сетевые задачи, созданные в фоне,
всегда создаются с флагом
discretionary
• Такие задачи тормозят и перестают
работать если у устройства осталось
мало заряда. На это жалуются
пользователи ;)
22. Грабли: NSURLSession
NSDictionary *userInfo = @{ @"background" : @(self.context.isBackground) };
!
// Сериализуем в JSON
NSData *data = [NSJSONSerialization dataWithJSONObject:userInfo
options:0
error:nil];
NSString *description = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
!
// Прописываем в taskDescription
sessionTask.taskDescription = description;
Можно хранить метаданные в
-[NSURLSessionTask taskDescription]
и при входе в приложение пересоздавать
discretionary-задачи
23. Грабли: NSURLSession
• NSURLSession не умеет возобновлять
загрузки из Content Delivery Network.
• Под капотом – If-Modified-Since и ETag.
• Если CDN отдает ETag, то возобновление
загрузок сломается (каждый сервер
отдает свой ETag).
24. Грабли: mmap
• Переводные модели состоят из
нескольких файлов, каждый из которых
отображается в память.
• Для перевода в одном направлении
нужно ~270 МБ виртуального адресного
пространства.
25. Грабли: mmap
File = fopen(path.c_str(), "rb");
fseek(File, 0, SEEK_END);
Size = static_cast<size_t>(ftell(File));
fseek(File, 0, SEEK_SET);
Data = mmap(0, Size, PROT_READ, MAP_SHARED, fileno(File), 0);
Объем виртуального адресного пространства
на iOS ограничен, mmap может вернуть
MAP_FAILED, если она занята другими
процессами или недоступна одним чанком.
26. Грабли: mmap
• На iPhone 4 / 4S система выделяет около
700 МБ пространства на девайс.
• Оптимизация: пришлось пожертовать
скоростью отклика при переключении
между направлениями.
27. Грабли: донести фичу
до пользователей
Пользователи сами не
вникнут в сложный
функционал. Нужно
как можно больше
учить и объяснять.
28. Важно: фидбек внутри
приложения
Некоторые
пользователи любят
жаловаться.
Встроенная форма
фидбека поможет
быстрее разбираться
с багами.
29. С чем мы столкнулись
• NSURLSession не дружит с CDN.
• NSURLSession оптимизируется системой.
• Ограничения виртуальной памяти.
• Про фичи нужно не только рассказывать,
их нужно показывать.
• In-App фидбек – очень удачное решение.
30. Открытые вопросы
(обсудим вместе?)
• Как грамотно собирать статистику по
User Experience в гибридном
приложении?
• Как лучше всего осуществлять переход
между онлайн и офлайн-режимом?
Ручной и автоматический подходы.