Реактивный двигатель вашего
Android приложения
Матвей Мальков
Обо мне
3
Обо мне
О чем доклад
5
• практика
6
О чем доклад
• практика
• проблемы
7
О чем доклад
• практика
• проблемы
• решения
8
О чем доклад
• практика
• проблемы
• решения
• android + RxJava
9
О чем доклад
Сейчас
11
User Interface
12
User Interface
TextView
EditText
EditText
Button Button
13
User Interface
TextView
EditText
EditText
Button Button
State
14
Data
15
Data
API
CacheDevice
DB
16
Data
State
API
CacheDevice
DB
17
Состояние системы
• мутабельно
18
Состояние системы
• мутабельно
• обновляется через callback
19
Состояние системы
• мутабельно
• обновляется через callback
• плохо композируется
20
Состояние системы
• мутабельно
• обновляется через callback
• плохо композируется
• явное
21
Состояние системы
Мутабельность
23
User user = new User("Jake Mobius");



/**

* 100 качественных строчек кода

*/



final String name = user.getName();

//этот assert легко может упасть

assert("Jake Mobius".equals(name));
24
// какой-то тред 

User user = new User("Jake Mobius");

fetchInfo(user);



final String name = user.getName();

//этот assert легко может упасть

assert("Jake Mobius".equals(name));
25
// какой-то тред 

User user = new User("Jake Mobius");

fetchInfo(user);



final String name = user.getName();

//этот assert легко может упасть

assert("Jake Mobius".equals(name));
// в это же время

// другой какой-то тред

user.setName("Jake Popik");
Обновляемость через callback
27
28
29
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

setHasDuplicates(calcDuplicates(response));

}

});

}

});

}

});
30
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

setHasDuplicates(calcDuplicates(response));

}

});

}

});

}

});
31
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

setHasDuplicates(calcDuplicates(response));

}

});

}

});

}

});
32
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

setHasDuplicates(calcDuplicates(response));

}

});

}

});

}

});
Плохая композируемость
34
35
passEditText.addTextChangedListener(new Watcher() {

@Override

public void afterTextChanged(Editable editable) {

lastPassText = editable.toString();

checkEqualityToEnableButton();

}

});



confirmEditText.addTextChangedListener(new Watcher() {

@Override

public void afterTextChanged(Editable editable) {

lastConfirmText = editable.toString();

checkEqualityToEnableButton();

}

});
36
passEditText.addTextChangedListener(new Watcher() {

@Override

public void afterTextChanged(Editable editable) {

lastPassText = editable.toString();

checkEqualityToEnableButton();

}

});



confirmEditText.addTextChangedListener(new Watcher() {

@Override

public void afterTextChanged(Editable editable) {

lastConfirmText = editable.toString();

checkEqualityToEnableButton();

}

});
37
38
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

lastIsNameEmpty = editable.toString().length() == 0;

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

lastIsHasDuplicates = calcDuplicates(response);

setHasDuplicates(lastIsHasDuplicates);

checkEqualityToEnableButton();

}

});

}

});

}

});
39
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

lastIsNameEmpty = editable.toString().length() == 0;

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

lastIsHasDuplicates = calcDuplicates(response);

setHasDuplicates(lastIsHasDuplicates);

checkEqualityToEnableButton();

}

});

}

});

}

});
40
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

lastIsNameEmpty = editable.toString().length() == 0;

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

lastIsHasDuplicates = calcDuplicates(response);

setHasDuplicates(lastIsHasDuplicates);

checkEqualityToEnableButton();

}

});

}

});

}

});
41
nameEditText.addTextChangedListener(new Watcher() {



@Override public void afterTextChanged(Editable editable) {

lastIsNameEmpty = editable.toString().length() == 0;

requestDuplicates(editable.toString(), new MyCallback() {



@Override public void onResponse(Response response){

checkResponseForDuplicates(response, new MyCallback() {



@Override public void onResponse(Response response) {

lastIsHasDuplicates = calcDuplicates(response);

setHasDuplicates(lastIsHasDuplicates);

checkEqualityToEnableButton();

}

});

}

});

}

});
Явное состояние
43
private String lastPassText;

private String lastConfirmText;

private boolean lastIsHasDuplicates;

private boolean lastIsNameEmpty;
44
lastPassText = editable.toString().trim();
45
lastPassText = editable.toString().trim();
46
lastPassText = editable.toString().trim();
47
private String lastPassText;

private String lastConfirmText;

private boolean lastIsHasDuplicates;

private boolean lastIsNameEmpty;



private String lastPassTrimmedString;
“Все может быть лучше”
“Пора стать реактивней”
50
Потоки данных
t
Данные Ошибка Конец
51
t
52
M
M
t
53
Mo
M Mo
t
54
Mob
M Mo Mob
t
55
Mobi
M Mo Mob Mobi
t
56
Mobiu
MobiuM Mo Mob Mobi
t
57
Mobius
MobiuM Mo Mob Mobi Mobius
t
• изолируют состояние
58
Потоки данных
• изолируют состояние
• прошлое
59
Потоки данных
• изолируют состояние
• прошлое
• настоящее
60
Потоки данных
• изолируют состояние
• прошлое
• настоящее
• будущее
61
Потоки данных
• изолируют состояние
• прошлое
• настоящее
• будущее
• неизменяемые
62
Потоки данных
• изолируют состояние
• прошлое
• настоящее
• будущее
• неизменяемые
• строго типизированные
63
Потоки данных
• EditTextObservable
64
Что может быть потоком?
• EditTextObservable
• ApiDataObservable
65
Что может быть потоком?
• EditTextObservable
• ApiDataObservable
• TouchObservable
66
Что может быть потоком?
• EditTextObservable
• ApiDataObservable
• TouchObservable
• Все, что захотите
67
Что может быть потоком?
RxJava
• reactive streams для java
69
RxJava
• reactive streams для java
• open source
• by Netflix
70
RxJava
• reactive streams для java
• open source
• by Netflix
• стабильная
71
RxJava
• reactive streams для java
• open source
• by Netflix
• стабильная
• почти
72
RxJava
Observable
74
Потоки данных
t
Данные Ошибка Конец
• создать
75
Observable можно
• создать
• изменить
76
Observable можно
• создать
• изменить
• получить значения
77
Observable можно
78
Создание
7
Observable<Integer> just = Observable.just(7);
t
79
Создание
6541 2 3 7
Observable<Integer> many = Observable

.from(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
t
80
Создание
Observable.create(subscriber -> {

final List<Data> list = requestNewData();



for (final Data data : list) {

subscriber.onNext(data);

}

subscriber.onCompleted();

});
81
Создание
Observable.create(subscriber -> {

final List<Data> list = requestNewData();



for (final Data data : list) {

subscriber.onNext(data);

}

subscriber.onCompleted();

});
82
Создание
Observable.create(subscriber -> {

final List<Data> list = requestNewData();



for (final Data data : list) {

subscriber.onNext(data);

}

subscriber.onCompleted();

});
83
Создание
Observable.create(subscriber -> {

final List<Data> list = requestNewData();



for (final Data data : list) {

subscriber.onNext(data);

}

subscriber.onCompleted();

});
84
Подписка
t
85
Подписка
Кто-то №1
t
86
Подписка
Кто-то №1
M
t
M
87
Подписка
Кто-то №2
M
t
Кто-то №1
M
88
Подписка
Кто-то №2
M Mo
t
Mo
Кто-то №1
89
Подписка
Кто-то №2
M Mo Mob
t
Mob
Кто-то №1
90
91
Observable<String> nameObs = EditTextObservable.from(nameEditText);



Subscription nameSubs = nameObs

.doOnNext(name -> {

Log.i(TAG, "new user name : " + name);

})

.subscribe(name -> {

signUpTitle.setText("Sign up, " + name);

});
92
Observable<String> nameObs = EditTextObservable.from(nameEditText);



Subscription nameSubs = nameObs

.doOnNext(name -> {

Log.i(TAG, "new user name : " + name);

})

.subscribe(name -> {

signUpTitle.setText("Sign up, " + name);

});
93
Observable<String> nameObs = EditTextObservable.from(nameEditText);



Subscription nameSubs = nameObs

.doOnNext(name -> {

Log.i(TAG, "new user name : " + name);

})

.subscribe(name -> {

signUpTitle.setText("Sign up, " + name);

});
94
// когда больше не надо слушать этот EditText

nameSubs.unsubscribe();
Observable<String> nameObs = EditTextObservable.from(nameEditText);



Subscription nameSubs = nameObs

.doOnNext(name -> {

Log.i(TAG, "new user name : " + name);

})

.subscribe(name -> {

signUpTitle.setText("Sign up, " + name);

});
95
Операторы
• возвращают новый Observable
97
Операторы
• возвращают новый Observable
• применяются ко всему потоку
98
Операторы
Основы : map и flatMap
100
102
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
103
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
104
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
105
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
106
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
107
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



nameObs.subscribe(name -> requestDuplicates(name));



Observable<String> rightTitleObservable = nameObs

.map(name -> {

if (TextUtils.isEmpty(name)) return name;

else return ", " + name;

});



rightTitleObservable.subscribe(name ->

signUpTitle.setText("Sign up" + name));
108
109
110
Debounce
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);



