RxJava и Android. Плюсы, минусы, подводные камни

47
, Tips and , Tips and Tricks Tricks RxJava RxJava Yaroslav Heriatovych

Transcript of RxJava и Android. Плюсы, минусы, подводные камни

Page 1: RxJava и Android. Плюсы, минусы, подводные камни

, Tips and, Tips andTricksTricks

RxJavaRxJava

Yaroslav Heriatovych

Page 2: RxJava и Android. Плюсы, минусы, подводные камни

RxJavaRxJavaBackgroundBackground

Page 3: RxJava и Android. Плюсы, минусы, подводные камни

ObservableObservable

Page 4: RxJava и Android. Плюсы, минусы, подводные камни

ObservableObservable

Observables fill the gap by being the ideal implementation of access toasynchronous sequences of multiple items

single items multiple items

synchronous T getData() Iterable<T> getData()

asynchronous Future<T> getData() Observable<T> getData()

Page 5: RxJava и Android. Плюсы, минусы, подводные камни

PrimitivesPrimitives public interface Observer <T> { void onCompleted(); void onError(java.lang.Throwable throwable); void onNext(T t); }

public class Observable <T> { public final static <T> Observable<T> create(OnSubscribe<T> f) public rx.Subscription subscribe(rx.Observer<? super T> observer) // ... }

public static interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {}

public interface Subscription { public void unsubscribe(); public boolean isUnsubscribed();}

public abstract class Subscriber<T> implements Observer<T>, Subscription {...}

Page 6: RxJava и Android. Плюсы, минусы, подводные камни

Create ObservableCreate Observable

Observable<String> o = Observable.from("a", "b", "c");Observable<String> o = Observable.just("one object");

Observable<String> o = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("World"); subscriber.onCompleted(); } });

Page 7: RxJava и Android. Плюсы, минусы, подводные камни

Transform ObservableTransform Observable

o.skip(10).take(5) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(); } }) .subscribe(new Action1<String>() { @Override public void call(String s) { Log.d("rx", s) } });

Page 8: RxJava и Android. Плюсы, минусы, подводные камни

SchedulersSchedulers

o.skip(10).take(5) .observeOn(Schedulers.computation()) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<String>() { @Override public void call(String s) { Log.d("rx", s) } });

Page 9: RxJava и Android. Плюсы, минусы, подводные камни

RxJava: Library, RxJava: Library, not Framework

Page 10: RxJava и Android. Плюсы, минусы, подводные камни

LambdaLambdathe new old thingthe new old thing

Page 11: RxJava и Android. Плюсы, минусы, подводные камни

Used lambdas before it was coolUsed lambdas before it was cool

Page 12: RxJava и Android. Плюсы, минусы, подводные камни

Observable<Integer> xs = Observable.just(1, 2, 3, 4, 5, 6, 7); xs.filter(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer x) { return x % 2 == 0; } }).map(new Func1<Integer, Integer>() { @Override public Integer call(Integer x) { return x + 10; } }).subscribe(new Action1<Integer>() { @Override public void call(Integer x) { Rx.this.print(x); } });

Anonymous classes hide logic behind thenoise

More code - more bugs

Page 13: RxJava и Android. Плюсы, минусы, подводные камни

MapMap

FilterFilter

Page 14: RxJava и Android. Плюсы, минусы, подводные камни

Observable<Integer> xs = Observable.just(1, 2, 3, 4, 5, 6, 7); xs.filter(x -> x % 2 == 0) .map(x -> x + 10) .subscribe(x -> print(x));

Less noiseClear logicConcise

Page 15: RxJava и Android. Плюсы, минусы, подводные камни

RetrolambdaRetrolambdagradle-retrolambdagradle-retrolambda

Once more:

Java8 for android developmentNative support in Android StudioMagic

Page 16: RxJava и Android. Плюсы, минусы, подводные камни

One more thingOne more thingIt's safer

static class MyActivity extends Activity { Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String str = "uamobile"; handler.postDelayed(new Runnable() { @Override public void run() { Log.d("Rx", str); } }, 10000); } }

class MyActivity$1 extends java.lang.Object implements java.lang.Runnable{final java.lang.String val$str;

final MyActivity this$0;

MyActivity$1(cMyActivity, java.lang.String); Code: 0: aload_0 1: aload_1 2: putfield 5: aload_0 6: aload_2 7: putfield 10: aload_0 11: invokespecial 14: return

public void run(); Code: 0: ldc 2: aload_0 3: getfield 6: invokestatic 9: pop 10: return}

Page 17: RxJava и Android. Плюсы, минусы, подводные камни

One more thingOne more thingIt's safer

static class MyActivity extends Activity { Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String str = "uamobile"; handler.postDelayed(() -> Log.d("Rx", str), 10000); } }

final class MyActivity$$Lambda$1 extends java.lang.Object implements java.lang.Runnable{public void run(); Code: 0: aload_0 1: getfield 4: invokestatic 7: return

public static java.lang.Runnable lambdaFactory$(java.lang.String); Code: 0: new 3: dup 4: aload_0 5: invokespecial 8: areturn}

Page 18: RxJava и Android. Плюсы, минусы, подводные камни

