Статьи

Компоненты архитектуры Android: жизненный цикл и LiveModel

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

В этом руководстве мы LiveModel компоненты Lifecycle и LiveModel . Пока мы их изучаем, мы также собираемся проверить некоторые фрагменты кода из примера приложения. Поскольку мы говорим о новых парадигмах Android, все фрагменты кода сделаны с использованием потрясающего Kotlin .

Если вы еще не знаете Kotlin, пожалуйста, не бойтесь следовать; реализация очень близка к Java, и я уверен, что вы сможете это понять. Если вы хотите узнать больше о Kotlin, Джессика Торнсби написала отличную серию здесь на Tuts + о кодировании приложений Android в Kotlin . Вы должны взглянуть!

  • Создание функциональных приложений для Android в Kotlin: начало работы

  • Функциональные приложения для Android в Котлине: Lambdas, Null Safety и многое другое

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

Образец заявки

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

Приложение Weather

Приложение максимально простое. Он имеет только одну активность, где пользователь может узнать погоду, выполнив поиск по названию города или используя текущее местоположение устройства. MainActivity вызывает MainModel для получения наблюдаемых LiveData и реагирует на них. MainModel извлекает данные о погоде из MainRepository и объединяет все данные в виде LiveData . MainRepository получает свои данные из нескольких источников.

Загрузите или клонируйте репозиторий из нашего репозитория GitHub и соберите его с помощью Gradle или откройте его в своей IDE. Вы также должны создать учетную запись OpenWeatherMap и получить новый идентификатор приложения. Добавьте идентификатор приложения в строковый ресурс с именем openWeather .

1
<string name=»openWeather»>XXXXXXXXXXXXXXX</string>

Поскольку компоненты архитектуры все еще находятся в альфа-версии, вы должны включить репозиторий Google, содержащий некоторые экспериментальные библиотеки, в build.gradle проекта.

1
2
3
4
5
6
7
allprojects {
    repositories {
 
        // add this repository
        maven { url ‘https://maven.google.com’ }
    }
}

В модуле build.gradle добавьте следующее в раздел dependencies чтобы добавить поддержку для Lifecycles LiveData , LiveData и ViewModel :

  • compile "android.arch.lifecycle:runtime:1.0.0-alpha5"
  • compile "android.arch.lifecycle:extensions:1.0.0-alpha5"
  • annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha5"

Если вы используете Kotlin, вы также должны добавить или заменить annotationProcessor на kapt , который обрабатывает аннотации в Kotlin.

1
kapt «android.arch.lifecycle:compiler:1.0.0-alpha5»

Чтобы включить kapt , добавьте следующее в корень модуля build.gradle .

1
2
3
kapt {
    generateStubs = true
}

Каждый разработчик Android знаком с концепцией жизненного цикла. Система управляет жизненным циклом приложений, действий, фрагментов и т. Д. Без контроля разработчика. Эта концепция является одной из парадигм Android, и до недавнего времени работать с ней было не так просто, поскольку было невозможно напрямую проверить текущее состояние жизненного цикла компонента. Мы могли реагировать на определенные методы, такие как onCreate и onDestroy , которые запускаются событиями жизненного цикла.

Деятельность Lifecyle

Все изменилось с момента анонса пакета Компоненты архитектуры, в котором появился компонент под названием Lifecycle . Теперь к некоторым объектам Android прикреплен Lifecycle , и это многое меняет для разработчиков. Можно Lifecycle состояние Lifecycle в любой момент времени, а также можно реагировать на события Lifecycle с помощью аннотаций. Фактически, основой новых компонентов архитектуры Android является компонент Lifecycle .

Все элементы пакета android.arch.lifecycle важны для концепции жизненного цикла , но два из них заслуживают большего внимания: LifecycleOwner и LifecycleObserver . Они создают возможность работы с Lifecycle , наблюдая, а также реагируя на события, происходящие в действиях, фрагментах, сервисах и так далее.

LifecycleOwner — это интерфейс с одним методом для классов, содержащих Lifecycle . Он абстрагируется от владения Lifecycle , позволяя вам писать компоненты, которые могут с ним работать. По новым стандартам действия и фрагменты являются LifecycleOwner s. Однако до запуска окончательной версии компонентов архитектуры вы должны использовать некоторые специальные классы: ActivityLifecycle , FragmentLifecycle и LifecycleService .

1
2
3
4
5
class MainActivity : LifecycleActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
}