duplicatesCheckObservable.subscribe(isUnique -> { /*process*/ });
111
Debounce
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);



duplicatesCheckObservable.subscribe(isUnique -> { /*process*/ });
112
Debounce
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);



duplicatesCheckObservable.subscribe(isUnique -> { /*process*/ });
113
Debounce
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);



duplicatesCheckObservable.subscribe(isUnique -> { /*process*/ });
• выполняется когда надо
114
Наше решение
• выполняется когда надо
• экономит траффик
115
Наше решение
• выполняется когда надо
• экономит траффик
• не нагружает сервер
116
Наше решение
• выполняется когда надо
• экономит траффик
• не нагружает сервер
• без callback hell
117
Наше решение
• выполняется когда надо
• экономит траффик
• не нагружает сервер
• без callback hell
• легко обрабатывает ошибки
118
Наше решение
119
Прочие полезности
• debounce
121
Полезности
• debounce
• cache
122
Полезности
• debounce
• cache
• timeout
123
Полезности
• debounce
• cache
• timeout
• first
124
Полезности
• debounce
• cache
• timeout
• first
• distinct
125
Полезности
• debounce
• cache
• timeout
• first
• distinct
• skipLast
126
Полезности
• debounce
• cache
• timeout
• first
• distinct
• skipLast
• reduce
127
Полезности
• debounce
• cache
• timeout
• first
• distinct
• skipLast
• reduce
• cast
128
Полезности
• debounce
• cache
• timeout
• first
• distinct
• skipLast
• reduce
• cast
• averageDouble
129
Полезности
• debounce
• cache
• timeout
• first
• distinct
• skipLast
• reduce
• cast
• averageDouble
• takeWhile
130
Полезности
Комбинирование
132
133
134
Observable<Boolean> shouldEnableButtonObs = Observable

.combineLatest(

passObs.map(pass -> pass.trim()),

confirmObs.map(confirm -> confirm.trim()),

duplicatesCheckObservable,

(pass, confirmPass, isNameUnique) ->

TextUtils.equals(pass, confirmPass)

&& isNameUnique

);



shouldEnableButtonObs

.subscribe(enabled -> button.setEnabled(enabled));
135
Observable<Boolean> shouldEnableButtonObs = Observable

.combineLatest(

passObs.map(pass -> pass.trim()),

confirmObs.map(confirm -> confirm.trim()),

duplicatesCheckObservable,

(pass, confirmPass, isNameUnique) ->

TextUtils.equals(pass, confirmPass)

&& isNameUnique

);



shouldEnableButtonObs

.subscribe(enabled -> button.setEnabled(enabled));
136
Observable<Boolean> shouldEnableButtonObs = Observable

.combineLatest(

passObs.map(pass -> pass.trim()),

confirmObs.map(confirm -> confirm.trim()),

duplicatesCheckObservable,

(pass, confirmPass, isNameUnique) ->

TextUtils.equals(pass, confirmPass)

&& isNameUnique

);



shouldEnableButtonObs

.subscribe(enabled -> button.setEnabled(enabled));
137
Observable<Boolean> shouldEnableButtonObs = Observable

.combineLatest(

passObs.map(pass -> pass.trim()),

confirmObs.map(confirm -> confirm.trim()),

duplicatesCheckObservable,

(pass, confirmPass, isNameUnique) ->

TextUtils.equals(pass, confirmPass)

&& isNameUnique

);



shouldEnableButtonObs

.subscribe(enabled -> button.setEnabled(enabled));
138
Observable<Boolean> shouldEnableButtonObs = Observable

.combineLatest(

passObs.map(pass -> pass.trim()),

confirmObs.map(confirm -> confirm.trim()),

duplicatesCheckObservable,

(pass, confirmPass, isNameUnique) ->

TextUtils.equals(pass, confirmPass)

&& isNameUnique

);



shouldEnableButtonObs

