Top Banner
RXJAVA REFACTORING FOR UBIRATAN SOARES SEPTEMBER / 2016
47

Android DevConference - Refactoring for RxJava

Apr 16, 2017

Download

Technology

iMasters
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Android DevConference - Refactoring for RxJava

RXJAVAREFACTORING FOR

UBIRATAN SOARESSEPTEMBER / 2016

Page 2: Android DevConference - Refactoring for RxJava

MOTIVAÇÕES

RxJava é um dos trending topics no desenvolvimento para Android nos últimos 2+ anos

Você certamente já viu alguma solução “Rxfyed" para algum problema na sua timeline.

Fato : programação reativa oferece soluções poderosas para problemas difíceis

Fato : RxJava irá alcançar o release 2.0.0 em breve, uma atualização significativa com novas funcionalidades e várias mudanças

Page 3: Android DevConference - Refactoring for RxJava

ESSA PARECE SER UMA ÓTIMA PERGUNTA !

Seu me projeto não utiliza nada de RxJava hoje, como eu o refatoro para ter acesso à essas benesses divinas?

Page 4: Android DevConference - Refactoring for RxJava

restAPI.endpoint() .compose(Transformers::handleNetworkingError) .onErrorResumeNext(t-> handleError(t)) .map(payload -> payload.array) .flatMap(Observable::from) .filter(DataValidation::validate) .map(ModelTransformer::toUI) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( data -> updateUI(data), this::reportError, () -> Timber.d(“DONE”) );

🤔

😖

😍SUA REAÇÃO ?

Page 5: Android DevConference - Refactoring for RxJava

ANTES DE TUDO ENTENDER

RXJAVA

Page 6: Android DevConference - Refactoring for RxJava

DATA TRANSFORMER

VISÃO SIMPLIFICADA

OBSERVABLE OBSERVER

FUNCTIONAL OPERATION

DATA SOURCE

FUNCTIONAL OPERATION

DATA CONSUMER

Page 7: Android DevConference - Refactoring for RxJava

UM GUIA ENVIESADO

1

2

3

4

Identifique uma fonte de emissões reativas e defina o tipo de fluxo desses dados

Adaptar e evoluir as camadas da sua aplicação para orquestrar o fluxo de dados, ligando fonte a consumidor

Identifique em que ponto da sua aplicação você quer receber esses dados (Observer / Subscriber)

Se as fontes de dados mais óbvias já esgotaram, hora de avançar para as não-óbvias. Retornar para passo 01

Page 8: Android DevConference - Refactoring for RxJava

REACTIVE DATA SOURCES

“Like bugs, you can find them everywhere in your code”

- Soares, U.

Page 9: Android DevConference - Refactoring for RxJava

ANTES (ASYNCTASK) private void notSoTastyThreading(String input) {

new AsyncTask<String, Void, String>() { @Override protected void onPreExecute() { notifyProcessingBeforeInit(); } @Override protected String doInBackground(String... params) { return processing(params[0]); } @Override protected void onPostExecute(String result) { handleResult(result); } }.execute(input); }

Page 10: Android DevConference - Refactoring for RxJava

DEPOIS (THREADING COM RXJAVA)

private void beatifulThreading(String input) {

Observable.just(input) .doOnSubscribe(this::notifyProcessingBeforeInit) .map(this::processing) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::handleResult);

}

Page 11: Android DevConference - Refactoring for RxJava

ANTES (TIMERTASK)Handler toMainThread = new Handler(Looper.getMainLooper());TimerTask periodic = new TimerTask() { @Override public void run() { toMainThread.post(() -> updateUI()); }}; Timer timer = new Timer();timer.schedule(periodic, NO_DELAY, PERIOD_IN_MILIS);

. . .

timer.purge();

Page 12: Android DevConference - Refactoring for RxJava

DEPOIS (TIMER COM RXJAVA)