One abstractionOne abstractionto rule them allto rule them all

Page 19: RxJava и Android. Плюсы, минусы, подводные камни

final View.OnClickListener clickListener = new View.OnClickListener() { @Override public void onClick(View view) { //... } };

new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //... } };

com.squareup.okhttp.Callback okCallback = new com.squareup.okhttp.Callback() { @Override public void onFailure(Request request, IOException e) { //... } @Override public void onResponse(Response response) throws IOException { //... } };

Observable<Void> click

Observable<Intent> broadcasts

Observable<Response> responses

Page 20: RxJava и Android. Плюсы, минусы, подводные камни

ViewObservable.text(editText) //Observable<OnTextChangeEvent> .map(event -> event.text) //Observable<CharSequence> .filter(cs -> !TextUtils.isEmpty(cs)) //Observable<CharSequence> .map(cs -> cs.toString().toUpperCase()) //Observable<String> .take(5) //Observable<String> .subscribe(str -> handleString(str)); //Subscription

editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {} @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {} int count = 5; @Override public void afterTextChanged(Editable editable) { if (!TextUtils.isEmpty(editable)) { String transformed = editable.toString().toUpperCase(); handleString(transformed); count--; if (count == 0) { editText.removeTextChangedListener(this); } } } });

Events as a streamEvents as a stream

Page 21: RxJava и Android. Плюсы, минусы, подводные камни

take(n)take(n)

Page 22: RxJava и Android. Плюсы, минусы, подводные камни

Abstract over event producerAbstract over event producer

Observable<CharSequence> input = ViewObservable.text(editText) .map(event -> event.text);

input.filter(cs -> !TextUtils.isEmpty(cs)) .map(cs -> cs.toString().toUpperCase()) .take(5) .subscribe(str -> handleString(str));

Observable<CharSequence> input = AndroidObservable.fromBroadcast(ctx, intentFilter) .map(intent -> intent.getStringExtra("magic_str"));

Call call = okHttpClient.newCall(...); Observable<String> input = executeOkCall(call) .map(response -> { try { return response.body().string(); } catch (IOException e) { throw new RuntimeException(e); } }) .flatMap(bodyString -> Observable.from(bodyString.split("\n"))) .observeOn(AndroidSchedulers.mainThread());

Page 23: RxJava и Android. Плюсы, минусы, подводные камни

flatMapflatMap

Page 24: RxJava и Android. Плюсы, минусы, подводные камни

public Observable<String> fromOkCall1(Call call){ return Observable.create(subscriber -> { try { Response response = call.execute(); subscriber.onNext(response.body().string()); subscriber.onCompleted(); } catch (IOException e) { subscriber.onError(e); } }); }

Page 25: RxJava и Android. Плюсы, минусы, подводные камни

public Observable<String> fromOkCall2(Call call){ return Observable.create(subscriber -> { call.enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { subscriber.onError(e); } @Override public void onResponse(Response response) throws IOException { try { subscriber.onNext(response.body().string()); subscriber.onCompleted(); } catch (IOException e) { subscriber.onError(e); } } }); }); }

Page 26: RxJava и Android. Плюсы, минусы, подводные камни

LazynessLazyness

Page 27: RxJava и Android. Плюсы, минусы, подводные камни

Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello"); subscriber.onNext("uamobile"); subscriber.onCompleted(); } });

Simple (cold) ObservableSimple (cold) Observable

observable.subscribe(new Action1<String>() { @Override public void call(String s) { handleString(s); } });

Page 28: RxJava и Android. Плюсы, минусы, подводные камни

Observable<String> lastNcRead = mBriefcaseHelper.getBriefcaseObservable() .flatMap(list -> { Observable<Briefcase> briefcaseObservable = Observable.from(list); Observable<String> maxRead = briefcaseObservable .ofType(NotificationReadBriefcase.class) .map(b -> b.attrs.version) .firstOrDefault("0"); Observable<String> maxClear = briefcaseObservable .ofType(NotificationClearBriefcase.class) .map(b -> b.attrs.version) .firstOrDefault("0"); return Observable.zip( maxRead, maxClear, (a, b) -> a.compareTo(b) > 0 ? a : b ); }); Observable<Boolean> hasNotificationsObservable = mDatastore.getNotifications() .switchMap(notifications -> lastNcRead.flatMap(lastNC -> Observable.from(notifications) .takeWhile(n -> n.getId().compareTo(lastNC) > 0) .isEmpty().map(empty -> !empty) ) ).observeOn(AndroidSchedulers.mainThread());

hasNotificationsObservable .subscribe(hasNotifications -> view.setEnabled(hasNotifications));

Do nothing

Execute allabove

Page 29: RxJava и Android. Плюсы, минусы, подводные камни

Cold and lazyCold and lazy

Observable<Integer> cold = Observable.create(subscriber -> { int result = new Random().nextInt(50); subscriber.onNext(result); subscriber.onCompleted(); }); cold.subscribe(n -> print(n)); //13 cold.subscribe(n -> print(n)); //42 =(

Page 30: RxJava и Android. Плюсы, минусы, подводные камни

