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

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend