Как обрабатывать вызовы REST, сохранение данных, синхронизацию и наблюдение за ContentProvider

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

Я собираюсь разработать приложение, которое использует веб-службу RESTful и должно соответствовать следующим требованиям:

  • приложение должно показывать некоторые книги, их авторов и их редакторов в списках и подробно

  • приложение также должно позволять искать книгу

  • книги, авторы и редакторы извлекаются из веб-службы RESTful

  • каждый объект должен быть кэширован, чтобы при открытии действия я сначала видел старые данные (если они есть), а новые обновляются из сети.

  • каждый раз, когда объект обновляется, заинтересованные стороны должны быть уведомлены (ContentObserver? Обычная Listener реализация?)

  • если вызов уже выполняется (скажем, к api/books/1337 или к api/editors), вызывающая сторона должна быть уведомлена о том, что она загружает данные, и должна быть передана старая (если она существует), как если бы это была исходная вызывающая сторона.

  • некоторые данные (только книги и авторы) должны обновляться каждые N минут (определяется пользователем) и должны быть уведомлены наблюдатели (SyncAdapter?)

Вопросы:

После просмотра и изучения всех компонентов, предложенных Вирджилом. Добьянски на Google I/O 2010 вот мои сомнения:

  1. Как я могу прозрачно обрабатывать концепцию «обновляемая сущность» для любого вызывающего объекта? Должен ли я использовать ContentObserver в ContentProvider, который мне придется реализовать?

  2. Если я использую ContentObserver, я могу легко установить флаг состояния для одного объекта (как предложил Добьянски), например, UPDATING, INSERTING и т. д. Но как мне обрабатывать список? Скажем, мне нужен список книг, куда мне поставить флаг статуса? Должен ли я поместить его в таблицу состояния только для списков? Если это так, я должен соблюдать два Cursor, один для статуса и один для фактического списка (т. е. URI таблицы/контента). А что, если объект, который я запрашиваю, не существует (пока) или вызов REST возвращает 404? Как обработать обратный вызов?

  3. Если я помещу все свои методы REST в **SyncAdapter**, могу ли я «заставить» SyncAdapter обновить список объектов/сущностей из сети (и, следовательно, поместить его в соответствующую таблицу)? Таким образом, флаг состояния был бы полезен.

  4. Может ли SyncAdapter работать с несколькими сущностями (на самом деле, со списками сущностей, так как я хочу время от времени обновлять книги и редакторы), поскольку у него есть только метод performSync?

  5. Если моя реализация SyncAdapter была отключена пользователем в настройках устройства, она ничего не обновит (и это нормально). Но если пользователь нажимает кнопку «обновить книги» в действии, могу ли я по-прежнему вызывать метод performSync или он также будет отключен?


person frapontillo    schedule 22.06.2012    source источник


Ответы (2)


SyncAdapter — это шаблон проектирования, включающий пять компонентов:

  1. Приложение. Это использует набор Activity вместе с Cursor и ContentObserver и, возможно, CursorAdapter и некоторыми, чтобы предоставить пользовательский интерфейс для локально сохраненных данных из ContentProvider.
  2. ContentProvider Хранилище данных локального устройства. Обрабатывает вызовы CRUD, обрабатывает уведомление SyncAdapter о необходимости отправить обновление на сервер.
  3. Account Идентификатор пользователя на удаленном сервере.
  4. SyncAdapter Фоновый процесс, который работает и обеспечивает синхронизацию локального хранилища данных с сервером.
  5. Сам сервер.

Так. На вопросы:

  1. «Обновление» означает «имеет локальные изменения, которые еще не были отправлены на сервер. Это флаг, который вы устанавливаете для строки в своей базе данных. Он устанавливается в ContentProvider при создании/обновлении/удалении строки. Когда SyncAdapter запускается , он видит флаг, отправляет обновление на сервер, сбрасывает флаг. Сам флаг выполняет две функции:
    а. Сообщает пользователю, что приложение занято сохранением изменения, и когда это done.
    b. Помечает строку как измененную, чтобы SyncAdapter знал, что нужно отправить ее на сервер.
    Подробнее читайте здесь.

  2. Если вы не синхронизируете весь каталог, ваш клиент будет напрямую запрашивать сервер и кэшировать результаты, помещая их в ContentProvider. Там нет флага состояния, так как они поступают с сервера и, следовательно, соответствуют состоянию сервера. Напишите свой SyncAdapter, чтобы игнорировать их или, возможно, отбросить их после того, как они были кэшированы в течение нескольких дней.

  3. а. Чтобы убедиться, что ваши локальные обновления отправляются на сервер, вы пишете свой ContentProvider для уведомления SyncAdapter во время вызовов ContentProvider Create/Update/Delete. (Читайте здесь... )
    b. Чтобы периодически получать обновления с сервера, вы настраиваете учетную запись для автоматической синхронизации. (Читать здесь...)

  4. да. PerformSync — это просто вызов функции. Напишите это, чтобы сделать то, что вы хотите. Попросите его получить таблицу 1 с сервера и поместить ее в одну таблицу в вашем ContentProvider. Затем пусть он извлечет таблицу 2 и поместит ее в другую таблицу. И Т. Д.

  5. а. Вы можете принудительно выполнить синхронизацию, вызвав ContentResolver.RequestSync() с ContentResolver.SYNC_EXTRAS_MANUAL в пакете дополнительных услуг.
    б. Вы можете вручную получить что-то с помощью клиентского кода и напрямую отправить его в Поставщик услуг.

person jcwenger    schedule 18.09.2012
comment
Большое спасибо за ваш ценный ответ. У меня все еще нет правильного списка: скажем, пользователь нажимает кнопку «Обновить». Что должно получить обновленный список (возможно, с использованием параметра since)? ContentProvider или настраиваемый сервис? И если пользователь выходит из Activity и снова входит в него, когда выборка еще не завершена, как я могу узнать, что некоторые объекты обновляются? Я думал, что смогу использовать столбец статуса, чтобы получить и установить статус для любого события: получить, удалить и так далее. - person frapontillo; 18.09.2012
comment
ContentProvider НИКОГДА не будет получать данные для обновления. Это просто ваш локальный поставщик хранилища. У вас есть два варианта. 1. Вы можете создать таблицу в своем ContentProvider для запросов на обновление, а затем заставить SyncAdapter прочитать эту таблицу, чтобы принудительно извлечь, а затем, получив результаты, отправить их в ContentProvider. Второй вариант: ваше приложение само порождает фоновый поток, извлекает данные и помещает их в ContentProvider. Создавайте свои классы выборки и анализа данных изолированно и сделайте так, чтобы и SyncAdapter, и приложение вызывали их. В любом случае вы можете пометить строку как обновляемую. - person jcwenger; 25.09.2012
comment
Спасибо. На данный момент это лучший ответ, который я когда-либо получал на StackOverflow. - person frapontillo; 28.09.2012
comment
Какие-либо демонстрационные приложения, доступные на github, объясняют вышеупомянутые передовые методы создания эффективных мобильных приложений? В настоящее время я реконструирую приложение Google I/O! - person LOG_TAG; 15.08.2013