5832632725_c48e011195_b (1)

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

Перевод Guide to App Architecture.

Общие проблемы, с которыми сталкиваются разработчики приложений

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

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

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

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

Суть всего в том, что компоненты вашего приложения могут запускаться индивидуально и не по порядку и могут быть уничтожены в любое время пользователем или системой. Поскольку компоненты приложения являются эфемерными, а их жизненный цикл (когда они созданы и уничтожены) не находятся под вашим контролем, вы не должны хранить данные приложения или состояние в своих компонентах приложения, а компоненты вашего приложения не должны зависеть друг от друга.

Общие архитектурные принципы

1

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

2

Второй важный принцип заключается в том, что вы должны управлять своим пользовательским интерфейсом (UI) из модели (model), предпочтительно постоянной модели. Постоянная идеально подходит по двум причинам: ваши пользователи не потеряют данные, если ОС уничтожит ваше приложение, чтобы освободить ресурсы, и ваше приложение будет продолжать работать, даже если сетевое соединение нечеткое или не подключено.

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

Модели — это компоненты, которые отвечают за обработку данных для приложения. Они независимы от компонентов Views и компонентов приложения в вашем приложении, поэтому они изолированы от проблем жизненного цикла этих компонентов. Сохранение кода UI простым и свободным от логики приложения упрощает управление. Основываясь на вашем приложении на классах моделей с четко определенной ответственностью за управление данными, они будут легко тестироваться, а ваше приложение будет согласованным.

Рекомендуемая архитектура приложений

В этом разделе мы демонстрируем, как структурировать приложение с использованием компонентов архитектуры, работая с use-case.

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

Представьте, что мы создаем пользовательский интерфейс, показывающий профиль пользователя. Этот профиль пользователя будет получен из нашего собственного бэкэнд с использованием REST API.

Строим UI

Пользовательский интерфейс будет состоять из фрагмента UserProfileFragment.java и соответствующего файла макета user_profile_layout.xml.

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

  • User ID, Идентификатор пользователя. Лучше передать эту информацию в фрагмент, используя аргументы фрагмента. Если ОС Android разрушает ваш процесс, эта информация будет сохранена, поэтому идентификатор будет доступен при следующем запуске приложения.
  • User object — POJO содержащий данные.

Мы создадим UserProfileViewModel на основе класса ViewModel, чтобы сохранить эту информацию.

ViewModel предоставляет данные для конкретного компонента пользовательского интерфейса, такие как фрагмент или активность, и обрабатывает связь с бизнес-частью обработки данных, например, вызов других компонентов для загрузки данных или перенаправления пользовательских изменений. ViewModel не знает о View и не влияет на изменения конфигурации, такие как воссоздание активности из-за вращения.

Имеем 3 файла:

  • user_profile.xml
  • UserProfileViewModel.java (extends ViewModel) — класс подготовки данных для UI
  • UserProfileFragment.java (extendsandroid.support.v4.app.Fragment) — Контроллер UI, который отображает данные в ViewModel и реагирует на взаимодействие с пользователем.

Ниже приведены наши исходные реализации (файл макета опущен для простоты):

Теперь у нас есть эти три модуля кода, как мы их связываем? В конце концов, когда поле user в ViewModel установлено, нам нужен способ информировать пользовательский интерфейс. Это место, в которое входит класс LiveData.

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

LiveData является наблюдаемым держателем данных (observable data holder). Он позволяет компонентам вашего приложения наблюдать объекты LiveData для изменений без создания явных и жестких путей зависимостей между ними. LiveData также учитывает состояние жизненного цикла ваших компонентов приложения (действия, фрагменты, службы) и делает все возможное, чтобы предотвратить утечку объекта, чтобы ваше приложение не потребляло больше памяти.

Примечание. Если вы уже используете библиотеку, такую ​​как RxJava или Agera, вы можете продолжать использовать их вместо LiveData. Но когда вы используете их или другие подходы, убедитесь, что вы правильно управляете жизненным циклом, чтобы потоки данных приостанавливались, когда связанный LifecycleOwner остановлен, и потоки уничтожаются при уничтожении LifecycleOwner. Вы также можете добавить артефакт android.arch.lifecycle: reactivestreams, чтобы использовать LiveData с другой реактивной библиотекой потоков (например, RxJava2).

Теперь мы заменяем поле User в UserProfileViewModel с помощью LiveData<User>, чтобы фрагмент мог быть проинформирован при обновлении данных. Самое замечательное в LiveData — это то, что он знает о жизненном цикле и автоматически очистит ссылки, когда они больше не нужны.

Каждый раз, когда данные пользователя обновляются, будет вызван обратный вызов Observer onChanged, и пользовательский интерфейс будет обновлен.

Если вы знакомы с другими библиотеками, где используются наблюдаемые обратные вызовы, возможно, вы поняли, что нам не нужно переопределять метод onStop(), чтобы остановить наблюдение за данными. Это не обязательно для LiveData, потому что это жизненный цикл, что означает, что он не будет вызывать обратный вызов, если фрагмент не находится в активном состоянии (получено onStart(), но не получает onStop()). LiveData также автоматически удалит наблюдателя, когда фрагмент получит onDestroy().

Мы также не делали ничего особенного для обработки изменений конфигурации (например, пользователь повернул экран). ViewModel автоматически восстанавливается при изменении конфигурации, поэтому, как только новый фрагмент оживает, он получит тот же экземпляр ViewModel, и обратный вызов будет вызываться мгновенно с текущими данными. Именно по этой причине ViewModels не должны напрямую ссылаться на Views; они могут пережить жизненный цикл View. См. Жизненный цикл ViewModel.

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