goto cph 2015 - GOTO · PDF file...

Click here to load reader

  • date post

    21-May-2020
  • Category

    Documents

  • view

    1
  • download

    0

Embed Size (px)

Transcript of goto cph 2015 - GOTO · PDF file...

  • Going Reactive 
 An architectural journey

  • Going Reactive 
 An architectural journey

    Matthias Käppler
 October 2015

  • commit 24c61b35754ff5ca153ce37c5886279153f0d16f Author: Matthias Kaeppler 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 @@

    + + com.netflix.rxjava + rxjava-core + 0.5.4 +

  • Journey Down
 The Stack

  • Layered Architecture

  • Featurized Architecture

  • Featurized Layers

  • The Sound Stream

  • 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]

  • Views

    class SoundStreamFragment extends LightCycleSupportFragment {

    @Inject @LightCycle SoundStreamPresenter presenter;

    public SoundStreamFragment() { setRetainInstance(true); ... } 
 ... }

  • LightCycle

    A

    C

    B onCreate LightCycle 


    Dispatcher

    @LightCycle

    @LightCycle

    @LightCycle

  • Presenters

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

  • Paging

  • Paging

    p1p2p3

    *current*next

    p1

    onNext(p1)

    subscribe() 
 / next()

    PublishSubject

    switchOnNext

    PagingFunction

    Pager

  • Use Cases

    class SoundStreamOperations {

    Observable initialStreamItems() { return loadFirstPageOfStream() .zipWith( 
 facebookInvites.loadWithPictures(), 
 prependFacebookInvites()) .subscribeOn(scheduler); }

    ... }

  • Feature Data

    class SoundStreamStorage {

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

    ... }

  • Cross-Feature Communication

  • Cross-Screen Messaging

    updated!

  • Screen-to-Screen Updates

    Rx Subject

  • Screen-to-Screen Updates

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

  • Screen-to-Screen Updates

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

    publishChangeSet: Action1

    RxSubject in disguise!

  • Screen-to-Screen Updates

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

    SoundStreamPresenter

  • Implementation
 Patterns

  • Life-Cycle Subscriptions

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

  • Fast Path & Lazy Updates

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

  • Observable Transformers

    Observable scheduledModel() { return Observable.create(...).compose(schedulingStrategy) }

    class HighPrioUiTask extends Transformer { public Observable call(Observable source) { return source 
 .subscribeOn(Schedulers.HIGH_PRIO) 
 .observeOn(AndroidSchedulers.mainThread()) } }

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

    Deferred Execution

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

    expensive!

  • Common Pitfalls

  • No-args subscribe

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

    OnErrorNotImplementedException

  • ObserveOn: onError

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

    onError cuts ahead of onNext

    gets dropped!

  • ObserveOn: Backpressure

    public void onStart() { request(RxRingBuffer.SIZE); }

    public void onNext(final T t) { ... if (!queue.offer(on.next(t))) { onError(new MissingBackpressureException()); return; } schedule(); }

    16!

  • ObserveOn: Backpressure

    ★ Take load off of target thread˝

    ★ Use buffering operators (buffer, toList, …)˝

    ★ Use onBackpressure* operators˝

    ★ System.setProperty(“rx.ring-buffer.size”)

  • Debugging

  • Debugging Observables

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

    How do we debug this?

  • Gandalf

    ★ Annotation based byte code injection 


    ★ Based on Hugo 
 https://github.com/JakeWharton/hugo 


    ★ AspectJ + Gradle plugin


    ★ @RxLogObservable, @RxLogSubscriber

    https://github.com/JakeWharton/hugo

  • Gandalf

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

    @RxLogSubscriber class StringSubscriber extends Subscriber {}

  • 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()]

  • . soundcloud.com

    Berlin New York San Francisco London

    Stay in touch @mttkay