going reactive an architectural journey going reactive an
play

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


  1. Going Reactive 
 An architectural journey

  2. Going Reactive 
 An architectural journey Matthias Käppler 
 October 2015

  3. 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>

  4. Journey Down 
 The Stack

  5. Layered Architecture

  6. Featurized Architecture

  7. Featurized Layers

  8. The Sound Stream

  9. Layer Objects Screen Presenter 
 Life-cycle dispatch, 
 view binding Rx[Android] Feature Operations 
 Business logic, data 
 wiring, scheduling Rx Storage & API 
 Network, database, 
 flat files, syncer

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

  11. LightCycle A @LightCycle onCreate LightCycle 
 B @LightCycle Dispatcher C @LightCycle

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

  13. Paging

  14. Paging onNext(p1) Pager subscribe() 
 switchOnNext / next() PublishSubject PagingFunction p1 *next *current p3 p2 p1

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

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

  17. Cross-Feature Communication

  18. Cross-Screen Messaging updated!

  19. Screen-to-Screen Updates Rx Subject

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

  21. Screen-to-Screen Updates publishChangeSet: Action1<PropertySet> @Override public void call(PropertySet changeSet) { eventBus.publish( 
 RxSubject in disguise! EventQueue.ENTITY_STATE_CHANGED, 
 EntityStateChangedEvent.fromLike(changeSet) 
 ); }

  22. Screen-to-Screen Updates SoundStreamPresenter protected void onViewCreated(...) { eventBus.subscribe( 
 EventQueue.ENTITY_STATE_CHANGED, 
 new UpdateListSubscriber(adapter) ); }

  23. Implementation 
 Patterns

  24. 
 
 Life-Cycle Subscriptions private CompositeSubscription viewLifeCycle; 
 protected void onViewCreated(...) { 
 viewLifeCycle = new CompositeSubscription(); viewLifeCycle.add(...); ... 
 } 
 protected void onDestroyView() { 
 viewLifeCycle.unsubscribe(); 
 }

  25. Fast Path & Lazy Updates Observable<Model> maybeCached() { return Observable.concat(cachedModel(), remoteModel()).first() }

  26. 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()) } }

  27. Deferred Execution Observable<Integer> intSequence() { return Observable.create((subscriber) -> { List<Integer> ints = computeListOfInts(); 
 for (int n : ints) { 
 expensive! subscriber.onNext(n); 
 subscriber.onCompleted(); } } Observable<Integer> intSequence() { return Observable.defer(() -> { return Observable.from(computeListOfInts()); } }

  28. Common Pitfalls

  29. No-args subscribe Observable.create(...).subscribe(/* no-args */) OnErrorNotImplementedException

  30. ObserveOn: onError gets dropped! Observable.create((subscriber) -> { subscriber.onNext(value); subscriber.onError(new Exception()); }.observeOn(mainThread()).subscribe(...) onError cuts ahead of onNext

  31. ObserveOn: Backpressure 16! public void onStart() { request(RxRingBuffer.SIZE); } public void onNext(final T t) { ... if (!queue.offer(on.next(t))) { onError(new MissingBackpressureException()); return; } schedule(); }

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

  33. Debugging

  34. Debugging Observables Observable.just(1, 2, 3) .map((n) -> {return Integer.toString(n);} .observeOn(AndroidSchedulers.mainThread()); How do we debug this?

  35. Gandalf ★ Annotation based byte code injection 
 ★ Based on Hugo 
 https://github.com/JakeWharton/hugo 
 ★ AspectJ + Gradle plugin 
 ★ @RxLogObservable, @RxLogSubscriber

  36. 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> {}

  37. 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()]

  38. Stay in touch @mttkay . soundcloud.com Berlin New York San Francisco London

Recommend


More recommend