В реализации этих классов нет существенных изменений по сравнению со стандартными Действиями и Фрагментами. Как только класс расширяет любой из них, к нему присоединяется Lifecycle , который можно получить в любое время с помощью метода getLifecycle() . Другая интересная возможность заключается в том, что мы можем проверить текущее состояние жизненного цикла с помощью getCurrentState , который возвращает Lifecycle.State .

Существует пять различных состояний Lifecycle :

  • INITIALIZED : для объекта, который был вызван, но еще не активен. Это эквивалент состояния перед методом Activity.onCreate .
  • CREATED : для объектов, которые были только что созданы. Он вызывается после метода onCreate а также onStop непосредственно перед методом onStop .
  • STARTED : onStart после onStart и непосредственно перед методом onPause .
  • RESUMED : активное состояние или возобновленное состояние для LifecycleOwner . Вызывается после метода onResume .
  • DESTROYED : для уничтоженного объекта LifecycleOwner . Этот Lifecycle не будет отправлять больше событий. Это событие достигается непосредственно перед методом onDestroy .
События LifecycleOwner

Одним из самых интересных свойств Lifecycle является то, что его легко наблюдать. Классы LifecycleObserver могут наблюдать компоненты LifecycleOwner , такие как Действия и Фрагменты. Он получает LifecycleOwner.Event и может реагировать на них с помощью аннотации @OnLifeCycleEvent( Lifecycle.Event ) .

01
02
03
04
05
06
07
08
09
10
class MainObserver : LifecycleObserver, AnkoLogger {
    @OnLifecycleEvent( Lifecycle.Event.ON_RESUME )
    fun onResult() {
        info(«onResult»)
    }
    @OnLifecycleEvent( Lifecycle.Event.ON_STOP )
    fun onStop() {
        info(«onStop»)
    }
}

Методы, аннотированные @OnLifecycleEvent , не нуждаются в аргументах, но если они используются, первым аргументом должен быть LifecycleOwner . Когда аннотация использует Lifecycle.Event.ON_ANY , метод должен ожидать два аргумента: LifecycleOwner и Lifecycle.Event .

1
2
3
4
5
6
@OnLifecycleEvent( Lifecycle.Event.ON_ANY )
fun onEvent( owner: LifecycleOwner, event: Lifecycle.Event )
{
    info(«onEvent: ownerState: ${owner.lifecycle.currentState}»)
    info(«onEvent: event: $event»)
}

Чтобы активировать аннотацию @OnLifecycleEvent , LifecycleObserver должен наблюдать Lifecycle , иначе он не получит событие. Для этого вызовите Lifecycle.addObserver(LifecycleOwner) и впоследствии LifecycleOwner сможет реагировать на Lifecycle.Event . Также возможно вызвать Lifecycle.removeObsever(LifecycleObserver) чтобы удалить наблюдателя.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
class MainActivity : LifecycleActivity(), AnkoLogger {
    @Inject lateinit var mainObserver: MainObserver
     
    override fun onCreate(savedInstanceState: Bundle?) {
        // …
        // On Kotlin, instead of getLifecycle,
        // we can call lifecycle directly
        lifecycle.addObserver( mainObserver )
    }
     
    override fun onDestroy() {
        // …
        lifecycle.removeObserver( mainObserver )
    }
}

Существуют различные интересные варианты использования для LifecycleObserver . Например, его можно использовать для создания слоя Presenter из шаблона архитектуры Presenter Model Viewer. Его также можно использовать для создания слушателей, которые могут прекратить прослушивание, когда Lifecycle отключен.

Разработанный для совместной работы со слоем пользовательского интерфейса, компонент ViewModel устраняет пробел, существовавший в Android с самого начала: он предоставляет способ элегантного управления и хранения объектов данных, связанных с представлением. Компонент поддерживает целостность данных между изменениями конфигурации, может использоваться совместно Activity и Fragments и является отличным инструментом, позволяющим полностью избежать утечек памяти.

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

Целью ViewModel является получение и хранение информации, необходимой для Действия или Фрагмента.

