Многие мобильные приложения показывают пользователю контент с сервера, и этот контент можно показывать в онлайне и оффлайне. Работа в онлайне тривиальна — при определенном UI событии, приложение читает данные с сети и показывает их пользователю. Работа в оффлайне может быть гораздо интереснее — возможность работы с документами в метро и тп. Но работа вне сети приносит и проблемы: теперь необходимо проводить синхронизацию данных и этот процесс не должен блокировать интерфейс пользователя.
— доступ к сетевым ресурсам
— обработка xml
— доступ к файловой системе
— работа с потоками
Приложение должно показывать текстовые файлы с сервера в режиме оффлаин. При каждом старте происходит синхронизация. При этом процесс обновления не должен блокировать работу пользователя с интерфейсом. Сам процесс обновления состоит из двух шагов:
1. Чтение списка файлов с сервера
2. Загрузка отсутствующих файлов
Для управления всем процессом мы создадим класс UpdateManager, который будет управлять объектами «Updaters». На данный момент нам надо два «Updater'а»: один для чтения списка файлов и второй для работы с файлами. Для них определим единый фасад, что позволит расширять систему в будущем. Этот фасад будет иметь как минимум один метод — start — который будет вызваться UpdateManager'ом для каждого Updater'а по-очереди.
Мы заранее знаем, что будем использовать асинхронное соединение для доступа к сети. Это вынуждает нас явно продолжать работу UpdateManager'а после завершения работы каждого Updater'а.
Объявим два протокола:
@protocol UpdateManagerProtocol -(void)next;
end
@protocol UpdaterProtocol -(void)startUpdate:(id ) manager;
end
UpdateManagerProtocol объявляет один метод, который вызывается каждым Updater'ом по завершению работы.
Наши классы выглядит так:

Все Updater'ы работают одинаково:

XMLListUpdater выполняет шаги:
1. Читает xml файл с сервера в буфер
2. Разбирает xml
3. Добавляет каждый файл в очередь
FileUpdater выполняет шаги:
1. Получает следующий файл из очереди
2. Проверяет, если файл уже существует на диске
3. Скачивает файл
4. Повторяе�� процесс, если очередь не пуста
Для начала напишем код, без упоминания потоков.
UpdateManager.h объявляет один статический метод для старта всего процесса. В конструкторе (init) инстанса происходит создание всех Updater'ов, добавление их в очередь и вызов одного за другим.
Так как каждый Updater читает данные с сети, то общий код можно вынести в отдельный класс — NetworkClient. Он имплементирует UpdaterProtocol вместе с методом для запуска асинхронного соединения (startNetworkCall).
Первый Updater — XMLFileUpdater. При старте, он читает xml в память с заранее известного адреса. По завершению, XMLListUpdater создает xml парсер для обработки данных. Каждый файл из списка добавляется в очередь для обработки следующим Updater'ом.
Второй шаг обновления контента FilesUpdater — он должен прочитать очередь и скачать каждый отсутствующий файл.
Теперь мы можем стартовать процесс UpdateManager, при загрузке главного view — и приложение синхронизирует контент.
View содержит только одну кнопку, без каких либо действий. При обновлении контента интерфейс будет блокироваться и нажатие на кнопку это выявит. Позже мы избавимся от блокировки добавив новый поток.
Так как у нас уже есть весь код работы с данными, то нам остается запустить отдельный поток и в нем выполнить обновление.
Добавим новый метод в UpdateManager — startInThread. С простыми шагами:
1. Создать NSAutoReleasePool
2. Запустить процесс обновления
3. Запустить RunLoop
4. Освободить pool
NSAutoRelease необходимо создавать в каждом новом потоке, для автоматического управления памятью. Иначе вы получите массу ошибок в консоли.
RunLoop более интересная штука. Если закомментировать RunLoop и запустить приложение, то вы увидите сообщение о начале сетевого соединения, но остальные события — как прием данных из сети, завершение соединения — не произойдут. Проблема в раннем завершении потока — который заканчивается при выходе из метода «startInThread». Поэтому мы запускаем RunLoop для того чтобы поток оставался активным.
Теперь инициализацию UpdateManager можно передвинуть в main.m.
UpdateManager.h содержит директивы компиляции — WORK_IN_SEPARATE_THREAD. Если она установлена в ноль, то новый поток не будет создаваться и UI будет блокироваться. При единице, обновление будет происходить в отдельном потоке
Исходный текст проекта: SF.net
Andrew Romanenco
andrew@romanenco.com
April, 2010
Элементы в исходном коде
— доступ к сетевым ресурсам
— обработка xml
— доступ к файловой системе
— работа с потоками
Требования к приложению
Приложение должно показывать текстовые файлы с сервера в режиме оффлаин. При каждом старте происходит синхронизация. При этом процесс обновления не должен блокировать работу пользователя с интерфейсом. Сам процесс обновления состоит из двух шагов:
1. Чтение списка файлов с сервера
2. Загрузка отсутствующих файлов
Дизайн кода
Для управления всем процессом мы создадим класс UpdateManager, который будет управлять объектами «Updaters». На данный момент нам надо два «Updater'а»: один для чтения списка файлов и второй для работы с файлами. Для них определим единый фасад, что позволит расширять систему в будущем. Этот фасад будет иметь как минимум один метод — start — который будет вызваться UpdateManager'ом для каждого Updater'а по-очереди.
Мы заранее знаем, что будем использовать асинхронное соединение для доступа к сети. Это вынуждает нас явно продолжать работу UpdateManager'а после завершения работы каждого Updater'а.
Объявим два протокола:
@protocol UpdateManagerProtocol -(void)next;
end
@protocol UpdaterProtocol -(void)startUpdate:(id ) manager;
end
UpdateManagerProtocol объявляет один метод, который вызывается каждым Updater'ом по завершению работы.
Наши классы выглядит так:

