Урок 1. Основы RxJava. Observable и Observer.
Этот урок начнем с паттерна Наблюдатель и разберемся, как он используется в RxJava. Рассмотрим основные понятия: Observable и Observer, и какие типы событий они используют. Далее разберем один теоретический и один практический примеры.
Версии RxJava
На момент создания этого курса, вторая версия RxJava была в статусе Release Candidate. Поэтому начало курса описывает первую версию. Но, во-первых, вторая версия очень похожа на первую, и уроки актуальны для обоих версий. А, во-вторых, если вы новичок в теме Rx, то вам будет сложно сразу понять главное новшество второй версии.
Поэтому предлагаю вам спокойно читать уроки, написанные по первой версии, т.к. все эти знания будут актуальны и для второй. А начиная с 11 урока мы просто перейдем на вторую версию. К тому моменту вы уже без проблем сможете понять главные отличия между ними.
Теория
Прежде чем начать обсуждать механизмы RxJava, давайте вспомним паттерн Наблюдатель. В нем есть объект, который генерирует какие-то события, и есть объект или объекты, которые подписываются и получают эти события. Я думаю, что в работе вы постоянно используете этот паттерн. Самый простой пример — обработчик нажатия кнопки.
В Java даже есть инструменты для этого паттерна — класс Observable и интерфейс Observer. Реализация интерфейса Observer — это объект, который ожидает событие. Observable — это класс, который всем переданным ему Observer-объектам сообщит о том, что событие наступило.
Эти же названия используются и в RxJava. И смысл их остался тем же: Observable генерирует событие, а Observer получает его. Но было значительно расширено само понятие «событие». В RxJava события, которые Observable передает в Observer, можно рассматривать как поток данных. И события в этом потоке имеют три типа:
1) Next — очередная порция данных
2) Error — произошла ошибка
3) Completed — поток завершен и данных больше не будет.
В качестве примера давайте рассмотрим поиск авиарейса. Пусть у нас есть метод, который умеет бегать по сайтам авиакомпаний, искать там по заданным критериям необходимый рейс и все полученные рейсы возвращать нам. Мы задаем ему пункт назначения, пункт отправления, дату — и он начинает работу.
По мере того, как он один за другим обрабатывает сайты, он генерирует события Next, в которые передает результат. Т.е. каждый найденный рейс — новое событие Next.
Если в работе метода произошла какая то серьезная ошибка и продолжение работы невозможно, метод отправит событие Error.
Если метод успешно обработал все известные ему сайты и закончил работу, он отправит нам событие Completed.
Какие места занимают во всей этой схеме Observable и Observer? Метод поиска при вызове возвращает нам объект Observable. А мы создаем Observer, в котором пишем код для обработки полученных событий, и подписываемся на этот Observable. По мере работы метода, Observable будет генерировать события, которые мы будем получать в нашем созданном Observer:
— если пришло событие Next с очередным рейсом, то берем рейс и, например, добавляем его в адаптер списка. Таким образом рейсы один за другим будут появляться в списке по мере их нахождения.
— если пришло событие Completed, значит мы можем выключить ProgressBar и уведомить пользователя, что поиск завершен
— если пришло событие Error, то уведомляем пользователя, что поиск был прерван с ошибкой
В этом примере я описал все три типа событий, чтобы было понятно, зачем они нужны. Но, вовсе необязательно в каждом потоке данных вы встретите все эти типы. Например, Observable, который сообщает нам о нажатиях на кнопку. В этом случае мы будем получать только событие Next при каждом нажатии. Событие Completed нам не придет, потому что пользователь может сколько угодно раз нажимать эту кнопку и никакого последнего нажатия там не будет. Ну и ошибку тут мы вряд ли получим.
Надеюсь, после этого введения и примеров у вас появилось понимание роли объектов Observable и Observer. Самое сложное поначалу — это просто отличать их друг от друга )
Практика
Давайте рассмотрим простейший пример Observable.
Создание Observable выглядит так:
Observable — это описание означает, что Observable будет предоставлять данные типа String, т.е. каждое событие Next, которое он будет генерировать, будет приходить с объектом типа String. Метод Observable.from создает для нас Observable, который возьмет данные из указанного String массива и передаст их получателям
Создаем получателя, т.е. Observer:
Observer — получатель данных типа String. Напомню, что он от Observable ожидает получения событий трех типов Next, Error и Completed. И под каждый тип у Observer есть свой одноименный метод:
onNext(String s) — в этот метод будут приходить данные
onError(Throwable e) — будет вызван в случае какой-либо ошибки и на вход получит данные об ошибке
onCompleted() — уведомление о том, что все данные переданы
Оба объекта созданы, осталось подписать Observer на Observable методом subscribe:
Сразу после подписки Observable передаст в Observer все данные (в метод onNext) и сигнал о том, что передача завершена (метод onCompleted).
Этот простой пример призван показать взаимодействие между Observable и Observer. Мы использовали в нем Observable, который умеет передавать данные из предоставленного ему массива. Но это только один из видов Observable. Дальше мы научимся создавать различные Observable.
Что дальше
Когда я только начинал изучать RxJava, у меня после рассмотрения таких примеров возникали вопросы типа:
— В какой момент Observable начал генерировать события: после создания или после подписки на него Observer-а?
— Что будет если подписать несколько Observer-ов: каждый получит свои данные или те, кто подписался позже, не получит ничего?
— Как создать свой Observable, который будет отправлять результаты работы моего кода?
— Как сделать, чтобы работа в Observable выполнялась в одном потоке (в смысле Thread), а результаты приходили в другом?
По мере дальнейшего изучения и экспериментов, эти вопросы были успешно решены, и обо всем этом я расскажу в следующих уроках этого курса.
Кроме того, мы рассмотрим, какие возможности работы с потоками данных предоставляет RxJava. Например, вы можете взять поток данных и выполнять над ним различные преобразования: фильтровать данные или конвертировать данные из одного типа в другой. Можете объединять данные из разных потоков данных последовательно, параллельно или попарно.
Также в RxJava присутствует отличный инструментарий для работы с потоками (речь уже не о потоках данных, а потоках, которые Thread). Вы можете указать один поток для генерации данных в Observable, другой поток для выполнения каких либо операций преобразования над этими данными и третий поток, в котором данные будут приходить в Observer.
В общем, тема очень интересная и полезная, и этот курс поможет вам в ней разобраться.
Источник
Урок 21. Android Data Binding. Observable поля. Двусторонний биндинг.
В этом уроке разберем, как настроить автоматическую передачу данных в View и обратно.
Полный список уроков курса:
Когда мы используем биндинг для обычного Java объекта, то экран не будет автоматически меняться при изменении значений в этом объекте.
Т.е. вы передаете объект Employee в биндинг, а затем можете сколько угодно менять в нем поля и ничего на экране меняться не будет. Вам надо будет снова руками передать измененный объект в биндинг или вызвать у биндинга метод invalidateAll, тогда экран отобразит актуальные данные.
В первом уроке по Data Binding я упоминал, что есть возможность сделать так, чтобы биндинг сам мониторил значения полей и обновлял экран, как только произошли какие-то изменения. Для этого надо использовать механизмы Observable.
Observable поля
Сделаем несколько Observable полей в классе Employee:
Для Java примитивов есть готовые Observable поля: ObservableInt, ObservableFloat и т.п. Для остальных используем ObservableField с указанием типа. Чтобы присвоить такому полю значение, используем метод set.
Поля name и salary делаем Observable. Их мы будем использовать в биндинге.
layout файл выглядит как обычно, в нем ничего менять не надо:
Теперь, передав в биндинг объект Employee, вы сможете менять значения его полей:
А биндинг сам отследит эти изменения и обновит экран.
Для коллекций есть классы ObservableArrayMap и ObservableArrayList.
Биндинг будет работать, даже если передавать значения в Observable поля не в UI потоке.
Рассмотрим еще один возможный сценарий использования ObservableField. Его можно использовать не только с отдельными полями объекта, но и с целым объектом.
Есть класс Employee:
Вполне может быть ситуация, когда нам в ViewModel (или в презентер) периодически «прилетает» из репозитория новый объект Employee и его надо отобразить в View.
В этом случае в ViewModel создаем поле ObservableField :
В это поле методом employee.set() репозиторий будет помещать новый Employee.
В layout в качестве переменной используем ViewModel.
Достаем из model объект employee и используем его поля в биндинге.
Теперь при обновлении значения в ObservableField будут изменены и поля в View без каких-либо дополнительных действий с нашей стороны.
BaseObservable
Есть еще один способ включить автобиндинг для Java объекта.
Делается это наследованием BaseObservable:
Поле id я оставил обычным. А поля name и salary будут отслеживаться биндингом. Для этого надо пометить get-методы аннотацией @Bindable, а в set-методах вызывать notifyPropertyChanged метод, который и будет уведомлять биндинг об изменениях значения поля.
В layout все будет как обычно.
Двусторонний биндинг
Биндинг может работать в обе стороны. Т.е. он будет не только передавать данные в View, но и получать их оттуда.
Рассмотрим на примере пары полей в Employee:
Поле name и статус enabled. Настроим биндинг этих полей в EditText и CheckBox.
При этом сделаем так, чтобы биндинг работал в обе стороны. Для этого надо в строке биндинга добавить символ = между @ и
Теперь при изменении текста в EditText, биндинг будет передавать новое значение в employee.name. А при включении\выключении чекбокса, биндинг будет передавать текущее состояние этого чекбокса в поле employee.enabled.
Т.е. изменения содержимого View будут отражены в Employee объекте, который мы передавали в биндинг. Если необходимо, можно использовать и Observable поля. С ними это тоже будет работать.
Кстати, если после передачи в биндинг вы нигде не храните у себя объект employee, то вы всегда можете получить его обратно методом binding.getEmployee().
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Урок 1. Основы RxJava. Observable и Observer.
Этот урок начнем с паттерна Наблюдатель и разберемся, как он используется в RxJava. Рассмотрим основные понятия: Observable и Observer, и какие типы событий они используют. Далее разберем один теоретический и один практический примеры.
Версии RxJava
На момент создания этого курса, вторая версия RxJava была в статусе Release Candidate. Поэтому начало курса описывает первую версию. Но, во-первых, вторая версия очень похожа на первую, и уроки актуальны для обоих версий. А, во-вторых, если вы новичок в теме Rx, то вам будет сложно сразу понять главное новшество второй версии.
Поэтому предлагаю вам спокойно читать уроки, написанные по первой версии, т.к. все эти знания будут актуальны и для второй. А начиная с 11 урока мы просто перейдем на вторую версию. К тому моменту вы уже без проблем сможете понять главные отличия между ними.
Теория
Прежде чем начать обсуждать механизмы RxJava, давайте вспомним паттерн Наблюдатель. В нем есть объект, который генерирует какие-то события, и есть объект или объекты, которые подписываются и получают эти события. Я думаю, что в работе вы постоянно используете этот паттерн. Самый простой пример — обработчик нажатия кнопки.
В Java даже есть инструменты для этого паттерна — класс Observable и интерфейс Observer. Реализация интерфейса Observer — это объект, который ожидает событие. Observable — это класс, который всем переданным ему Observer-объектам сообщит о том, что событие наступило.
Эти же названия используются и в RxJava. И смысл их остался тем же: Observable генерирует событие, а Observer получает его. Но было значительно расширено само понятие «событие». В RxJava события, которые Observable передает в Observer, можно рассматривать как поток данных. И события в этом потоке имеют три типа:
1) Next — очередная порция данных
2) Error — произошла ошибка
3) Completed — поток завершен и данных больше не будет.
В качестве примера давайте рассмотрим поиск авиарейса. Пусть у нас есть метод, который умеет бегать по сайтам авиакомпаний, искать там по заданным критериям необходимый рейс и все полученные рейсы возвращать нам. Мы задаем ему пункт назначения, пункт отправления, дату — и он начинает работу.
По мере того, как он один за другим обрабатывает сайты, он генерирует события Next, в которые передает результат. Т.е. каждый найденный рейс — новое событие Next.
Если в работе метода произошла какая то серьезная ошибка и продолжение работы невозможно, метод отправит событие Error.
Если метод успешно обработал все известные ему сайты и закончил работу, он отправит нам событие Completed.
Какие места занимают во всей этой схеме Observable и Observer? Метод поиска при вызове возвращает нам объект Observable. А мы создаем Observer, в котором пишем код для обработки полученных событий, и подписываемся на этот Observable. По мере работы метода, Observable будет генерировать события, которые мы будем получать в нашем созданном Observer:
— если пришло событие Next с очередным рейсом, то берем рейс и, например, добавляем его в адаптер списка. Таким образом рейсы один за другим будут появляться в списке по мере их нахождения.
— если пришло событие Completed, значит мы можем выключить ProgressBar и уведомить пользователя, что поиск завершен
— если пришло событие Error, то уведомляем пользователя, что поиск был прерван с ошибкой
В этом примере я описал все три типа событий, чтобы было понятно, зачем они нужны. Но, вовсе необязательно в каждом потоке данных вы встретите все эти типы. Например, Observable, который сообщает нам о нажатиях на кнопку. В этом случае мы будем получать только событие Next при каждом нажатии. Событие Completed нам не придет, потому что пользователь может сколько угодно раз нажимать эту кнопку и никакого последнего нажатия там не будет. Ну и ошибку тут мы вряд ли получим.
Надеюсь, после этого введения и примеров у вас появилось понимание роли объектов Observable и Observer. Самое сложное поначалу — это просто отличать их друг от друга )
Практика
Давайте рассмотрим простейший пример Observable.
Создание Observable выглядит так:
Observable — это описание означает, что Observable будет предоставлять данные типа String, т.е. каждое событие Next, которое он будет генерировать, будет приходить с объектом типа String. Метод Observable.from создает для нас Observable, который возьмет данные из указанного String массива и передаст их получателям
Создаем получателя, т.е. Observer:
Observer — получатель данных типа String. Напомню, что он от Observable ожидает получения событий трех типов Next, Error и Completed. И под каждый тип у Observer есть свой одноименный метод:
onNext(String s) — в этот метод будут приходить данные
onError(Throwable e) — будет вызван в случае какой-либо ошибки и на вход получит данные об ошибке
onCompleted() — уведомление о том, что все данные переданы
Оба объекта созданы, осталось подписать Observer на Observable методом subscribe:
Сразу после подписки Observable передаст в Observer все данные (в метод onNext) и сигнал о том, что передача завершена (метод onCompleted).
Этот простой пример призван показать взаимодействие между Observable и Observer. Мы использовали в нем Observable, который умеет передавать данные из предоставленного ему массива. Но это только один из видов Observable. Дальше мы научимся создавать различные Observable.
Что дальше
Когда я только начинал изучать RxJava, у меня после рассмотрения таких примеров возникали вопросы типа:
— В какой момент Observable начал генерировать события: после создания или после подписки на него Observer-а?
— Что будет если подписать несколько Observer-ов: каждый получит свои данные или те, кто подписался позже, не получит ничего?
— Как создать свой Observable, который будет отправлять результаты работы моего кода?
— Как сделать, чтобы работа в Observable выполнялась в одном потоке (в смысле Thread), а результаты приходили в другом?
По мере дальнейшего изучения и экспериментов, эти вопросы были успешно решены, и обо всем этом я расскажу в следующих уроках этого курса.
Кроме того, мы рассмотрим, какие возможности работы с потоками данных предоставляет RxJava. Например, вы можете взять поток данных и выполнять над ним различные преобразования: фильтровать данные или конвертировать данные из одного типа в другой. Можете объединять данные из разных потоков данных последовательно, параллельно или попарно.
Также в RxJava присутствует отличный инструментарий для работы с потоками (речь уже не о потоках данных, а потоках, которые Thread). Вы можете указать один поток для генерации данных в Observable, другой поток для выполнения каких либо операций преобразования над этими данными и третий поток, в котором данные будут приходить в Observer.
В общем, тема очень интересная и полезная, и этот курс поможет вам в ней разобраться.
Источник