Upload
sam-lee
View
2.573
Download
6
Embed Size (px)
DESCRIPTION
The original slide is here... http://misgod.github.io/Slide-FRP-Android Introduce to Functional Reactive Programming on Android with RxJava/RxAndroid.
Citation preview
Functional Reactive Programming onAndroidSam Lee2014/11/22 @Mosut x Taina.py x FP
http://misgod.github.io/Slide-FRP-Android/ 第 1 ⾴
About MeSam Lee ([email protected])
A software engineer lives in Tainan
Work for htc
Most used Clojure, Scala and Java
Interested in Functional Programming
Interested in Machine Learning and Data Analyst
·
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 2 ⾴
What Is Functional Reactive ProgrammingA style of programming based on two key ideas: continuous time-varying behaviors, andevent-based reactivity
Try it
aObservable.map(x -> x*x) //Square
.reduce((a, b) -> a+b) //Sum
.subscribe(x -> println(x)); //Show
http://misgod.github.io/Slide-FRP-Android/ 第 3 ⾴
Why Functional Reactive Programming
Writing concurrent programs correct is di�cult.·
You can transform & compose asynchronous operations.
High-level abstractions
Standard error handling
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 4 ⾴
Say Hello to Rx familyJava: RxJava
Scala: RxScala
Clojure: RxClojure
Groovy: RxGroovy
JRuby: RxJRuby
JavaScript: RxJS
C#: Rx.NET
C#(Unity): UniRx
C++: RxCpp
Ruby: Rx.rb
Python: RxPY
Kotlin: RxKotlin
·
·
·
·
·
·
·
·
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 5 ⾴
Introduce to RxJavahttps://github.com/ReactiveX/RxJava
RxJava is a JVM implementation of Reactive Extensions
RxJava extends Observer pattern to support data/event and compose operators inabstract.
Built by Net�ix
Support Java 6+ & Android 2.3+
Java 8 lambda support
·
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 6 ⾴
Asynchronous sequences of multiple items
SINGLE ITEMS MULTIPLE ITEMS
synchronous T getData() Iterable<T> getData()
asynchronous Future<T> getData() Observable<T> getData()
http://misgod.github.io/Slide-FRP-Android/ 第 7 ⾴
Observable is "dual" to IterableEVENT ITERABLE (PULL) OBSERVABLE (PUSH)
retrieve data T next() onNext(T)
discover error throws Exception onError(Exception)
complete !hasNext() onCompleted()
http://csl.stanford.edu/~christos/pldi2010.�t/meijer.duality.pdf
http://misgod.github.io/Slide-FRP-Android/ 第 8 ⾴
Observable & Observer
pushman.subscribe(new Action1<Integer>() {
@Override
public void call(Integer x) {
println("receive: " + x);
}
});
http://misgod.github.io/Slide-FRP-Android/ 第 9 ⾴
Observer pattern
http://misgod.github.io/Slide-FRP-Android/ 第 10 ⾴
Functional Reactive
http://misgod.github.io/Slide-FRP-Android/ 第 11 ⾴
Lambda Expression
http://misgod.github.io/Slide-FRP-Android/ 第 12 ⾴
OriginalaObservable.filter(new Func1<Integer, Boolean>() {
public Boolean call(Integer n) {
return n % 2 == 0;
}
})
.map(new Func1<Integer, Integer>() {
public Integer call(Integer n) {
return n * n;
}
})
.subscribe(new Action1<Integer>() {
public void call(Integer n) {
System.out.println(n);
}
});
WTF...·
http://misgod.github.io/Slide-FRP-Android/ 第 13 ⾴
Java 8 lambda
aObservable.filter(n -> n % 2 == 0)
.map(n -> n * n)
.subscribe(System.out::println);
but ... no java8 on Davilk(Android)·
http://misgod.github.io/Slide-FRP-Android/ 第 14 ⾴
RetrolambdaRetrolambda lets you run Java 8 code with lambda expressions and
method references on Java 7 or lower.
Retrolambda
Gradle plugin
·
https://github.com/orfjackal/retrolambda-
·
https://github.com/evant/gradle-retrolambda-
http://misgod.github.io/Slide-FRP-Android/ 第 15 ⾴
Retrolambda
http://misgod.github.io/Slide-FRP-Android/ 第 16 ⾴
rx.Observable
http://misgod.github.io/Slide-FRP-Android/ 第 17 ⾴
Creating Observable - just
Observable<List<String>> ob = Observable.just(aList);
Observable<String> ob2 = Observable.just("Some String");
http://misgod.github.io/Slide-FRP-Android/ 第 18 ⾴
Creating Observable - from
List<String> aList = ...;
Observable<String> ob = Observable.from(aList);
http://misgod.github.io/Slide-FRP-Android/ 第 19 ⾴
Creating Observable - create
http://misgod.github.io/Slide-FRP-Android/ 第 20 ⾴
Creating Observable - createob = Observable.create(subscriber -> {
try {
for (String s : aList) {
if (subscriber.isUnsubscribed())
return;
subscriber.onNext(s);
}
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
});
http://misgod.github.io/Slide-FRP-Android/ 第 21 ⾴
Creating Observable ...
https://github.com/ReactiveX/RxJava/wiki/Creating-Observables
repeat( )
range( )
interval( )
timer( )
empty( )
error( )
·
·
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 22 ⾴
Transforming Observables - map
http://misgod.github.io/Slide-FRP-Android/ 第 23 ⾴
Transforming Observables - mapObservable.range(0, 5)
.map(x -> toBinaryString(x*x))
.subscribe(s -> println(s),
err -> err.printStackTrace(),
() -> println("done"));
0
1
100
1001
10000
done
http://misgod.github.io/Slide-FRP-Android/ 第 24 ⾴
Transforming Observables - flatmap
http://misgod.github.io/Slide-FRP-Android/ 第 25 ⾴
Transforming Observables - flatmapObservable.range(1, 3)
.flatMap(x -> Observable.just(x).repeat(x))
.subscribe(System.out::println);
1
2
2
3
3
3
Observable is a Monad·
unit (return) ==> just
join (bind, >>=) ==> �atmap
-
-
http://misgod.github.io/Slide-FRP-Android/ 第 26 ⾴
Transforming Observables - concatmap
http://misgod.github.io/Slide-FRP-Android/ 第 27 ⾴
Filtering Observables
http://misgod.github.io/Slide-FRP-Android/ 第 28 ⾴
Filtering ObservablesObservable.range(0, 10)
.filter(x -> (x % 2) == 0)
.subscribe(System.out::println);
0
2
4
6
8
http://misgod.github.io/Slide-FRP-Android/ 第 29 ⾴
Filtering Observables
https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables
distinct( )
�rst( )
take( )
skip( )
elementAt( )
sample( )
more...
·
·
·
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 30 ⾴
Aggregate Operators - reduce
http://misgod.github.io/Slide-FRP-Android/ 第 31 ⾴
Aggregate Operators - reduceObservable.range(1, 10)
.reduce((a, b) -> a*b)
.subscribe(System.out::println);
3628800
http://misgod.github.io/Slide-FRP-Android/ 第 32 ⾴
Aggregate Operators - concat
http://misgod.github.io/Slide-FRP-Android/ 第 33 ⾴
Combining Observables - merge
http://misgod.github.io/Slide-FRP-Android/ 第 34 ⾴
Combining Observables - mergeObservable<String> lower = Observable.from(new String[]{"a", "b", "c"});
Observable<String> upper = Observable.from(new String[]{"A", "B", "C"});
Observable.merge(lower,upper).subscribe(System.out::println);
/* Or */
lower.mergeWith(upper).subscribe(System.out::println);
a
b
A
c
B
C
http://misgod.github.io/Slide-FRP-Android/ 第 35 ⾴
Combining Observables - startWith
http://misgod.github.io/Slide-FRP-Android/ 第 36 ⾴
Combining Observables - startWithObservable<String> lower = Observable.from(new String[]{"a", "b", "c"});
Observable<String> upper = Observable.from(new String[]{"A", "B", "C"});
lower.startWith(upper).subscribe(System.out::println);
A
B
C
a
b
c
http://misgod.github.io/Slide-FRP-Android/ 第 37 ⾴
Combining Observables - zip
http://misgod.github.io/Slide-FRP-Android/ 第 38 ⾴
Combining Observables - zipObservable<String> lower = Observable.from(new String[]{"a", "b", "c"});
Observable<String> upper = Observable.from(new String[]{"A", "B", "C"});
Observable.zip(lower, upper, Pair::create)
.map(pair -> pair.first +"_" +pair.second)
.subscribe(System.out::println);
a_A
b_B
c_C
http://misgod.github.io/Slide-FRP-Android/ 第 39 ⾴
Error Handling
https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
OnErrorResumeNext
OnErrorReturn
retry
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 40 ⾴
ThreadingobserveOnspecify on which Scheduler a Subscriber should observe the Observable
subscribeOnspecify which Scheduler an Observable should use when its subscription is invoked
https://github.com/ReactiveX/RxJava/wiki/Scheduler
http://misgod.github.io/Slide-FRP-Android/ 第 41 ⾴
SubjectsA Subject = Subscriber + Observable
http://misgod.github.io/Slide-FRP-Android/ 第 42 ⾴
Subjects
https://github.com/ReactiveX/RxJava/wiki/Subject
PublishSubject
BehaviorSubject
AsyncSubject
ReplaySubject
·
·
·
·
http://misgod.github.io/Slide-FRP-Android/ 第 43 ⾴
Subjects - How to useSubscriber -----> Observable
PublishSubject<Integer> subject = PublishSubject.create();
//Observable
subject.map(x -> x*x)
.subscribe(o -> println(o));
subject.map(x -> x+1)
.subscribe(o -> println(o));
//Subscriber
subject.onNext(11);
subject.onNext(11);
subject.onCompleted();
Masking a Subject as an Observable - aSubject.asObservable()·
http://misgod.github.io/Slide-FRP-Android/ 第 44 ⾴
Hot and Cold Observables
A “cold” Observable waits to emitting items untilobserver subscribes , and so an observer can see thewhole sequenceg.
·
A “hot” Observable may begins emitting items as soonas it is created.
·
http://misgod.github.io/Slide-FRP-Android/ 第 45 ⾴
RxJava on Android
http://misgod.github.io/Slide-FRP-Android/ 第 46 ⾴
About Android DevelopmentDeveloping a robust app is painful
Developing a good UX app is painful
Thread, Executor, Handler, AsyncTask, Loader ...
RxJava can mitigate your pain
Retro�t supports methods with a return type of rx.Observable
·
life cycle, async, event, state, threading, error handling-
·
interactive, realtime, smooth, animation-
·
·
·
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
http://misgod.github.io/Slide-FRP-Android/ 第 47 ⾴
RxAndroid
Don't forget to unsubscribe to avoid memory leak!
Android speci�c bindings for RxJava.
Scheduler on main UI thread or a given Android Handler thread.
Reactive components for common Android use cases and UI widgets
·
https://github.com/ReactiveX/RxAndroid-
·
AndroidSchedulers
HandlerThreadScheduler
-
-
·
AndroidObservable
ViewObservable
-
-
http://misgod.github.io/Slide-FRP-Android/ 第 48 ⾴
Thinking in Functional Reactive
http://misgod.github.io/Slide-FRP-Android/ 第 49 ⾴
Case 1 - Say Hello to Callback Hell/* API */
void getFromServer(String key, Action1<String> callback);
void getFromDB(String key, Action1<String> callback);
/* Code */
btnClick.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
getFromDB("myid", new Action1<String>() {
public void call(String s) {
getFromServer(s, new Action1<String>() {
public void call(final String s) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(context, s, LENGTH_LONG).show();
}
});
/* ... a lot of }) ... */
http://misgod.github.io/Slide-FRP-Android/ 第 50 ⾴
Case 1 - Say Hello to Callback HellUsing Lambda expression
btnClick.setOnClickListener(view ->
getFromDB("myid",
s -> getFromServer(s,
x -> runOnUiThread(
() -> Toast.makeText(context, x, LENGTH_LONG).show()))));
Shorter but not easy to read·
http://misgod.github.io/Slide-FRP-Android/ 第 51 ⾴
Case 1 - Say Goodbye to Callback HellUsing rx.Observable
/* API */
Observable<String> getFromServer(String key);
Observable<String> getFromDB(String key);
/* Code */
ViewObservable.clicks(btnClick)
.map(x -> "myid")
.observeOn(Schedulers.io())
.flatMap(this::getFromDB)
.flatMap(this::getFromServer)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(x -> Toast.makeText(context, x, LENGTH_LONG).show());
http://misgod.github.io/Slide-FRP-Android/ 第 52 ⾴
Case 2 - All In One searchRequirements
A search engine includes google/yahoo/bing search results.1.
Should search di�erent engines in parallel2.
Retry 3 times when search fail3.
remove redundant url4.
Observable<SearchResult> g = googleSearch.search(keyword).retry(3);
Observable<SearchResult> b = bingSearch.search(keyword).retry(3);
Observable<SearchResult> y = yahooSearch.search(keyword).retry(3);
Observable.merge(g, b, y)
.distinct(site -> site.url)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(site -> appendDataForUI() ,
err -> errorhandle(err));
http://misgod.github.io/Slide-FRP-Android/ 第 53 ⾴
Case 3 - A simple EventBusPublishSubject<Object> subject = PublishSubject.create(); //Global Singleton
//...In Class A...
subject.filter(x -> x instanceof DataUpdateAction)
.subscribe( x -> ... doSomething ...);
//...In Class B...
subject.filter(x -> x instanceof DeleteAction)
.subscribe( x -> ... doSomething ...);
//...In Class C...
subject.filter(x -> x instanceof RefreshAction)
.subscribe( x -> ... doSomething ...);
subject.onNext(aDataUpdateAction);
subject.onNext(aDataUpdateAction;
subject.onNext(aRefreshAction);
http://misgod.github.io/Slide-FRP-Android/ 第 54 ⾴
Case 4 - Input Suggestion
private Observable<String> getSuggestion(String prefix) { ... }
Observable<Observable<List<String>>> o =
ViewObservable.text(aEditText)
.map(event -> event.text.toString())
.filter(x -> x.length() > 1)
.observeOn(Schedulers.io())
.map(x -> getSuggestion(x).toList());
Observable.switchOnNext(o)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(lst -> showSuggestion(lst));
http://misgod.github.io/Slide-FRP-Android/ 第 55 ⾴
Conclusion
Functional Reactive makes async/state design straightforward.1.
RxJava is not just a library.2.
The most important is Functional Reactive Thinking.3.
Try It in your next project!4.
http://misgod.github.io/Slide-FRP-Android/ 第 56 ⾴
http://misgod.github.io/Slide-FRP-Android/ 第 57 ⾴