.subscribe(enabled -> button.setEnabled(enabled));
139
А сколько у нас явного состояния?
141
// нисколько
• полезно знать все
142
Операции
• полезно знать все
• применение есть всем. Ищите!
143
Операции
• полезно знать все
• применение есть всем. Ищите!
• смотрите исходники
144
Операции
• полезно знать все
• применение есть всем. Ищите!
• смотрите исходники
• думайте!
145
Операции
Когда тредов > 1
Scheduler
• имеет пул потоков
148
Scheduler
• имеет пул потоков
• создает Scheduler.Worker
149
Scheduler
• имеет пул потоков
• создает Scheduler.Worker
• Worker –– цепочка последовательных
вычислений
150
Scheduler
subscribeOn
observeOn
1. observeOn для того,
что ниже по коду
2. subscribeOn для того,
что выше по коду
3. Каждый следующий observeOn
заменяет предыдущий
4. subscribeOn актуален только до
первого observeOn
Оба оператора все еще возвращают
новый поток
157
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);

158
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.onErrorReturn(throwable -> false);

159
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

160
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

161
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

162
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

163
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

164
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

165
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);

166
Observable<Boolean> duplicatesCheckObservable = nameObs

.debounce(500, TimeUnit.MILLISECONDS)

.observeOn(Schedulers.newThread())

.map(name -> requestDuplicates(name))

.map(response -> checkResponseForDuplicates(response))

.map(checkedResp -> calcDuplicates(checkedResp))

.subscribeOn(mainThread)

.observeOn(mainThread)

.onErrorReturn(throwable -> false);



duplicatesCheckObservable.subscribe(isUnique -> { /*process*/ });
Ложечка дегтя
• объектов аллоцируется больше
168
Ложечка дегтя
• объектов аллоцируется больше
• сложно дебажить
169
Ложечка дегтя
• объектов аллоцируется больше
• сложно дебажить
• логируйте много
170
Ложечка дегтя
• объектов аллоцируется больше
• сложно дебажить
• логируйте много
• пишите правильно сразу
171
Ложечка дегтя
• объектов аллоцируется больше
• сложно дебажить
• логируйте много
• пишите правильно сразу
• сложно объяснить
172
Ложечка дегтя
• объектов аллоцируется больше
• сложно дебажить
• логируйте много
• пишите правильно сразу
• сложно объяснить
• сложно найти крутую команду
173
Ложечка дегтя
Итоги
• изолированное состояние
175
Итоги
• изолированное состояние
• легкая многопоточность
176
Итоги
• изолированное состояние
• легкая многопоточность
• легкое комбинирование
177
Итоги
• изолированное состояние
• легкая многопоточность
• легкое комбинирование
• понятность кода
178
Итоги
• изолированное состояние
• легкая многопоточность
• легкое комбинирование
• понятность кода
• стойкость к ошибкам
179
Итоги
Reactive Extension
Reactive Extension
Reactive Extension
Это круто!
Меняйтесь
За этим будущее
Спасибо
Матвей Мальков
malkov.matvey@gmail.com
matveyka_jj
Правильная реализация подсветки
кнопки
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable2 = nameObs

.flatMap(name ->

DebouncedDuplicatesObservable

.from(name)

.startWith(false))

.onErrorReturn(thr -> false);



duplicatesCheckObservable2.

subscribe(isDubl -> { /*process*/ });
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable2 = nameObs

.flatMap(name ->

DebouncedDuplicatesObservable

.from(name)

.startWith(false))

.onErrorReturn(thr -> false);



duplicatesCheckObservable2.

subscribe(isDubl -> { /*process*/ });
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable2 = nameObs

.flatMap(name ->

DebouncedDuplicatesObservable

.from(name)

.startWith(false))

.onErrorReturn(thr -> false);



duplicatesCheckObservable2.

subscribe(isDubl -> { /*process*/ });
Observable<String> nameObs = EditTextObservable

.from(nameEditText);



Observable<Boolean> duplicatesCheckObservable2 = nameObs

.flatMap(name ->

DebouncedDuplicatesObservable

.from(name)

.startWith(false))

.onErrorReturn(thr -> false);



duplicatesCheckObservable2.

subscribe(isDubl -> { /*process*/ });
• Reactive Streams
• Akka Stream
• Reactor
• Ratpack
• RxJava
• Reactive Manifesto
192
Ссылки
• Reactive Extensions
• Reactive Extensions for JavaScript
• Reactive Cocoa
• Rx.py
• Rx.php
193
Ссылки

Реактивный двигатель вашего Android приложения