Going Reactive An architectural journey Going Reactive An - - PowerPoint PPT Presentation

going reactive an architectural journey going reactive an
SMART_READER_LITE
LIVE PREVIEW

Going Reactive An architectural journey Going Reactive An - - PowerPoint PPT Presentation

Going Reactive An architectural journey Going Reactive An architectural journey Matthias Kppler October 2015 commit 24c61b35754ff5ca153ce37c5886279153f0d16f Author: Matthias Kaeppler <mk@soundcloud.com> Date: Wed Mar 13


slide-1
SLIDE 1

Going Reactive
 An architectural journey

slide-2
SLIDE 2
slide-3
SLIDE 3

Going Reactive
 An architectural journey

Matthias Käppler
 October 2015

slide-4
SLIDE 4

commit 24c61b35754ff5ca153ce37c5886279153f0d16f Author: Matthias Kaeppler <mk@soundcloud.com> Date: Wed Mar 13 16:09:04 2013 +0100 Throw RxJava into the mix! diff --git a/app/pom.xml b/app/pom.xml index 86ba988..1bf5109 100644

  • -- a/app/pom.xml

+++ b/app/pom.xml @@ -178,6 +178,11 @@ + <dependency> + <groupId>com.netflix.rxjava</groupId> + <artifactId>rxjava-core</artifactId> + <version>0.5.4</version> + </dependency> </dependencies>

slide-5
SLIDE 5

Journey Down
 The Stack

slide-6
SLIDE 6

Layered Architecture

slide-7
SLIDE 7

Featurized Architecture

slide-8
SLIDE 8

Featurized Layers

slide-9
SLIDE 9

The Sound Stream

slide-10
SLIDE 10

Layer Objects

Rx

Screen Presenter


Life-cycle dispatch,
 view binding

Feature Operations


Business logic, data 
 wiring, scheduling

Storage & API


Network, database,
 flat files, syncer Rx[Android]

slide-11
SLIDE 11

Views

class SoundStreamFragment extends LightCycleSupportFragment { @Inject @LightCycle SoundStreamPresenter presenter; public SoundStreamFragment() { setRetainInstance(true); ... }
 ... }

slide-12
SLIDE 12

LightCycle

A C B

  • nCreate

LightCycle
 Dispatcher

@LightCycle @LightCycle @LightCycle

slide-13
SLIDE 13

Presenters

class SoundStreamPresenter extends RecyclerViewPresenter<StreamItem> { ...
 @Override protected CollectionBinding<StreamItem> onBuildBinding(Bundle args) { return CollectionBinding.from(
 streamOperations.initialStreamItems()) .withAdapter(adapter) .withPager(streamOperations.pagingFunction()) .build(); } }

slide-14
SLIDE 14

Paging

slide-15
SLIDE 15

Paging

p1 p2 p3

*current *next

p1

  • nNext(p1)

subscribe()
 / next()

PublishSubject

switchOnNext PagingFunction

Pager

slide-16
SLIDE 16

Use Cases

class SoundStreamOperations { Observable<List<StreamItem>> initialStreamItems() { return loadFirstPageOfStream() .zipWith(
 facebookInvites.loadWithPictures(),
 prependFacebookInvites()) .subscribeOn(scheduler); } ... }

slide-17
SLIDE 17

Feature Data

class SoundStreamStorage { Observable<PropertySet> streamItems(int limit) { Query query = Query.from(“SoundStreamTable”).limit(limit); return propellerRx.query(query).map(new StreamItemMapper()); 
 } ... }

slide-18
SLIDE 18

Cross-Feature Communication

slide-19
SLIDE 19

Cross-Screen Messaging

updated!

slide-20
SLIDE 20

Screen-to-Screen Updates

Rx Subject

slide-21
SLIDE 21

Screen-to-Screen Updates

Observable<PropertySet> toggleLike(Urn urn, 
 boolean addLike) { return storeLikeCommand.toObservable(urn, addLike) .map(toChangeSet(targetUrn, addLike)) .doOnNext(publishChangeSet); }

slide-22
SLIDE 22

Screen-to-Screen Updates

@Override public void call(PropertySet changeSet) { eventBus.publish(
 EventQueue.ENTITY_STATE_CHANGED, 
 EntityStateChangedEvent.fromLike(changeSet)
 ); }