Subscription timer = Observable.timer(PERIOD_IN_SECONDS, TimeUnit.SECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(instant -> updateUI());

. . .

timer.unsubscribe()

Page 13: Android DevConference - Refactoring for RxJava

ANTES (RETROFIT VIA CALL PATTERN)api.movieWithId(movieId); .enqueue(new Callback<Movie>() { @Override public void onResponse( Call<Movie> call, Response<Movie> response) { if(response.isSuccessful()) { // Success } else { // 4xx or 5xx } } @Override public void onFailure(Call<Movie> call, Throwable t) { // Deu ruim mesmo } });

Page 14: Android DevConference - Refactoring for RxJava

DEPOIS (RETROFIT COM RXJAVA)

starWarsAPI.people() .subscribeOn(Schedulers.io()) .flatMap(payload -> Observable.from(payload.results)) .observeOn(AndroidSchedulers.mainThread()) .subscribe( data -> addToList(data), Throwable::printStackTrace, () -> adapter.notifyDataSetChanged() );

Page 15: Android DevConference - Refactoring for RxJava

E PARA ENCADEAR DUAS OPERAÇÕES ASSÍNCRONAS ?

Page 16: Android DevConference - Refactoring for RxJava

ANTES : CHAINING CALLBACKS

Page 17: Android DevConference - Refactoring for RxJava

DEPOIS : CHAINING COM RXJAVAstarWarsApi.people() .subscribeOn(Schedulers.io()) .flatMap(payload -> selectRandomPeople(payload.results)) .doOnNext(System.out::println) .flatMap(people -> Observable.from(people.films)) .flatMap(filmUrl -> { String filmId = ResourceIdExtractor.idFromUrl(filmUrl); return api.movieById(filmId) }) .observeOn(AndroidSchedulers.mainThread()) .subscribe( data -> addToMoviesList(data), Throwable::printStackTrace, () -> moviesAdapter.notifyDataSetChanged() );

Page 18: Android DevConference - Refactoring for RxJava

FLATTENING (MERGE)

MAPPING (PROVIDED FUNCTION)

FLAT

MA

P

Page 19: Android DevConference - Refactoring for RxJava

REACTIVE SOURCES NA INTERFACE

RxBinding to the rescue!

É possível adaptar outros callbacks utilizando Subjects ou o utilitário fromAsync / fromEmitter

Atenção ao lidar com operadores que envolvam tempo : eles já trocam o Scheduler da sequência, é preciso ressincronizar com a UI Thread para atualizações na UI

Page 20: Android DevConference - Refactoring for RxJava

SEARCHVIEW (RX WAY)@Override public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_search_by_terms, menu); MenuItem search = menu.findItem(R.id.search); SearchView searchView = (SearchView) MenuItemCompat.getActionView(search); RxSearchView.queryTextChangeEvents(searchView) .debounce(300, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::proceedWithQuery); return true; }

Page 21: Android DevConference - Refactoring for RxJava

DEFINING YOUR OBSERVERS

“Ideas are bullet proof”

- V

Page 22: Android DevConference - Refactoring for RxJava

OBSERVER (NORMAL WAY)api.getAvaliableItems(),

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<List<Item>>() {

@Override public void onCompleted() { … }

@Override public void onError(Throwable e) { … } @Override public void onNext(List<Item> items) { … }

} );

Page 23: Android DevConference - Refactoring for RxJava

OBSERVER (ACTIONS WAY)

Observable.fromCallable(() -> “Better subscribing with actions”)

.subscribe( System.out::println, throwable -> someErrorAction(), () -> done() );

Page 24: Android DevConference - Refactoring for RxJava

IMPORTANTE

SEMPRE IMPLEMENTE onError( ) em seus Observers/Subscribers, com Actions ou não

Observers / Subscribers causam memory leaks se retidos em Activities / Fragments / etc

Controle no ciclo de vida via Subscription

Evite Subscribers (statefull)

Page 25: Android DevConference - Refactoring for RxJava

Subscription first = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::print);Subscription second = Observable.range(1, 100000) .subscribe(System.out::print);CompositeSubscription subs = new CompositeSubscription();subs.add(first);subs.add(second);

// . . .

first.unsubscribe(); subs.add(third); if(subs.hasSubscriptions()) subs.unsubscribe();

Page 26: Android DevConference - Refactoring for RxJava

EVOLVING YOUR ARCHITECTURE

“Tradeoffs? Wellcome to Engineering”

- Uncle Bob Martin

Page 27: Android DevConference - Refactoring for RxJava

UMA APLICAÇÃO DOS DIAS ANTIGOS

REST API MANAGER

ACTIVITY FRAGMENT

Callback

EventBus

Page 28: Android DevConference - Refactoring for RxJava

UMA APLICAÇÃO DOS DIAS MODERNOS

PRESENTER

ACTIVITY FRAGMENT VIEW

MODELS / EXTERNAL WORLD ADAPTERS

Page 29: Android DevConference - Refactoring for RxJava

PRESENTER

VIEW (IMPL)

MODEL

MANAGER

ACTIVITY / FRAGMENT

As direções de fluxo de dados indicam como você pode substituir callbacks / eventos por uma sequência observável!

Page 30: Android DevConference - Refactoring for RxJava

PRESENTER

VIEW (IMPL)

MODEL

MANAGER

ACTIVITY / FRAGMENT

Caso 01 : as emissões serão geradas nas camadas mais internas da aplicação e consumidas nas camadas mais próximas à UI (Presenter ou Android)

Page 31: Android DevConference - Refactoring for RxJava

PRESENTER

VIEW (IMPL)

MODEL

MANAGER

ACTIVITY / FRAGMENT

Caso 02 : as emissões são originadas da UI e consumidas nas camadas internas mais da aplicação

Page 32: Android DevConference - Refactoring for RxJava

REACTIVE MVP

PRESENTER

ACTIVITY / FRAGMENT / VIEW

RX SOURCE

Observable<SomeModel>

Observable<SomeModel>

Observable<SomeModel>

Observable<SomeModel>

Page 33: Android DevConference - Refactoring for RxJava

REACTIVE CLEAN ARCHITECTURE

PRESENTERRX SOURCE

Observable<InputModel>

Observable<ViewModel>Observable<DomainModel>

REST API

PREFS

DB

ACTIVITY

FRAGMENT

ETCPLATFORM

USECASE

Observable<DomainModel>

Page 34: Android DevConference - Refactoring for RxJava

CONSIDERAÇÕES PRÁTICAS

Consumir emissões no Presenter vs View Passiva ?

Consumir emissões no Android vs View Reativa ?

Consumo de emissões na UI vs Repository passivo(s) ?

Emissão na UI e consumo no Repository reativo?

Como lidar com estado no Presenter?

Como driblar boilerplating da replicação de dados?

Como testar tudo isso?

Page 35: Android DevConference - Refactoring for RxJava

NON-OBVIOUS REACTIVE SOURCES

“Let`s catch them all”

Ash

Page 36: Android DevConference - Refactoring for RxJava

ONDE PROCURAR?

Qualquer callback de uso recorrente pode ser encapsulado para emitir eventos em uma sequência observável

Android Framework está cheio deles!

APIs de suporte estão cheias deles!

PlayServices e adendos estão cheios deles!

ETC

Page 37: Android DevConference - Refactoring for RxJava

class GoogleApiClientObservable extends BaseClient implements Action1<AsyncEmitter<GoogleApiClient>> {

private final Api api; private AsyncEmitter<GoogleApiClient> emitter;

private GoogleApiClientObservable(Context context, Api api) { super(context); this.api = api; }

static Observable<GoogleApiClient> create(Context context, Api api) { return Observable.fromAsync(new GoogleApiClientObservable(context, api), BackpressureMode.NONE); }

@Override public void call(AsyncEmitter<GoogleApiClient> emitter) { this.emitter = emitter; buildClient(api); connect(); emitter.setSubscription(Subscriptions.create(this::disconnect)); }

@Override void onClientConnected(GoogleApiClient googleApiClient) { emitter.onNext(googleApiClient); }

@Override void onClientError(Throwable throwable) { emitter.onError(throwable); } }

Snippet from Servant

https://github.com/Mauin/servant

Page 38: Android DevConference - Refactoring for RxJava

QUANDO NÃO USAR RXJAVA ?

“U HAVE NOTHING, NOTHING !!!!”

- Al Capone, The Untouchables

Page 39: Android DevConference - Refactoring for RxJava

ALGUM CASOS A CONSIDERARValores que não mudam nunca : justificam ser passados por Callback observável?

Observer/Subscriber desencadeia uma operação pesada no emissor, e a sequência por sua vez é multicasted

Você precisa de snapshots de estados intermediários referentes às emissões por algum motivo

Seu design de classes sugere que um Observable<T> até podia ser uma variável membro …

ETC

Page 40: Android DevConference - Refactoring for RxJava

FINAL REMARKS

"You know nothing, Jon Snow"

- Game of Thrones

Page 41: Android DevConference - Refactoring for RxJava

DONT FORGET KIDS

Comece pelos casos simples

Evolua para os casos complexos

Defina quais camadas da sua aplicação são reativas ou não

Substitua callbacks/eventos por sequência observáveis

FTW

Page 42: Android DevConference - Refactoring for RxJava

REFERÊNCIAS (I)"Functional Reactive Programming with RxJava" by Ben Christensen https://youtu.be/_t06LRX0DV0

“Learning RxJava (for Android) by example“ by Kaushik Goupal https://youtu.be/k3D0cWyNno4

“Demystifying RxJava Subscribers" by Jake Wharton https://youtu.be/NVKmyK6sd-Q

“What does it mean to be Reactive ?” by Erik Meijer https://youtu.be/sTSQlYX5DU0

Page 43: Android DevConference - Refactoring for RxJava

REFERÊNCIAS (II)"Grokking RxJava Series” by Dan Lew http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

“The Introduction to Reactive Programming you`ve been missing” by André Staltz https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Oficial RxJava Wiki by NetFlix https://github.com/ReactiveX/RxJava/wiki

Advanced RxJava Blog by David Karnok akarnokd.blogspot.com

Page 44: Android DevConference - Refactoring for RxJava

REFERÊNCIAS (III)

GradleLambda : https://github.com/evant/gradle-retrolambda

RxAndroid : https://github.com/ReactiveX/RxAndroid

RxLifecycle : https://github.com/trello/RxLifecycle

RxBinding : https://github.com/JakeWharton/RxBinding

Frodo : https://github.com/android10/frodo

Page 45: Android DevConference - Refactoring for RxJava

speakerdeck.com/ubiratansoares/refactoring-to-rxjava

Page 46: Android DevConference - Refactoring for RxJava

UBIRATAN SOARESComputer Scientist by ICMC/USP

Software Engineer @ Luiza Labs

Google Developer Expert for Android

Teacher, speaker, etc, etc

Page 47: Android DevConference - Refactoring for RxJava

THANKS!THAT`S ALL FOLKS !!!

@ubiratanfsoaresbr.linkedin.com/in/ubiratanfsoares

ubiratansoares.github.io