Download - GKAC 2015 Apr. - RxAndroid
Rx
The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators.
https://msdn.microsoft.com/en-us/data/gg577609
RxJava
RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.
Copyright 2013 Netflix, Inc.(https://github.com/ReactiveX/RxJava)
onNext
An Observable calls this method whenever the Observable emits an item. This method takes as a parameter the item emitted by the Observable.
onError
An Observable calls this method to indicate that it has failed to generate the expected data or has encountered some other error. This stops the Observable and it will not make further calls to onNext or onCompleted. The onError method takes as its parameter an indication of what caused the error.
onCompleted
An Observable calls this method after it has called onNext for the final time, if it has not encountered any errors.
Simple ObservableObservable<String> simpleObservable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello RxAndroid !!"); subscriber.onCompleted(); } });
Simple(?) SubscribersimpleObservable .subscribe(new Subscriber<String>() { @Override public void onCompleted() { Log.d(TAG, "complete!"); } (cont.)
Subscriber @Override public void onError(Throwable e) { Log.e(TAG, "error: " + e.getMessage()); } @Override public void onNext(String text) { ((TextView) findViewById(R.id.textView)).setText(text); } });
simpleObservable .subscribe(new Action1<String>() { @Override public void call(String text) { ((TextView) findViewById(R.id.textView)).setText(text); } });
More simple subscriber #1
simpleObservable .subscribe(new Action1<String>() { ... }, new Action1<Throwable>() { ... }, new Action0() { ... });
More simple subscriber #2
simpleObservable .subscribe(new Action1<String>() { ... }, new Action1<Throwable>() { ... });
More simple subscriber #3
Operator: Map
.map(new Func1<Integer, Integer>() { @Override public Integer call(Integer value) { return value * 10; }})
Operator: mapsimpleObservable
.map(new Func1<String, String>() {
@Override
public String call(String text) {
return text.toUpperCase();
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String text) {
((TextView) findViewById(R.id.textView)).setText(text);
}
});
Operator: mapsimpleObservable
.map(new Func1<String, Integer>() {
@Override
public Integer call(String text) {
return text.length();
}
})
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer length) {
((TextView) findViewById(R.id.textView)).setText("length: " + length);
}
});
More operators
http://reactivex.io/documentation/operators.html
Observer utilities
Observable<String> simpleObservable = Observable.just("Hello RxAndroid");
see also: Observable.from (for collections)
LambdaObservable<String> simpleObservable = Observable.just("Hello Lambda!!");
simpleObservable .map(text -> text.length()) .subscribe(length ->((TextView)findViewById(R.id.textView)) .setText("length: " + length));
Lambda: stage 1.map(new Func1<String, Integer>() { @Override public Integer call(String text) { return text.length(); } })
buildscript { repositories { jcenter() }
dependencies { classpath 'me.tatarka:gradle-retrolambda:2.5.0' }}
apply plugin: 'com.android.application'apply plugin: 'me.tatarka.retrolambda'
build.gradle
android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } … }
build.gradle
ViewObservable.clicksViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewB yId(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
Observable<Event>ViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
Map: Event -> IntegerViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
SubscriberViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
Observable.mergeObservable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left");
Observable<String> rights = ViewObservable.clicks(findViewById(R.id.rightButton)) .map(event -> "right");
Observable<String> together = Observable.merge(lefts, rights);
together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text));
together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
2 ObservablesObservable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left");
Observable<String> rights = ViewObservable.clicks(findViewById(R.id.rightButton)) .map(event -> "right");
Observable<String> together = Observable.merge(lefts, rights);
together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text));
together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
Observable.mergeObservable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left");
Observable<String> rights = ViewObservable.clicks(findViewById(R.id.rightButton)) .map(event -> "right");
Observable<String> together = Observable.merge(lefts, rights);
together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text));
together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
2 SubscriberObservable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left");
Observable<String> rights = ViewObservable.clicks(findViewById(R.id.rightButton)) .map(event -> "right");
Observable<String> together = Observable.merge(lefts, rights);
together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text));
together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
Operator: scanObservable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1);Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1);
Observable<Integer> together = Observable.merge(minuses, pluses);
together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString()));together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
2 Observables (+map)Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1);Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1);
Observable<Integer> together = Observable.merge(minuses, pluses);
together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString()));together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
MergeObservable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1);Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1);
Observable<Integer> together = Observable.merge(minuses, pluses);
together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString()));together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
1st scan + subscriber: counterObservable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1);Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1);
Observable<Integer> together = Observable.merge(minuses, pluses);
together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString()));together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
2nd scan + subscriber: sumObservable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1);Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1);
Observable<Integer> together = Observable.merge(minuses, pluses);
together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString()));together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
Scheduler
Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
Observable runs on “newThread”
Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
Subscriber runs on “mainThread”
Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
Notice:Subscriber doesn’t run on mainThread immediately.
Boundnew Thread(() -> { final Handler handler = new Handler(); Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.handlerThread(handler)) .subscribe(/* an Observer */)}, "custom-thread-1").start();
Bound to this threadnew Thread(() -> { final Handler handler = new Handler(); Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.handlerThread(handler)) .subscribe(/* an Observer */)}, "custom-thread-1").start();
Varieties of Scheduler (RxJava)
● Schedulers.computation( )● Schedulers.from(executor)● Schedulers.immediate( )● Schedulers.io( )● Schedulers.newThread( )● Schedulers.trampoline( )
Varieties of Scheduler (RxAndroid)
● AndroidSchedulers.mainThread()● AndroidSchedulers.handlerThread(handler)
Use Schedulers without Observableworker = Schedulers.newThread().createWorker();
worker.schedule(() -> { yourWork();});
Unsubscirbe SubscriptionSubscription s = Observable.from("one", "two", "three", "four", "five").subscribe(/* Observer */)
s.unsubscribe()
Unsubscirbe CompositeSubscriptionprivate void doSomething() { mCompositeSubscription.add( s.subscirbe(/* observer*/);}@Overrideprotected void onDestroy() { super.onDestroy(); mCompositeSubscription.unsubscribe();}
Recursive Schedulersworker = Schedulers.newThread().createWorker();worker.schedule(() { yourWork(); worker.schedule(this);});
// It will stop your workerworker.unsubscribe();
Retrofit without RxAndroid
@GET(“/user/{id}”)void getUser( @path(“id”) long id, Callback<User> callback);
You can combie Retrofit’s Observables
Observable.zip( service.getUserPhoto(id), service.getPhotoMetadata(id), (photo, metadata) -> createPhotoWithData(photo, metadata)) .subscribe(photoWithData -> showPhoto(photoWithData));
Battery with RxAndroid@RpcObject(uri="http://ip.jsontest.com/")public class JsonTest { @Response public String ip;}
Observable<JsonTest> jsonTestObservable = Rpc.invokeRx(new AndroidExecutionContext(this), jsonTest);
jsonTestObservable.subscribe(jsonText -> { Log.d("MAIN", jsonTest.ip);});
Subject
A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable.
PublishSubjectPublishSubject publishSubject<View> = PublishSubject.create();view.setOnClickListener(view -> publishSubject.onNext(view));
Observable<View> observable = publishSubject.asObservable();
Use ReplaySubject for cachingReplaySubject<ImageSearchResult> mSubject = ReplaySubject.create();mSubject = ReplaySubject.create();mPageNumber = 1;
mApi.searchImage( "json", API_KEY, query, ITEM_COUNT, mPageNumber) .observeOn(AndroidSchedulers.mainThread()) .subscribe(mSubject::onNext);
See also
https://github.com/GDG-Korea/HelloRxhttps://github.com/hl5pma/RxExample