Android был представлен миру в 2005 году, и за эти 12 лет существования платформа достигла удивительных успехов, став самой установленной мобильной ОС. За это время было запущено 14 различных версий операционной системы, причем Android всегда становится все более зрелым. Тем не менее, очень важная область платформы по-прежнему игнорировалась: стандартный шаблон архитектуры, способный обрабатывать особенности платформы и достаточно простой для понимания и принятия средним разработчиком.
Ну, лучше поздно, чем никогда. На последнем вводе / выводе Google команда Android наконец решила решить эту проблему и ответить на отзывы разработчиков по всему миру, объявив официальную рекомендацию по архитектуре приложений Android и предоставив строительные блоки для ее реализации: новую архитектуру Компоненты. И что еще лучше, им удалось сделать это, не ставя под угрозу открытость системы, которую мы все знаем и любим.
В этом руководстве мы рассмотрим стандартизированную архитектуру, предложенную командой Android в Google I / O, и рассмотрим основные элементы новых компонентов архитектуры: Lifecycle
, ViewModel
, LifeData
и Room
. Мы не будем уделять слишком много внимания коду, а сосредоточимся на концепции и логике этих тем. Мы также рассмотрим некоторые простые фрагменты, все они написаны с использованием Kotlin, удивительного языка, который теперь официально поддерживается Android.
1. Что пропало в Android?
Если вы только начинаете свое путешествие как разработчик, возможно, вы не знаете точно, о чем я говорю. В конце концов, архитектура приложения может быть поначалу неясной темой. Но поверьте мне, вы скоро узнаете его важность! По мере роста и усложнения приложения его архитектура становится все более важной. Это может буквально сделать вашу работу блаженством или адом жизни.
Архитектура приложений
Грубо говоря, архитектура приложения представляет собой согласованный план, который необходимо составить до начала процесса разработки. Этот план предоставляет карту того, как различные компоненты приложения должны быть организованы и связаны друг с другом. В нем представлены руководящие принципы, которые должны соблюдаться в процессе разработки, и вынуждает жертвовать собой (обычно связанные с большим количеством классов и шаблонов), которые в итоге помогут вам создать хорошо написанное приложение, которое будет более тестируемым, расширяемым и обслуживаемым.
Архитектура прикладного программного обеспечения — это процесс определения структурированного решения, отвечающего всем техническим и эксплуатационным требованиям, при оптимизации общих атрибутов качества, таких как производительность, безопасность и управляемость. Он включает ряд решений, основанных на широком спектре факторов, и каждое из этих решений может оказать значительное влияние на качество, производительность, ремонтопригодность и общий успех приложения.
— Руководство по архитектуре и дизайну программного обеспечения Microsoft
Хорошая архитектура учитывает множество факторов, особенно характеристики и ограничения системы. Существует множество архитектурных решений, каждый из которых имеет свои плюсы и минусы. Тем не менее, некоторые ключевые понятия являются общими для всех видений.
Старые ошибки
До последнего ввода-вывода Google система Android не рекомендовала какой-либо конкретной архитектуры для разработки приложений. Это означает, что вы были полностью свободны в принятии любой модели: MVP, MVC, MVPP или вообще без паттерна. Кроме того, платформа Android даже не предоставила нативных решений для проблем, создаваемых самой системой, в частности, жизненным циклом компонента.
Таким образом, если вы хотите использовать шаблон приложения View View в своем приложении, вам нужно с нуля придумать собственное решение, написать много стандартного кода или использовать библиотеку без официальной поддержки. И это отсутствие стандартов создало множество плохо написанных приложений с кодами, которые было сложно поддерживать и тестировать.
Как я уже сказал, эта ситуация критиковалась годами. На самом деле, я недавно написал об этой проблеме и о том, как ее решить, в моей серии « Как принять модель представления на Android ». Но важно то, что после 12 долгих лет команда Android наконец решила выслушать наши жалобы и помочь нам с этой проблемой.
2. Архитектура Android
Новое Руководство по архитектуре Android определяет некоторые ключевые принципы, которым должно соответствовать хорошее приложение Android, а также предлагает безопасный путь для разработчика для создания хорошего приложения. Однако в руководстве прямо указано, что представленный маршрут не является обязательным, и в конечном итоге решение является личным; Разработчик должен решить, какой тип архитектуры выбрать.
Согласно руководству, хорошее Android-приложение должно обеспечивать четкое разделение проблем и управлять пользовательским интерфейсом от модели. Любой код, который не обрабатывает пользовательский интерфейс или взаимодействие с операционной системой, не должен быть в Деятельности или Фрагменте, потому что поддержание их как можно более чистыми позволит вам избежать многих проблем, связанных с жизненным циклом. В конце концов, система может уничтожить Действия или Фрагменты в любое время. Кроме того, данные должны обрабатываться моделями, которые изолированы от пользовательского интерфейса и, следовательно, от проблем жизненного цикла.
Новая рекомендуемая архитектура
Архитектура, которую рекомендует Android, не может быть легко помечена среди стандартных шаблонов, которые мы знаем. Он выглядит как шаблон Model View Controller, но он настолько тесно связан с архитектурой системы, что трудно маркировать каждый элемент с использованием известных соглашений. Это, однако, не имеет значения, так как важно то, что он полагается на новые компоненты архитектуры, чтобы создать разделение задач с отличной тестируемостью и ремонтопригодностью. А еще лучше, это легко реализовать.
Чтобы понять, что предлагает команда Android, мы должны знать все элементы компонентов архитектуры, поскольку именно они сделают за нас тяжелую работу. Существует четыре компонента, каждый с определенной ролью: Room
, ViewModel
, LiveData
и Lifecycle
. Все эти части имеют свои собственные обязанности, и они работают вместе, чтобы создать надежную архитектуру. Давайте посмотрим на упрощенную схему предложенной архитектуры, чтобы лучше ее понять.
Как видите, у нас есть три основных элемента, каждый из которых несет ответственность.
-
Activity
иFragment
представляют слойView
, который не имеет дело с бизнес-логикой и сложными операциями. Он только настраивает представление, обрабатывает взаимодействие с пользователем и, самое главное, наблюдает иLiveData
элементыLiveData
взятые изViewModel
. -
ViewModel
автоматически наблюдает за состояниемLifecycle
представления, поддерживая согласованность во время изменений конфигурации и других событий жизненного цикла Android. Это также требуется представлением для извлечения данных изRepository
, который предоставляется как наблюдаемыеLiveData
. Важно понимать, чтоViewModel
никогда не ссылается наView
напрямую и что обновления данных всегда выполняются сущностьюLiveData
. -
Repository
не является специальным компонентом Android. Это простой класс, без какой-либо конкретной реализации, который отвечает за выборку данных из всех доступных источников, из базы данных в веб-службы. Он обрабатывает все эти данные, обычно преобразовывая их в наблюдаемыеLiveData
и делая их доступными дляViewModel
. - База данных
Room
— это библиотека отображений SQLite, которая облегчает процесс работы с базой данных. Он автоматически записывает кучу шаблонов, проверяет ошибки во время компиляции и, самое главное, может напрямую возвращать запросы с наблюдаемымиLiveData
.
Я уверен, что вы заметили, что мы много говорили о наблюдаемых. Шаблон наблюдателя является одной из основ элемента LiveData
и компонентов, LiveData
Lifecycle
. Этот шаблон позволяет объекту уведомлять список наблюдателей о любых изменениях его состояния или данных. Поэтому, когда Activity наблюдает за сущностью LiveData
, она будет получать обновления, когда эти данные претерпевают любые изменения.
Еще одна рекомендация Android — консолидировать свою архитектуру с помощью системы Dependency Injection , такой как Google Dagger 2, или с помощью шаблона Service Locator (который намного проще, чем DI, но без многих его преимуществ). Мы не будем описывать DI или Service Locator в этом учебном пособии, но Envato Tuts + имеет несколько отличных учебных пособий по этим темам. Однако имейте в виду, что есть некоторые особенности работы с компонентами Dagger 2 и Android, которые будут описаны во второй части этой серии.
3. Компоненты архитектуры
Мы должны глубоко погрузиться в аспекты новых компонентов, чтобы действительно понять и принять эту модель архитектуры. Однако мы не будем вдаваться во все детали этого урока. Из-за сложности каждого элемента в этом руководстве мы будем говорить только об общей идее каждого из них и рассмотрим некоторые упрощенные фрагменты кода. Мы постараемся охватить достаточно места, чтобы представить компоненты и начать работу. Но не бойтесь, потому что будущие статьи в этой серии будут углубляться и охватывать все особенности компонентов архитектуры.
Компоненты, поддерживающие жизненный цикл
К большинству компонентов приложения Android прикреплены жизненные циклы, которые управляются непосредственно самой системой. До недавнего времени разработчик мог отслеживать состояние компонентов и действовать соответствующим образом, инициализируя и заканчивая задачи в соответствующее время. Тем не менее, было действительно легко запутаться и сделать ошибки, связанные с этим типом операции. Но пакет android.arch.lifecycle
изменил все это.
Теперь к действиям и фрагментам прикреплен объект Lifecycle
который можно наблюдать с помощью классов LifecycleObserver
, таких как ViewModel
или любой объект, реализующий этот интерфейс. Это означает, что наблюдатель будет получать обновления об изменениях состояния объекта, который он наблюдает, например, когда действие приостановлено или когда он запускается. Он также может проверить текущее состояние наблюдаемого объекта. Так что теперь гораздо проще обрабатывать операции, которые должны учитывать жизненные циклы платформы.
На данный момент, чтобы создать действие или Fragment
который соответствует этому новому стандарту, вы должны расширить LifecycleActivity
или LifecycleFragment
. Тем не менее, возможно, что это не всегда будет необходимо, так как команда Android стремится полностью интегрировать эти новые инструменты в свою среду.
1
2
3
4
5
6
7
|
class MainActivity : LifecycleActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
|
LifecycleObserver
получает события LifecycleObserver
Lifecycle
и может реагировать с помощью аннотации. Переопределение метода не требуется.
01
02
03
04
05
06
07
08
09
10
11
|
class MainActivityObserver : LifecycleObserver, AnkoLogger {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
info(«onResume»)
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
info(«onPause»)
}
}
|
Компонент LiveData
Компонент LiveData
представляет собой держатель данных, который содержит значение, которое можно наблюдать. Учитывая, что наблюдатель предоставил Lifecycle
во время LiveData
экземпляра LiveData
, LiveData
будет вести себя в соответствии с состоянием Lifecycle
. Если состояние Lifecycle
наблюдателя RESUMED
или RESUMED
, наблюдатель active
; в противном случае он inactive
.
LiveData
знает, когда данные были изменены, а также active
ли наблюдатель и должен получить обновление. Еще одна интересная характеристика LiveData
заключается в том, что он способен удалять наблюдателя, если он находится в состоянии Lifecycle.State.DESTROYED
, избегая утечек памяти при наблюдении операций и фрагментов.
LiveData
должен реализовывать onActive
и onInactive
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
class LocationLiveData(context: Context)
: LiveData<Location>(), AnkoLogger, LocationListener {
private val locationManager: LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
override fun onActive() {
info(«onActive»)
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, this)
}
override fun onInactive() {
info(«onInactive»)
locationManager.removeUpdates(this)
}
// ….
}
|
Чтобы наблюдать компонент LiveData
, вы должны вызвать observer(LifecycleOwner, Observer<T>)
.
1
2
3
4
5
6
7
8
9
|
class MainActivity : LifecycleActivity(), AnkoLogger {
fun observeLocation() {
val location = LocationLiveData(this)
location.observe(this,
Observer { location ->
info(«location: $location»)
})
}
}
|
Компонент ViewModel
Одним из наиболее важных классов новых компонентов архитектуры является ViewModel
, который предназначен для хранения данных, связанных с пользовательским интерфейсом, сохраняя их целостность во время изменений конфигурации, таких как поворот экрана. ViewModel
может LiveData
с Repository
, получая из него LiveData
и делая его доступным, чтобы его можно было наблюдать в представлении. ViewModel
также не нужно будет делать новые вызовы в Repository
после изменений конфигурации, что значительно оптимизирует код.
Чтобы создать модель представления, расширьте класс ViewModel
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
class MainActivityViewModel : ViewModel() {
private var notes: MutableLiveData<List<String>>?
fun getNotes(): LiveData<List<String>> {
if (notes == null) {
notes = MutableLiveData<List<String>>()
loadNotes()
}
return notes!!
}
private fun loadNotes() {
// do async operation to fetch notes
}
}
|
Для доступа из представления вы можете вызвать ViewProviders.of(Activity|Fragment).get(ViewModel::class)
. Этот фабричный метод возвратит новый экземпляр ViewModel
или получит сохраненный, в зависимости от ситуации.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
class MainActivity : LifecycleActivity(), AnkoLogger {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProviders.of(this)
.get(MainActivityViewModel::class.java)
viewModel.getNotes().observe(
this, Observer {
notes -> info(«notes: $notes»)
}
)
}
}
|
Компонент Room
Android поддерживал SQLite с самого начала; однако, чтобы это работало, всегда нужно было писать много шаблонов. Кроме того, SQLite не сохранял POJO (простые старые объекты Java) и не проверял запросы во время компиляции. А вот и Room
для решения этих проблем! Это библиотека отображения SQLite, способная сохранять Java POJO, напрямую преобразовывать запросы в объекты, проверять ошибки во время компиляции и создавать наблюдаемые LiveData
из результатов запроса. Room
— это библиотека объектно-реляционного сопоставления с некоторыми интересными дополнениями для Android.
До сих пор вы могли делать большую часть того, на что способен Room
, используя другие библиотеки ORM Android. Тем не менее, ни один из них официально не поддерживается, и, самое главное, они не могут давать результаты LifeData
. Библиотека Room
идеально подходит в качестве постоянного слоя в предложенной архитектуре Android.
Чтобы создать базу данных Room
, вам потребуется @Entity
для сохранения, которым может быть любой Java POJO, интерфейс @Dao
для выполнения запросов и операций ввода / вывода, а также абстрактный класс @Database
который должен расширять RoomDatabase
.
1
2
3
4
5
6
7
|
@Entity
class Note {
@PrimaryKey
var id: Long?
var text: String?
var date: Long?
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@Dao
interface NoteDAO {
@Insert( onConflict = OnConflictStrategy.REPLACE )
fun insertNote(note: Note): Long
@Update( onConflict = OnConflictStrategy.REPLACE )
fun updateNote(note: Note): Int
@Delete
fun deleteNote(note: Note): Int
@Query(«SELECT * FROM note»)
fun findAllNotes(): LiveData<Note>
// on Kotlin the query arguments are renamed
// to arg[N], being N the argument number.
// on Java the arguments assume its original name
@Query(«SELECT * FROM note WHERE id = :arg0»)
fun findNoteById(id: Long): LiveData<Note>
}
|
1
2
3
4
|
@Database( entities = arrayOf(Note::class), version = 1)
abstract class Databse : RoomDatabase() {
abstract fun noteDAO(): NoteDAO
}
|
Добавление компонентов архитектуры в ваш проект
На данный момент, чтобы использовать новые архитектурные компоненты, вам необходимо сначала добавить репозиторий Google в файл build.gradle
. Для получения более подробной информации смотрите официальное руководство .
1
2
3
4
5
6
7
|
allprojects {
repositories {
jcenter()
// Add Google repository
maven { url ‘https://maven.google.com’ }
}
}
|
Вывод
Как видите, стандартная архитектура, предложенная Android, включает в себя множество концепций. Не ожидайте полного понимания этой темы. В конце концов, мы просто вводим тему. Но у вас уже достаточно знаний, чтобы понять логику архитектуры и роли различных компонентов архитектуры.
Мы говорили о большинстве тем, связанных с предлагаемой архитектурой Android и ее компонентами; однако подробности о реализации Компонентов и некоторых дополнительных функциях, таких как класс Repository
и система Dagger 2, не могут быть рассмотрены в этой первой части. Мы рассмотрим эти темы в следующих постах.
До скорого!