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

Post on 25-Jul-2015

144 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Реактивный двигатель вашего 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

29nameEditText.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)); } }); } }); } });

30nameEditText.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)); } }); } }); } });

31nameEditText.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)); } }); } }); } });

32nameEditText.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

38nameEditText.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(); } }); } }); } });

39nameEditText.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(); } }); } }); } });

40nameEditText.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(); } }); } }); } });

41nameEditText.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

Mt

53

Mo

M Mot

54

Mob

M Mo Mobt

55

Mobi

M Mo Mob Mobit

56

Mobiu

MobiuM Mo Mob Mobit

57

Mobius

MobiuM Mo Mob Mobi Mobiust

• изолируют состояние

58Потоки данных

• изолируют состояние

• прошлое

59Потоки данных

• изолируют состояние

• прошлое

• настоящее

60Потоки данных

• изолируют состояние

• прошлое

• настоящее

• будущее

61Потоки данных

• изолируют состояние

• прошлое

• настоящее

• будущее

• неизменяемые

62Потоки данных

• изолируют состояние

• прошлое

• настоящее

• будущее

• неизменяемые

• строго типизированные

63Потоки данных

• EditTextObservable

64Что может быть потоком?

• EditTextObservable

• ApiDataObservable

65Что может быть потоком?

• EditTextObservable

• ApiDataObservable

• TouchObservable

66Что может быть потоком?

• EditTextObservable

• ApiDataObservable

• TouchObservable

• Все, что захотите

67Что может быть потоком?

RxJava

• reactive streams для java

69RxJava

• reactive streams для java

• open source

• by Netflix

70RxJava

• reactive streams для java

• open source

• by Netflix

• стабильная

71RxJava

• reactive streams для java

• open source

• by Netflix

• стабильная

• почти

72RxJava

Observable

74Потоки данных

t

Данные Ошибка Конец

• создать

75Observable можно

• создать

• изменить

76Observable можно

• создать

• изменить

• получить значения

77Observable можно

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

Mt

M

87Подписка

Кто-то №2

Mt

Кто-то №1

M

88Подписка

Кто-то №2

M Mot

Mo

Кто-то №1

89Подписка

Кто-то №2

M Mo Mobt

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

// когда больше не надо слушать этот EditTextnameSubs.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

110DebounceObservable<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*/ });

111DebounceObservable<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*/ });

112DebounceObservable<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*/ });

113DebounceObservable<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

• имеет пул потоков

148Scheduler

• имеет пул потоков

• создает Scheduler.Worker

149Scheduler

• имеет пул потоков

• создает Scheduler.Worker

• Worker –– цепочка последовательных вычислений

150Scheduler

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 Extensions

• Reactive Extensions for JavaScript

• Reactive Cocoa

• Rx.py

• Rx.php

193Ссылки

top related