why-study-architecture_0

Продолжение.

Получение данных

Мы связали ViewModel с фрагментом, но каким образом ViewModel извлекает данные пользователя? В этом примере мы предполагаем, что наш бэкэнд предоставляет REST API. Мы будем использовать библиотеку Retrofit для доступа к нашему бэкенду, хотя вы можете использовать другую библиотеку, которая выполняет ту же задачу. Вот наш retrofit Webservice, который связывается с нашим бэкэнд:

Нативная реализация ViewModel может напрямую вызвать Webservice для извлечения данных и назначения их обратно user объекту. Несмотря на то, что это работает, ваше приложение будет трудно поддерживать по мере его роста. Это дает слишком большую ответственность классу ViewModel, который противоречит принципу разделения проблем, о которых мы говорили ранее. Кроме того, scope (область видимости) ViewModel привязана к жизненному циклу активности или фрагмента, поэтому потеря всех данных, когда его жизненный цикл завершен, является плохим пользовательским опытом. Вместо этого наш ViewModel делегирует эту работу новому модулю Repository.

Модули Repository отвечают за обработку данных. Они предоставляют чистый API для остальной части приложения. Они знают, где взять данные и какие вызовы API делать при обновлении данных. Вы можете рассматривать их как посредников между различными источниками данных (постоянная модель, веб-сервис, кэш и т.д.).

В нижеприведенном коде UserRepository класс использует WebService  для извлечения элемента данных пользователя.

Несмотря на то, что модуль Repository выглядит ненужным, он служит важной цели; он абстрагирует источники данных от остальной части приложения. Теперь наш ViewModel не знает, что данные извлекаются Webservice, что означает, что мы можем поменять его для других реализаций по мере необходимости.

Читать ещё :   Видео : Architecture Components: ViewModel

Примечание. Для простоты мы отказались от случая сетевой ошибки. Для альтернативной реализации, которая предоставляет ошибки и статус загрузки, см. Приложение: отображение статуса сети.

Управление зависимостями между компонентами

Для класса UserRepository требуется экземпляр Webservice для выполнения его работы. Он может просто создать его, но для этого ему также необходимо знать зависимости класса Webservice для его создания. Это значительно усложнит и дублирует код (например, каждому классу, которому требуется экземпляр Webservice, нужно будет знать, как его построить с его зависимостями). Кроме того, UserRepository, вероятно, не единственный класс, которому нужен Web-сервис. Если каждый класс создает новый WebService, он будет очень ресурсоемким.

Есть два шаблона, которые вы можете использовать для решения этой проблемы:

  1. Dependency Injection: позволяет классам определять свои зависимости, не создавая их. Во время выполнения другой класс отвечает за предоставление этих зависимостей. Мы рекомендуем Google Dagger 2 библиотеку для внедрения инъекций зависимостей в приложениях Android. Dagger 2 автоматически создает объекты, прогуливаясь по дереву зависимостей и предоставляя гарантии времени компиляции зависимостей.
  2. Service Locator предоставляет реестр, в котором классы могут получать свои зависимости, а не создавать их. Это относительно проще реализовать, чем Injection Dependency (DI), поэтому, если вы не знакомы с DI, вместо этого используйте Locator.

Эти шаблоны позволяют масштабировать ваш код, поскольку они обеспечивают четкие шаблоны для управления зависимостями без дублирования кода или сложности. Оба из них также позволяют выполнять замены для тестирования; что является одним из основных преимуществ их использования.

В этом примере мы будем использовать Dagger 2 для управления зависимостями.

Подключение ViewModel и Repository

Теперь мы модифицируем наш UserProfileViewModel для использования репозитория.

Кэширование данных

Вышеупомянутая реализация репозитория хороша для абстрагирования вызова веб-службы, но поскольку она использует только один источник данных, она не очень функциональна.

Проблема с вышеприведенной реализацией UserRepository заключается в том, что после извлечения данных они не хранятся нигде. Если пользователь покидает UserProfileFragment и возвращается к нему, приложение повторно запрашивает данные. Это плохо по двум причинам: оно тратит значительную пропускную способность сети и заставляет пользователя ждать завершения нового запроса. Чтобы решить эту проблему, мы добавим новый источник данных в наш UserRepository, который будет кэшировать объекты User в памяти.

Читать ещё :   Руководство по архитектуре приложений. Часть 3

Сохраняющиеся данные

В нашей текущей реализации, если пользователь поворачивает экран или уходит и возвращается в приложение, существующий пользовательский интерфейс будет отображаться мгновенно, потому что репозиторий извлекает данные из кеша в памяти. Но что произойдет, если пользователь покинет приложение и вернется через несколько часов после того, как ОС Android убьет этот процесс?

С текущей реализацией нам нужно будет снова получить данные из сети. Это не только плохой пользовательский опыт, но и расточительный, поскольку он будет использовать мобильные данные для повторной выборки одних и тех же данных. Вы можете просто исправить это, кэшируя веб-запросы, но это создает новые проблемы. Что произойдет, если одни и те же данные пользователя появятся из другого типа запроса (например, выбор списка друзей)? Тогда ваше приложение, возможно, покажет непоследовательные данные, что в лучшем случае представляет собой запутанный пользовательский интерфейс. Например, данные одного и того же пользователя могут отображаться по-разному, потому что запрос на список друзей и запрос пользователя могут выполняться в разное время. Ваше приложение должно объединить их, чтобы избежать отображения несогласованных данных.

Правильный способ справиться с этим — использовать постоянную модель. Именно здесь на помощь приходит Room.

[продолжение следует…]