Landi Migration Workshop Traini… · One more example to control thread - Traditionally Landi APOS...
Transcript of Landi Migration Workshop Traini… · One more example to control thread - Traditionally Landi APOS...
RxJava Introduction
JUNE 2017
FUZHOU, CHINA
Why RxJava
3
Show all png image to UI - Traditional
Landi APOS A8 training – June 2017
new Thread() {@Overridepublic void run() {
super.run();for (File folder : folders) {
File[] files = folder.listFiles();for (File file : files) {
if (file.getName().endsWith(".png")) {final Bitmap bitmap = getBitmapFromFile(file);getActivity().runOnUiThread(new Runnable() {
@Overridepublic void run() {
imageCollectorView.addImage(bitmap);}
});}
}}
}}.start();
Logic Nesting
4
Show all png image to UI - RxJava
Landi APOS A8 training – June 2017
Observable.fromArray(folders)
.flatMap(file -> Observable.fromArray(file.listFiles()))
.filter(file -> file.getName().endsWith(".png"))
.map(file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Consumer<Bitmap>) bitmap -> imageCollectorView.addImage(bitmap));
5
Another example to avoid callback hell - Callback
Landi APOS A8 training – June 2017
private void downloadParameters() {EmvParameterDownloader emvParameterDownloader = new EmvParameterDownloader(context);
new TerminalParameterDownloader().download(new Runnable(){@Overridepublic void run() {
emvParameterDownloader.downloadICParameters(new Runnable(){@Overridepublic void run() {
emvParameterDownloader.downloadPublicKeys();}
});}
});}
Callback Hell
6
Another example to avoid callback hell - RxJava
Landi APOS A8 training – June 2017
private Completable downloadParameters() {EmvParameterDownloader emvParameterDownloader = new EmvParameterDownloader(context);new TerminalParameterDownloader(context).download())
.andThen(emvParameterDownloader.downloadICParameters())
.andThen(emvParameterDownloader.downloadPublicKeys());}
7
One more example to control thread - Traditionally
Landi APOS A8 training – June 2017
private void login() {// find userreturn findUser(name, password, new Runnable(){
@Overridepublic void run() {
updateLastLoginUser(user);
runOnUiThread(new Runnable() {@Overridepublic void run() {
user -> handleLogonUser(user);}
});}
});}
8
One more example to control thread - RxJava
Landi APOS A8 training – June 2017
private Completable login() {// find userreturn findUser(name, password)
// update last login user.doOnSuccess(user -> updateLastLoginUser(user))
.observeOn(AndroidSchedulers.mainThread())
.flatMapCompletable(user -> handleLogonUser(user));}
9
One more example to handle error - Traditional
Landi APOS A8 training – June 2017
private Handler handler = new Handler(Looper.getMainLooper()){@Overridepublic void handleMessage(Message msg) {
if(msg.what == DOWNLOAD_IMG){Exception data = (Exception)msg.obj;// show exception
}}
};
public void signIn() {new Thread(new Runnable() {
@Overridepublic void run() {
try {// business logic
}catch (Exception e){Message message = Message.obtain();message.obj = e;message.what = ERROR;
handler.sendMessage(message);}}
}).start();}
10
One more example to handle error - RxJava
Landi APOS A8 training – August 2017
public void signIn() {new SignIn(context).signIn()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableCompletableObserver() {@Overridepublic void onComplete() {
}
@Overridepublic void onError(@NonNull Throwable e) {
// show error}
});}
Process
STEP 04STEP 02
STEP 03 STEP 05STEP 01
How we use in Arke ProjectIntroduce ReactiveX and
RxJava
RxJava Topics
Observable/Observer/Schedue
rler/Error Handling
Learning Path Lambda Expression
12
ReactiveXAn asynchronous and event-based programs solution.
Functional Less is more Async error handling Concurrency made easy
Landi APOS A8 training – June 2017
13
CROSS-PLATFORM
Java: RxJava
JavaScript: RxJS
C#: Rx.NET
C#(Unity): UniRx
Scala: RxScala
Clojure: RxClojure
C++: RxCpp
Lua: RxLua
Ruby: Rx.rb
Python: RxPY
Go: RxGo
Groovy: RxGroovy
JRuby: RxJRuby
Kotlin: RxKotlin
Swift: RxSwift
PHP: RxPHP
Elixir: reaxive
Dart: RxDart
RxNetty RxAndroid RxCocoa
Landi APOS A8 training – June 2017
14
RxJava and RxAndroid
AsyncTask
AsyncTaskLoaderRxJavaThread and Handler
Introduction to RxJava for Android Developers
Intro to RxJavaWhat's different in 2.0
Landi APOS A8 training – June 2017
Why and how to replace Thread and AsyncTask
15
Learning Path & Resouces
Examples
- training / RxJava2-Android-Samples
- kaushikgopal/RxJava-Android-
Samples
- How we use in Arke Project.
Pratice and Google
- Practice in Project
- Google and Thinking
- ReactiveX Official Site
- ReactiveX GitHub
- RxJava GitHub
- RxJava Category in my Blog
- Exploring RxJava 2 for Android
Jake Wharton
- Intro to RxJava for Android
- Intro to RxJava
Video
Guide by Arke Team
Reactive Programming with RxJava
Official Site
Landi APOS A8 training – June 2017
16
Shut up, show me the code
Landi APOS A8 training – June 2017
Observable.fromArray(folders)
.flatMap(file -> Observable.fromArray(file.listFiles()))
.filter(file -> file.getName().endsWith(".png"))
.map(file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Consumer<Bitmap>) bitmap -> imageCollectorView.addImage(bitmap));
17
Primer on RxJava
Landi APOS A8 training – June 2017
// include RxJava librarycompile 'io.reactivex.rxjava2:rxjava:2.0.6'
// include RxAndroid librarycompile 'io.reactivex.rxjava2:rxandroid:2.0.1'
Key Concepts
30
S c h e d u l e r
O b s e r v a b l eO b s e r v e r
/ S u b s c r i b e r
O p e r a t o r
Concepts
Observable
- How to create
- How to subscribe
- How to unsubscribe
- Subject
20
Observable event types
Landi APOS A8 training – June 2017
- OnNext
- onComplete
- onError
OnNext* (OnComplet | OnError)?
21
How to emit events
Landi APOS A8 training – June 2017
Observable<Integer> ints = Observable.create(new ObservableOnSubscribe(){
@Overridepublic void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(5);emitter.onNext(6);emitter.onNext(7);emitter.onComplet();
}});
22
Hot and Cold Observables
Landi APOS A8 training – June 2017
No subscriber, no events emit
Cold
Observable
Event no subscriber, always emit
events
Hot Observable
23
Reactive Types
Landi APOS A8 training – June 2017
1
Single
0
Completable
0,1
Maybe
0..n
Flowable
0..n
Observable
CompletableObserverExampleActivity.javaSingleObserverExampleActivity.java
MaybeObserverExampleActivity.java
FlowableExampleActivity.java
24
Backpressure
Landi APOS A8 training – August 2017
Backpressure No Backpressure
Flowable Observable
Maybe
Single
Completable
When to use Flowable When to use Observable
10k+ events > 1k events
pull-based push-based
25
Reactive Types Transformation
Landi APOS A8 training – June 2017
// Completable to FlowableCompletable.complete().toFlowable();
// Completable to MaybeCompletable.complete().toMaybe();
// Completable to ObservableCompletable.complete().toObservable();
// Completable to SingleCompletable.complete().toSingleDefault(1);
26
Create Observable
Landi APOS A8 training – June 2017
// convert an object or a set of objects into an Observable that emits that or those objectsObservable.just("one");Observable.just(1, 2);
// convert some other object or data structure into an ObservableObservable.fromArray(1, 2, 3);Observable.fromCallable();Observable.fromFuture();
// create an Observable that emits a sequence of integers spaced by a particular time intervalObservable.interval(1, TimeUnit.SECONDS);
// create an Observable that emits a particular range of sequential integersObservable.range(1, 10);
// create Observables that have very precise and limited behaviorObservable.empty();Observable.error();Observable.never();
27
Create Observable with Observable.create()
Landi APOS A8 training – June 2017
Observable<Integer> ints = Observable.create(new ObservableOnSubscribe(){
@Overridepublic void subscribe(ObservableEmitter emitter) throws Exception {
emitter.onNext(5);emitter.onNext(6);emitter.onNext(7);emitter.onComplet();
}});
28
Maybe, Completable, Single, Flowable Creation
Landi APOS A8 training – June 2017
// create Completables that have very precise and limited behavior Completable.complete();Completable.error();Completable.never();Completable.timer();
// convert some other object or data structure into an ObservableCompletable.fromAction();Completable.fromCallable();Completable.fromFuture();Completable.fromRunnable();
// convert some other reactive types into an ObservableCompletable.fromObservable();Completable.fromPublisher();Completable.fromSingle();
Completable.create();
29
Subscribing Observable
Landi APOS A8 training – June 2017
// Subscribe with ObserverObservable.just("one").subscribe(new Observer<String>() {
@Overridepublic void onSubscribe(@NonNull Disposable d) {
Log.d(TAG, "Subscribed");}
@Overridepublic void onNext(@NonNull String s) {
Log.d(TAG, "Got events " + s);}
@Overridepublic void onError(@NonNull Throwable e) {
Log.d(TAG, "Error happened");}
@Overridepublic void onComplete() {
Log.d(TAG, "Completed");}
});
30
Simple Subscribing Observable
Landi APOS A8 training – June 2017
Observable.just("one").subscribe(new Consumer<String>() {@Overridepublic void accept(@NonNull String s) throws Exception {
Log.d(TAG, s);}
});
OnNext
Observable.just("one").subscribe(new Consumer<String>() {@Overridepublic void accept(@NonNull String s) throws Exception {
Log.d(TAG, s);}
}, new Consumer<Throwable>() {@Overridepublic void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, throwable.getMessage());}
});
onNext
onError
Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError,
Action onComplete, Consumer<? super Disposable> onSubscribe)Full
31
Types of Observer
Landi APOS A8 training – June 2017
32
Unsubscribing from Observable
Landi APOS A8 training – June 2017
Observable.just("one", "two").subscribe(new DisposableObserver<String>() {@Overridepublic void onNext(String s) {
if(s.equals("two")){dispose();
}}
@Overridepublic void onError(Throwable e) {
dispose();}
@Overridepublic void onComplete() {
dispose();}
});
33
Subject
Landi APOS A8 training – June 2017
- AsyncSubject
- BehaviorSubject
- PublishSubject
- ReplaySubject
34
AsyncSubject
Landi APOS A8 training – June 2017
AsyncSubjectExampleActivity.java
35
BehaviorSubject
Landi APOS A8 training – June 2017
BehaviorSubjectExampleActivity.java
36
PublishSubject
Landi APOS A8 training – June 2017
PublishSubjectExampleActivity.java
37
ReplaySubject
Landi APOS A8 training – June 2017
Lambda
Expression
39
Use Retrolambda
Landi APOS A8 training – June 2017
321
No Step 3
Download jdk8 and set it as
your default.
JDK8
Project build.gradle
compileOptions {targetCompatibility 1.8sourceCompatibility 1.8
}
In project build.gradle:
Gradle
In module build.gradle:
buildscript {dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.6.0'
}}
apply plugin: 'com.android.application' apply plugin: 'me.tatarka.retrolambda'
40
Syntax of Lambda Expressions
Landi APOS A8 training – June 2017
(a, b) -> {System.out.println("Performing add operation...");return a+b;
}
1. Input parameter
2. Arrow token
3. Code block
a -> a + 1;
41
Lambda Expressions used in RxJava
Landi APOS A8 training – June 2017
getObservable().map(new Function<List<ApiUser>, List<User>>() {
@Overridepublic List<User> apply(List<ApiUser> apiUsers) throws Exception {
return Utils.convertApiUserListToUserList(apiUsers);}
});
Do an exercise: MapExampleActivity.java
Anonymous function demo:
getObservable().map(apiUsers -> Utils.convertApiUserListToUserList(apiUsers));
Replace with Lambda Expression:
Operator
43
Marble Diagrams
Landi APOS A8 training – June 2017
44
Operators Categories
Landi APOS A8 training – June 2017
CATEGORY
06 A toolbox of useful Operators for
working with Observables.
Utility OperatorsCATEGORY
04 Work with multiple source
Observables to create a single
Observable.
CombiningCATEGORY
05 Help to recover from error
notifications from an Observable.
Error Handling Operators
CATEGORY
03 Selectively emit items from a
source Observable.
FilteringCATEGORY
02 Frequently, your initial font choice
is taken out of your hands
TransformingCATEGORY
01 Originate new Observables
Creating
CATEGORY
09 Specialty Observables that have more
precisely-controlled subscription
dynamics.
ConnectableCATEGORY
07 Evaluate one or more
Observables or items emitted by
Observables.
CATEGORY
08 Operate on the entire sequence
of items emitted by an
Observable
Mathematical and AggregateConditional
ReactiveX - Operators Which Operator do I use?
Filtering Operators
- filter
- distinct
- distinctUtilChanged
46
Filtering
Landi APOS A8 training – June 2017
47
filter example
Landi APOS A8 training – June 2017
Observable.just(1, 2, 3, 4, 5).filter(item -> item < 4)).subscribe(new DisposableObserver<Integer>() {
@Overridepublic void onNext(Integer item) {
System.out.println("Next: " + item);}
@Overridepublic void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());}
@Overridepublic void onComplete() {
System.out.println("Sequence complete.");}
});
48
Dropping Duplicates Using distinct() and distinctUntilChanged()
Landi APOS A8 training – August 2016
Observable.just(1, 2, 1, 1, 2, 3).distinct(); // [1, 2, 3]
Observable.just(1, 2, 1, 1, 2, 3).distinctUntilChanged(); // [1, 2, 1, 2, 3]
Utility Operators
- delay
50
Postponing Events Using the delay()
Landi APOS A8 training – June 2017
just(x, y, z).delay(1, TimeUnit.SECONDS);
Transforming Operators
- map
- flatMap
- concatMap
52
map() Operator
Landi APOS A8 training – June 2017
53
Map example
Landi APOS A8 training – June 2017
MapExampleActivity.java
Observable<Status> tweets = //...Observable<Date> dates = tweets.map(status ->
status.getCreatedAt());
Observable<Instant> instants = tweets.map(Status::getCreatedAt).map((Date d) -> d.toInstant());
54
Wrapping Up Using flatMap()
Landi APOS A8 training – June 2017
55
Flatmap example - Flatten students to course list
Landi APOS A8 training – June 2017
List<Student> students = new ArrayList<Student>();// students.add...
Observable.from(students).flatMap(student -> Observable.from(student.getCoursesList());).subscribe(course -> Log.i(TAG, course.getName()));
56
Flatmap exercise
Landi APOS A8 training – June 2017
Observable<Integer> oneToEight = Observable.range(1, 8);
Observable<String> ranks = oneToEight.map(Object::toString);
Observable<String> files = oneToEight.map(x -> 'a' + x - 1).map(ascii -> (char)ascii.intValue()).map(ch -> Character.toString(ch));
Observable<String> squares = files.flatMap(file -> ranks.map(rank -> file + rank));
57
Order of Events After flatMap()
Landi APOS A8 training – June 2017
just(10L, 1L).flatMap(x ->just(x).delay(x, TimeUnit.SECONDS)).subscribe(System.out::println);
58
Another flatMap exercise
Landi APOS A8 training – June 2017
Observable<String> loadRecordsFor(DayOfWeek dow) {switch(dow) {
case SUNDAY:return Observable
.interval(90, MILLISECONDS)
.take(5)
.map(i -> "Sun-" + i);case MONDAY:
return Observable.interval(65, MILLISECONDS).take(5).map(i -> "Mon-" + i);
//...}
}
Observable.just(DayOfWeek.SUNDAY, DayOfWeek.MONDAY).flatMap(this::loadRecordsFor);
59
Preserving Order Using concatMap()
Landi APOS A8 training – June 2017
Observable
.just(DayOfWeek.SUNDAY, DayOfWeek.MONDAY)
.concatMap(this::loadRecordsFor);
Sun-0, Sun-1, Sun-2, Sun-3, Sun-4, Mon-0, Mon-1,
Mon-2, Mon-3, Mon-4
Observable
.just(DayOfWeek.SUNDAY, DayOfWeek.MONDAY)
.flatMap(this::loadRecordsFor);
Mon-0, Sun-0, Mon-1, Sun-1, Mon-2, Mon-3, Sun-2,
Mon-4, Sun-3, Sun-4
Combining Operators
- zip
- combineLatest
- concat
- merge
61
Pairwise Composing Using zip()
Landi APOS A8 training – June 2017
62
zip() example
Landi APOS A8 training – June 2017
Observable<Long> red = Observable.interval(10, TimeUnit.MILLISECONDS);Observable<Long> green = Observable.interval(10, TimeUnit.MILLISECONDS);
Observable.zip(red.timestamp(),green.timestamp(),(r, g) -> r.getTimestampMillis() - g.getTimestampMillis()
).forEach(System.out::println);
ZipExampleActivity.java
Observable<Long> red = Observable.interval(10, TimeUnit. SECONDS);Observable<Long> green = Observable.interval(10, TimeUnit.MILLISECONDS);
Observable.zip(red.timestamp(),green.timestamp(),(r, g) -> r.getTimestampMillis() - g.getTimestampMillis()
).forEach(System.out::println);
63
When Streams Are Not Synchronized with One Another
Landi APOS A8 training – June 2017
64
combineLatest example
Landi APOS A8 training – June 2017
FormValidationCombineLatestFragment.java
Observable.combineLatest(interval(170, MILLISECONDS).map(x -> "S" + x),interval(100, MILLISECONDS).map(x -> "F" + x),(s, f) -> f + ":" + s
).forEach(System.out::println);
65
Ways of Combining Streams: concat(), merge()
Landi APOS A8 training – June 2017
MergeExampleActivity.java, ConcatExampleActivity.java
66
Merge & Concat Example
Landi APOS A8 training – June 2017
final Observable<String> sundayObservable = Observable.interval(90, MILLISECONDS).take(5).map(i -> "Sun-" + i);
final Observable<String> mObservable = Observable.interval(65, MILLISECONDS).take(5).map(i -> "Mon-" + i);
Observable.merge(sundayObservable, mondayObservable); Observable.concat(sundayObservable, mondayObservable);
Mon-0, Sun-0, Mon-1, Sun-1, Mon-2, Mon-3, Sun-2, Mon-4, Sun-3, Sun-4
Sun-0, Sun-1, Sun-2, Sun-3, Sun-4, Mon-0, Mon-1, Mon-2, Mon-3, Mon-4
Conditional Operators
68
all, exists, contain
Landi APOS A8 training – June 2017
Observable<Integer>numbers = Observable.range(1, 5);
numbers.all(x -> x != 4); // [false]numbers.exists(x -> x == 4); // [true]numbers.contains(4); // [true]
Schedulers
- observeOn
- subscribeOn
70
A simple Observable – A simple data source
Landi APOS A8 training – June 2017
private Observable<String> simple() {return Observable.create(emitter -> {
log("emitting A");emitter.onNext("A");
log("emitting B");emitter.onNext("B");
log("Completing");emitter.onComplete();
});}
71
subscribeOn()
Landi APOS A8 training – June 2017
log("Starting");final Observable<String> obs = simple();log("Created");obs.subscribe(
x -> log("Got " + x),Throwable::printStackTrace,() -> log("Completed")
);
298 | main | Starting
298 | main | Created
298 | main | Emitting A
298 | main | Got A
298 | main | Emitting B
298 | main | Got B
298 | main | Completed
298 | main | Exiting
log("Starting");final Observable<String> obs = simple();log("Created");obs.subscribeOn(Schedulers.io())
.subscribe(x -> log("Got " + x),Throwable::printStackTrace,() -> log("Completed")
);log("Exiting");
298 | main | Starting
298 | main | Created
298 | main | Exiting
301 | RxCachedThreadScheduler | Emitting A
301 | RxCachedThreadScheduler | Got A
301 | RxCachedThreadScheduler | Emitting B
301 | RxCachedThreadScheduler | Got B
301 | RxCachedThreadScheduler | Completed
72
observeOn()
Landi APOS A8 training – June 2017
Controls which Scheduler is used to invoke downstream
log(“Starting”);final Observable<String> obs = simple();log(“Created”);obs.observeOn(Schedulers.io())
.subscribe(x -> log(“Got ” + x),Throwable::printStackTrace,() -> log(“Completed”));
log("Exiting");
298 | main | Starting298 | main | Created298 | main | Emitting A298 | main | Emitting B298 | main | Exiting
301 | RxCachedThreadScheduler | Got A301 | RxCachedThreadScheduler | Got B301 | RxCachedThreadScheduler | Completed
73
Use subcribeOn and observeOn
Landi APOS A8 training – June 2017
log(“Starting”);final Observable<String> obs = simple();log(“Created”);obs.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(x -> log(“Got ” + x),Throwable::printStackTrace,() -> log(“Completed”)
);log("Exiting");
298 | main | Starting298 | main | Created298 | main | Exiting301 | RxCachedThreadScheduler-1 | Emitting A301 | RxCachedThreadScheduler-1 | Emitting B302 | RxCachedThreadScheduler-2 | Got A302 | RxCachedThreadScheduler-2 | Got B302 | RxCachedThreadScheduler-2 | Completed
74
Schedulers
Landi APOS A8 training – June 2017
- Schedulers.newThread()
- Schedulers.io()
- Schedulers.computation()
- Schedulers.trampoline()
- Schedulers.single()
- AndroidSchedulers.mainThread()
Error Handling
//.....subscribe(
System.out::println,throwable -> showError());
76
Replacing errors with a fixed result using onErrorReturn()
Landi APOS A8 training – June 2017
77
onErrorReturn example
Landi APOS A8 training – June 2017
Observable<Income> income = person.flatMap(this::determineIncome).onErrorReturn(error -> Income.no())
//...
private Observable<Income> determineIncome(Person person) {return Observable.error(new RuntimeException(“Foo”));
}
class Income {static Income no() {
return new Income(0);}
}
78
Timing Out When Events Do Not Occur
Landi APOS A8 training – June 2017
79
timeout example
Landi APOS A8 training – June 2017
Observable<String> confirmation() {Observable<String> delayBeforeCompletion =
Observable.<String>empty().delay(200, MILLISECONDS);
return Observable.just("a").delay(100, MILLISECONDS).concatWith(delayBeforeCompletion);
}
confirmation().timeout(110, MILLISECONDS).test().await().assertValue("a").assertError(TimeoutException.class);
80
Retrying After Failures
Landi APOS A8 training – June 2017
Observable<String> risky() {return Observable.fromCallable(() -> {
if (Math.random() < 0.1) {Thread.sleep((long) (Math.random() * 2000));return "OK";
} else {throw new RuntimeException("Transient");
}});
}
risky().timeout(1, SECONDS).doOnError(th -> log.warn("Will retry", th)).retry().subscribe(log::info);
Thank you