6. И в самых неожиданных
местах
• На Северном полюсе
• На вершине горы Эверест
• В пустынях ОАЭ
• На МКС
7. Вроде бы все прекрасно,
все счастливы и можно
не париться
8. К сожалению, реальность
такова:
• 3G доступен только в больших городах, покрытие не 100%
• 3G не везде безлимитный, порой очень дорогой
• В роуминге цены на 3G достигают безумных цифр
• Интернет в самолетах стоит примерно ~$20 за 10 Mб, средняя
скорость ~0.05 Мбит/сек
• В метро связь доступна только на станциях и то не на всех
• Даже домашний интернет могут отключить, если забыл оплатить :)
14. Итого
• Пользователю все равно: находится он в
онлайне или нет, доступна сейчас сеть или
пропала
• Пользователи расстраиваются если
приложение перестает функционировать в
отсутствии интернета
• При дальних перелетах/поездках/в метро у
людей полно свободного времени, но нет
интернета — это НУЖНО использовать,
особенно в образовательных приложениях, где
львиную долю функций можно реализовать без
необходиомости постоянного доступа к сети
15. Есть решение!
• Кеширование медиа-файлов
• Хранение всех пользовательских
данных в локальной БД
• Сохранение действий пользователя,
произведенных в оффлайне
• Синхронизация с сервером при
появлении доступа к сети
17. Кеши бывают разные
Требования
1. Возможность хранения данных
не только в памяти, но и на
диске
2. Поддержка политик
(изменяемое поведение для
разных типов запросов)
3. Строгое следование заголовкам
HTTP, обозначающим правила
кеширования и перевалидации
(Cache-Control, Expires, Last-
Modified)
4. Возможность отправки
conditional GET
HTTP code: 200
Age: 0
Via: 1.1 varnish
X-Varnish: 1103200550
Accept-Ranges: bytes, bytes
Last-Modified: Tue, 11 Sep 2012 14:01:07 GMT
Content-Type: audio/mpeg
Connection: keep-alive
Expires: Tue, 26 Mar 2013 18:32:41 GMT
Server: nginx/1.2.7
Content-Length: 4752
Date: Tue, 19 Mar 2013 18:32:41 GMT
Cache-Control: max-age=604800
18. Что кешировать?
• НЕ нужно кешировать всё - долго и
занимает много места на диске
• Данные, без которых оффлайн работа
будет невозможна
• Опциональные наборы данных лучше
кешировать по запросу пользователя
19. Политики кеширования
•Политика определяет поведение при наличии закешированных данных
и при ошибке
•Оффлайн режим для разработчика по сути представляет собой
выполнение всех запросов с ошибкой
•Для запросов данных, которые предполагается кешировать для
оффлайн работы, важно указать именно политику при ошибке (напр. в
ASIHTTPRequest для iOS это ASIFallbackToCacheIfLoadFailsCachePolicy)
•В оффлайне лучше показать устаревшие данные, чем ошибку
20. Итого
• Выбираем правильную библиотеку для
кеширования, соответствующую
описанным требованиям
• Выставляем правильные политики для
кешируемых запросов
• Реализуем выборочное кеширование и
по запросу пользователя
22. Я знаю, что вы сделали
прошлым летом
Пока пользователь находится
вне зоны доступа, кроме
использования закешированных
данных важно хранить историю
его действий для последующей
синхронизации с сервером
Для этого нужно понятное,
автоматизированное и
прозрачное решение, которое не
требует значительных
изменений в коде
23. Не нужно изобретать
велосипед
Можно использовать принцип, аналогичный триггерам в
транзакционных SQL или хукам вVCS (post commit):
Пользователь
совершил
действие
Изменился
объект в БД
Произошел
commit в базу
Сработал
post-commit
hook
Изменение
сохранилось в лог
24. • Доступ к данным осуществляется исключительно через Data Access
Objects (DAO), полностью инкапсулирующих в себе работу с БД
• Через proxy-классы перехватываются методы DAO для добавления/
удаления/изменения объектов и устанавливаются transaction
listener-ы в качестве post-commit хуков (триггеров)
• Сразу после коммита в БД вызываются transaction listener-ы и
сохраняют изменения в БД в виде записей транзакционного лога,
содержащие тип операции (добавление/удаление/изменение),
таймштамп, сам измененный объект и вспомогательные данные
• Взаимоисключающие и дополняющие операции объединяются для
компактности данных
Реализация: как это сделано
в LinguaLeo
25. Реализация: DAO и
вложенные транзакции
В каждом объекте DAO имеется экземпляр объекта транзакции
В основе объекта транзакции лежит счетчик открытых и закрытых
транзакций (аналогично счетчику ссылок retainCount у NSObject в
Objective-C)
При выполнении [transaction begin] счетчик увеличивается
При выполнении [transaction commit] счетчик уменьшается
Как только счетчик стал равен нулю, выполняется реальный commit в
базу данных и сразу после выполняется метод didCommitTransaction у
всех transaction listener-ов
27. Пример: proxy-класс для
DAO
@implementation LLAOPLoggingDAOProxy
+ (id)newProxyForWordDAO:(id <LLWordDAO>)wordDAO {
id result = [[[LLAOPLoggingDAOProxy alloc] initWithInstance:wordDAO] autorelease];
[result interceptMethodEndForSelector:@selector(insertWordsToUserDictionary:)
interceptorSelector:@selector(logAddingNewWords:)];
[result interceptMethodEndForSelector:@selector(insertWordsFromGlossaryToUserDictionary:)
interceptorSelector:@selector(logAddingNewWordFromSimpleWords:)];
[result interceptMethodStartForSelector:@selector(updateWord:)
interceptorSelector:@selector(logUpdatingWord:)];
[result interceptMethodStartForSelector:@selector(updateWords:)
interceptorSelector:@selector(logUpdatingWords:)];
[result interceptMethodStartForSelector:@selector(deleteWordsFromUserDictionary:)
interceptorSelector:@selector(logDeletingWords:)];
[result interceptMethodStartForSelector:@selector(updateSimpleWords:asKnown:)
interceptorSelector:@selector(logUpdatingSimpleWord:)];
return result;
}
@end
28. Пример: транзакционный лог
В итоге лог выглядит примерно так:
[
{
'data': { ... },//поля объекта класса LLBonus
'entity_type':1,//тип сущности (LLBonus)
'operation':0,//тип операции (0 - вставка, 1 - обновление, 2 - удаление)
'timestamp':'2013-02-20T10:33:46+0200'//таймштамп
},
{
'data': { ... },
'entity_type':0,//LLWord
'operation':1,//обновить
'timestamp':'2013-02-21T11:25:40+0200'
}
...
]
29. Итого
• Всю работу с БД осуществляем в
DAO
• Сохраняем все действия
пользователя в лог не смотря на
наличие/отсутствие сети
• Стараемся сделать лог компактным,
объединяя записи
31. Да будет сеть!
Как только появляется доступ к сети,
нужно синхронизовать данные с
сервером:
• отправить на сервер сохраненные
операции
• принять операции от сервера,
совершенные на сайте и других
устройствах
• данных могло накопиться много -
нужно упаковать данные, чтобы
синхронизация прошла быстро и не
"съела" много траффика
32. Проблемы синхронизации
У пользователя может быть несколько
устройств
Какие-то из них могут находиться в
оффлайне
В них будет накапливаться история
операций на основе устаревших
данных
При выходе этих устройств в сеть
могут возникать конфликты
34. Принцип VCS для
синхронизации
Это самый обычный кейс для систем
контроля версий вроде SVN или Git
Все решается вводом численного
обозначения ревизии данных (или хеша),
которое контролирует сервер
При синхронизации:
Если на устройстве устаревшая версия
данных, сначала накатываются и мержатся
изменения с сервера (svn up/git pull) и
только затем делается аплоад накопившихся
операций на сервер (svn commit/git push)
36. Итого
• Регулярно делаем синхронизацию,
чтобы минимизировать объем
отправляемых данных за раз
• Применяем методы синхронизации
из систем контроля версий
• При неудачной синхронизации
делаем rollback