publishChangeSet: Action1<PropertySet>

RxSubject in disguise!

slide-23
SLIDE 23

Screen-to-Screen Updates

protected void onViewCreated(...) { eventBus.subscribe(
 EventQueue.ENTITY_STATE_CHANGED,
 new UpdateListSubscriber(adapter) ); }

SoundStreamPresenter

slide-24
SLIDE 24

Implementation
 Patterns

slide-25
SLIDE 25

Life-Cycle Subscriptions

private CompositeSubscription viewLifeCycle;
 
 protected void onViewCreated(...) {
 viewLifeCycle = new CompositeSubscription(); viewLifeCycle.add(...); ...
 }
 
 protected void onDestroyView() {
 viewLifeCycle.unsubscribe();
 }

slide-26
SLIDE 26

Fast Path & Lazy Updates

Observable<Model> maybeCached() { return Observable.concat(cachedModel(), remoteModel()).first() }

slide-27
SLIDE 27

Observable Transformers

Observable<Model> scheduledModel() { return Observable.create(...).compose(schedulingStrategy) } class HighPrioUiTask<T> extends Transformer<T, T> { public Observable<T> call(Observable<T> source) { return source
 .subscribeOn(Schedulers.HIGH_PRIO)
 .observeOn(AndroidSchedulers.mainThread()) } }

slide-28
SLIDE 28

Observable<Integer> intSequence() { return Observable.create((subscriber) -> { List<Integer> ints = computeListOfInts();
 for (int n : ints) {
 subscriber.onNext(n);
 subscriber.onCompleted(); } }

Deferred Execution

Observable<Integer> intSequence() { return Observable.defer(() -> { return Observable.from(computeListOfInts()); } }

expensive!

slide-29
SLIDE 29

Common Pitfalls

slide-30
SLIDE 30

No-args subscribe

Observable.create(...).subscribe(/* no-args */)

OnErrorNotImplementedException

slide-31
SLIDE 31

ObserveOn: onError

Observable.create((subscriber) -> { subscriber.onNext(value); subscriber.onError(new Exception()); }.observeOn(mainThread()).subscribe(...)

  • nError cuts ahead of onNext

gets dropped!

slide-32
SLIDE 32

ObserveOn: Backpressure

public void onStart() { request(RxRingBuffer.SIZE); } public void onNext(final T t) { ... if (!queue.offer(on.next(t))) {

  • nError(new MissingBackpressureException());

return; } schedule(); }

16!

slide-33
SLIDE 33

ObserveOn: Backpressure

★ Take load off of target thread˝ ★ Use buffering operators (buffer, toList, …)˝ ★ Use onBackpressure* operators˝ ★ System.setProperty(“rx.ring-buffer.size”)

slide-34
SLIDE 34

Debugging

slide-35
SLIDE 35

Debugging Observables

Observable.just(1, 2, 3) .map((n) -> {return Integer.toString(n);} .observeOn(AndroidSchedulers.mainThread());

How do we debug this?

slide-36
SLIDE 36

Gandalf

★ Annotation based byte code injection
 ★ Based on Hugo 


https://github.com/JakeWharton/hugo


★ AspectJ + Gradle plugin
 ★ @RxLogObservable, @RxLogSubscriber

slide-37
SLIDE 37

Gandalf

@RxLogObservable Observable<String> createObservable() {
 return Observable.just(1, 2, 3) .map((n) -> {return Integer.toString(n);} .observeOn(mainThread()); } @RxLogSubscriber class StringSubscriber extends Subscriber<String> {}

slide-38
SLIDE 38

Gandalf

[@Observable :: @InClass -> MainActivity :: @Method 


  • > createObservable()]

[@Observable#createObservable -> onSubscribe() :: 
 @SubscribeOn -> main] [@Observable#createObservable -> onNext() -> 1] [@Observable#createObservable -> onNext() -> 2] [@Observable#createObservable -> onNext() -> 3] [@Observable#createObservable -> onCompleted()] [@Observable#createObservable -> onTerminate() :: 
 @Emitted -> 3 elements :: @Time -> 4 ms] [@Observable#createObservable -> onUnsubscribe()]

slide-39
SLIDE 39

.

soundcloud.com Berlin New York San Francisco London

Stay in touch @mttkay

slide-40
SLIDE 40
slide-41
SLIDE 41