Вдобавок ко всему, ViewModel облегчает разделение проблем в процессе разработки Android. Перемещая все связанные с данными операции в этот компонент и позволяя ему обрабатывать логику, можно значительно повысить тестируемость и удобство сопровождения приложения. С помощью ViewModel можно легко принять архитектуру Android, предложенную в Google I / O 2017 года. Вы даже можете использовать его для принятия более сложных архитектурных шаблонов, таких как MVP или MVVM.

Есть два способа реализации ViewModel . Стандартным является расширение класса с помощью конструктора без аргументов. Это самый простой способ, но он не работает с Dependency Injection.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
class MainViewModel : ViewModel() {
    init {
        // initialize some behavior
    }
     
    fun getData() : LiveData<String> {
        // get some data
    }
 
    override fun onCleared() {
        super.onCleared()
        // called before its destruction
    }
}

Чтобы получить ViewModel с помощью этого метода, из Activity или Fragment, просто вызовите ViewModelProviders.of(FragmentActivity).get(Class<T>) . Последний аргумент должен содержать класс ViewModel . Тот же экземпляр ViewModel будет выбран представлением и будет содержать все данные для этого представления.

1
2
val viewModel: MainViewModel =
   ViewModelProviders.of(this).get(MyViewModel::class.java)

Обратите внимание, что поскольку ViewModel взят из метода ViewModelProviders.of , его конструктор не может получать аргументы. В качестве обходного пути вы можете реализовать ViewModelProvider.Factory . На самом деле, это та же техника, которую мы используем для внедрения ViewModel .

При использовании DI все становится немного сложнее. Вам нужно будет реализовать ViewModelProvider.Factory . Следующие шаги могут быть использованы для внедрения ViewModel с помощью Dagger. ViewModelFactory — это служебный класс, который предоставляет ViewModel для области.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Suppress(«UNCHECKED_CAST»)
@Singleton
class ViewModelFactory
@Inject
constructor(
        private val creators: Map<Class<out ViewModel>,
                @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
 
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>?
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException(«unknown model class » + modelClass)
        }
        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
 
    }
}

Dagger также нужен @MapKey определенный для ViewModel и связыватель для каждой модели и для фабрики в модуле.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// @MapKey
@MustBeDocumented
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@kotlin.annotation.Retention()
@MapKey
internal annotation class ViewModelKey(
        val value: KClass<out ViewModel>)
         
// ViewModel Module
@Module
abstract class ViewModelsModule {
    // Bind each ViewModel
    @Binds
    @IntoMap
    @ViewModelKey( MainViewModel::class )
    abstract fun bindMainViewModel( mainViewModel: MainViewModel ) : ViewModel
 
    // ViewModel factory binding
    @Binds
    abstract fun bindViewModelFactory( factory: ViewModelFactory ) : ViewModelProvider.Factory
}

После этого следуйте стандартным процедурам Dagger, и вы сможете создать ViewModel способную вводить аргументы в своем конструкторе. Чтобы создать новый экземпляр, получите ViewModelFactory и возьмите из него желаемую ViewModel .

1
2
3
4
5
6
7
// Get the ViewModel factory
@Inject lateinit var viewModelFactory:
    ViewModelProvider.Factory
     
// Get ViewModel
val viewModel = ViewModelProviders.of(this, viewModelFactory)
    .get(MainViewModel::class.java)

В нашем примере проекта вы можете внимательно изучить DI с помощью Dagger. Я также предоставил вам папку примеров в учебном репозитории GitHub с фрагментами, показывающими, как настроить ViewModels в системе Dagger с использованием Kotlin.

1
2
3
4
5
6
7
8
9
class MainViewModel
@Inject
constructor(
        private val repository: MainRepository
)
    : ViewModel(), AnkoLogger {
     
    // … code goes here
}

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

До скорого! А пока, посмотрите другие наши посты о разработке приложений для Android!

  • Android SDK
    Android O: проверка номера телефона с помощью SMS-токенов
    Чике Мгбемена
  • Android вещи
    Вещи Android: создание облачного сервисного швейцара
    Пол Требилкокс-Руис
  • Android SDK
    Создайте интеллектуальное приложение с Google Cloud Speech и API на естественном языке
    Ашраф Хатхибелагал