Pull to refresh

Обновление контента IPhone приложения

Reading time 3 min
Views 11K
Многие мобильные приложения показывают пользователю контент с сервера, и этот контент можно показывать в онлайне и оффлайне. Работа в онлайне тривиальна — при определенном 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'ом по завершению работы.

Наши классы выглядит так:
Class Diagram

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

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
Tags:
Hubs:
+25
Comments 8
Comments Comments 8

Articles