Все Updater'ы работают одинаково:

XMLListUpdater выполняет шаги:
1. Читает xml файл с сервера в буфер
2. Разбирает xml
3. Добавляет каждый файл в очередь
FileUpdater выполняет шаги:
1. Получает следующий файл из очереди
2. Проверяет, если файл уже существует на диске
3. Скачивает файл
4. Повторяе�� процесс, если очередь не пуста
Исходный код
Для начала напишем код, без упоминания потоков.
UpdateManager.h объявляет один статический метод для старта всего процесса. В конструкторе (init) инстанса происходит создание всех Updater'ов, добавление их в очередь и вызов одного за другим.
Так как каждый Updater читает данные с сети, то общий код можно вынести в отдельный класс — NetworkClient. Он имплементирует UpdaterProtocol вместе с методом для запуска асинхронного соединения (startNetworkCall).
Первый Updater — XMLFileUpdater. При старте, он читает xml в память с заранее известного адреса. По завершению, XMLListUpdater создает xml парсер для обработки данных. Каждый файл из списка добавляется в очередь для обработки следующим Updater'ом.
Второй шаг обновления контента FilesUpdater — он должен прочитать очередь и скачать каждый отсутствующий файл.
Теперь мы можем стартовать процесс UpdateManager, при загрузке главного view — и приложение синхронизирует контент.
View содержит только одну кнопку, без каких либо действий. При обновлении контента интерфейс будет блокироваться и нажатие на кнопку это выявит. Позже мы избавимся от блокировки добавив новый поток.
Добавление отдельного потока
Так как у нас уже есть весь код работы с данными, то нам остается запустить отдельный поток и в нем выполнить обновление.
Добавим новый метод в UpdateManager — startInThread. С простыми шагами:
1. Создать NSAutoReleasePool
2. Запустить процесс обновления
3. Запустить RunLoop
4. Освободить pool
NSAutoRelease необходимо создавать в каждом новом потоке, для автоматического управления памятью. Иначе вы получите массу ошибок в консоли.
RunLoop более интересная штука. Если закомментировать RunLoop и запустить приложение, то вы увидите сообщение о начале сетевого соединения, но остальные события — как прием данных из сети, завершение соединения — не произойдут. Проблема в раннем завершении потока — который заканчивается при выходе из метода «startInThread». Поэтому мы запускаем RunLoop для того чтобы поток оставался активным.
Теперь инициализацию UpdateManager можно передвинуть в main.m.
Замечания по исходному коду
UpdateManager.h содержит директивы компиляции — WORK_IN_SEPARATE_THREAD. Если она установлена в ноль, то новый поток не будет создаваться и UI будет блокироваться. При единице, обновление будет происходить в отдельном потоке
Исходный текст проекта: SF.net
Andrew Romanenco
andrew@romanenco.com
April, 2010