Hot and sharedHot and shared

ConnectableObservable<Integer> cold = Observable.<Integer>create(subscriber -> { int result = new Random().nextInt(50); subscriber.onNext(result); subscriber.onCompleted(); }).publish(); cold.subscribe(n -> print(n)); //40 cold.subscribe(n -> print(n)); //40 cold.connect();

Page 31: RxJava и Android. Плюсы, минусы, подводные камни

publish & connectpublish & connect

Page 32: RxJava и Android. Плюсы, минусы, подводные камни

interface Response { List<String> getData(); Observable<Response> next(); } public Observable<Response> apiCall(...) {...} public Observable<String> loadAll(Observable<Response> source) { if (source == null) { return Observable.empty(); } else { return source.flatMap(resp -> { List<String> data = resp.getData(); Observable<Response> next = resp.next(); return Observable.concat( Observable.from(data), loadAll(next) ); }); } }

Observable<String> all = loadAll(apiCall(...)) .filter(str -> str.contains("#uamobile")) .take(20) .timeout(1, TimeUnit.MINUTES);

Tip: Use infinite squencesTip: Use infinite squences

Page 33: RxJava и Android. Плюсы, минусы, подводные камни

ObservableObservable.empty().empty()

concatconcat

Page 34: RxJava и Android. Плюсы, минусы, подводные камни

Tip: be careful with evaluation timeTip: be careful with evaluation time

Observable<MyHeavyData> dataObservable = loadDataFromNetwork() .timeout(10, TimeUnit.SECONDS) .onErrorResumeNext(Observable.just(loadDataFromStorage()));

Observable<MyHeavyData> dataObservable = loadDataFromNetwork() .timeout(10, TimeUnit.SECONDS) .onErrorResumeNext(ex -> Observable.just(loadDataFromStorage()));

Page 35: RxJava и Android. Плюсы, минусы, подводные камни

onErrorResumeNext

timeouttimeout

Page 36: RxJava и Android. Плюсы, минусы, подводные камни

CancelationCancelation

Page 37: RxJava и Android. Плюсы, минусы, подводные камни

ProactiveProactive

Observable<Integer> items = Observable.create(subscriber -> { int i = 0; while (true) { if(subscriber.isUnsubscribed()) return; subscriber.onNext(i++); } }); items.take(10).subscribe(x -> print(x));

Page 38: RxJava и Android. Плюсы, минусы, подводные камни

ReactiveReactive

final LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(ctx); Observable<Intent> broadcasts = Observable.create(subscriber -> { final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { subscriber.onNext(intent); } }; final Subscription subscription = Subscriptions.create( () -> localBroadcastManager.unregisterReceiver(broadcastReceiver) ); subscriber.add(subscription); localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter); });

Page 39: RxJava и Android. Плюсы, минусы, подводные камни

OkHttp exampleOkHttp example

public Observable<String> fromOkCall3(Call call){ return Observable.create(subscriber -> { subscriber.add(Subscriptions.create(() -> call.cancel())); call.enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { subscriber.onError(e); } @Override public void onResponse(Response response) throws IOException { try { subscriber.onNext(response.body().string()); subscriber.onCompleted(); } catch (IOException e) { subscriber.onError(e); } } }); }); }

Page 40: RxJava и Android. Плюсы, минусы, подводные камни

Functional CollectionsFunctional CollectionsRight here, in your javaRight here, in your java

Observables can be used to operateover simple collections

Page 41: RxJava и Android. Плюсы, минусы, подводные камни

Collection as ObservableCollection as Observable

Build-in operatorsNo intermediate allocationsFunctional style

Page 42: RxJava и Android. Плюсы, минусы, подводные камни

How to useHow to use

1. Create Observable from IterableObservable.from(list)

2. Transform it using build-in operators.filter(...), .map(...), .flatMap(...)

3. Transform to BlockingObservable and get resut.toBlocking.toList().single().toBlocking.single().toBlocking.toIterable()

Page 43: RxJava и Android. Плюсы, минусы, подводные камни

class GameSession { String user; int score; } List<Pair<String, Integer>> leaderboard = Observable.from(gameSessions) .filter(session -> session.score != 0) .groupBy(session -> session.user) .flatMap(groupedSessions -> groupedSessions.map(session -> session.score) .reduce(0, (n1, n2) -> n1 + n2) .map(totalScore -> Pair.create(groupedSessions.getKey(), totalScore)) ) .take(10) .toSortedList((pair1, pair2) -> pair2.second.compareTo(pair1.second)) .toBlocking() .single();

Example: LeaderboardExample: Leaderboard

Page 44: RxJava и Android. Плюсы, минусы, подводные камни

groupBygroupBy

Page 45: RxJava и Android. Плюсы, минусы, подводные камни

reducereduce

Page 46: RxJava и Android. Плюсы, минусы, подводные камни

Questions?Questions?

Page 47: RxJava и Android. Плюсы, минусы, подводные камни

LinksLinkshttps://github.com/ReactiveX/RxJava/wikiYour Mouse is a DatabasePrinciples of Reactive ProgrammingTop 7 Tips for RxJava on Android