Você está na página 1de 50

RxUI

Hola!
Soy Cristian Gomez
Android developer
@PagerInc, Co-Organizer
@MedellinAndroid.

You can find me at:


@iyubinest on github,
twitter, instagram.
Agenda

▪ Conceptos generales
▪ Porque RxUI?
▪ El problema
▪ Componentes
▪ Encadenando todo
1
Rx - Conceptos generales

Reactive world
Rx
Agregar control sobre la lógica
y el encadenamiento de acciones
en la UI
RxJava 2

Single Completable Maybe


Emite un solo elemento con Emite un evento para saber Emite 0 o 1 elementos o
un valor o un error. si el stream ha sido puede fallar.
completado.

Observable Flowable
No hay manejo de Permite manejo de
backpressure. backpressure.
Backpressure

Default Latest Oldest


Maneja un buffer, Mientras el Mientras el
si este buffer se elemento actual elemento actual
llena se produce es procesado , es procesado ,
una excepción. elimina los elimina los
primeros valores últimos valores
del stream, del stream,
conservando los conservando los
últimos. primeros.
Cuando usar Observable

▪ Stream menor a 1000 elementos


▪ GUI events
▪ Tiene un overhead menor
Cuando usar Flowable

▪ Stream mayor a 10k elementos


▪ Operaciones de IO
▪ Parsing de datos
2
Porque RxUI?

AndroidSchedulers
Bases

▪ Jake Wharton (The reactive state)


▪ Artem Zinnatullin (RxUI)
▪ RxBindings
▪ PublishSubject
Problemas de una UI normal

▪ Manejo del hilo de la UI


▫ runOnUiThread
▫ Inyectar mainThread()
▪ Backpressure
▪ Manejo de multiples estados para
diferentes elementos graficos
3
El problema

Guardar Vuelos App


App de vuelos
▪ Permitir autocompletado
▪ Permitir validación del
formulario
▪ Guardar informacion
4
Componentes

RxMVP?
UI interface
Definición de las acciones
permitidas
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
interface FlightView {

Observable<String> fromChanges();

Observable<String> toChanges();

Observable<FlightModel> saveClicked();

Consumer<List<String>> showFrom();

Consumer<List<String>> showTo();

Consumer<Throwable> showError();

Consumer<Object> showLoading();

Consumer<FlightModel> showSaved();

Consumer<Boolean> showButton();
}
Data
Definición del acceso a datos
public class FlightWorker extends Fragment {

private final FindCity findCity;

private final SaveFlight saveFlight;

private CompositeDisposable disposables = new CompositeDisposable();

public FlightWorker() {
setRetainInstance(true);
findCity = FindCity.create();
saveFlight = SaveFlight.create();
}

void bind(final FlightView view) {


disposables.add(autocompleteFrom(view.fromChanges(), view.showFrom()));
disposables.add(autocompleteFrom(view.toChanges(), view.showTo()));
disposables.add(validateForm(view));
disposables.add(saveFlight(view));
}
}
Autocomplete
private Disposable autocompleteFrom(Observable<String> textChanges,
Consumer<List<String>> publishTo) {
return textChanges.switchMap(text -> findCity.exec(text).toObservable())
.compose(mainThread())
.subscribe(publishTo, Functions.emptyConsumer());
}
Validar el
formulario
private Disposable validateForm(RealFlightView view) {
return Observable.combineLatest(view.fromChanges(), view.toChanges(),
(from, to) -> !TextUtils.isEmpty(from) && !TextUtils.isEmpty(to))
.compose(mainThread())
.subscribe(view.showButton(), Functions.emptyConsumer());
}
Guardar datos
private Disposable saveFlight(RealFlightView view) {
return view.saveClicked()
.doOnNext(view.showLoading())
.flatMap(saveFlight::save)
.map(this::map)
.doOnError(view.showError())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(view.showSaved(), Functions.emptyConsumer());
}
Dependencias
Encontrar nombre
de ciudad
public final class FindCity {
private final AutoCompleteCityApi api;
public static FindCity create() {
return new FindCity();
}
private FindCity() {
api = new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("http://autocompletecity.geobytes.com/")
.build()
.create(AutoCompleteCityApi.class);
}
public Maybe<List<String>> exec(String query) {
return api.options(query).map(response -> response).observeOn(Schedulers.io());
}
interface AutoCompleteCityApi {
@GET("AutoCompleteCity") Maybe<List<String>> options(@Query("q") String query);
}
}
Guardar
informacion
public final class SaveFlight {
public class Response {...}
private final DatabaseReference firebase;
public static SaveFlight create() {return new SaveFlight();}
private SaveFlight() {
firebase = FirebaseDatabase.getInstance().getReference();
}
public Observable<Response> save(FlightModel model) {
PublishSubject<Response> subject = PublishSubject.create();
firebase.child("/").push().setValue(model, (error, reference) -> {
if (null != error) {
subject.onError(error.toException());
} else {
subject.onNext(new Response(model));
subject.onComplete();
}
});
return subject;
}
}
public Observable<Response> save(FlightModel model) {
PublishSubject<Response> subject = PublishSubject.create();
firebase.child("/").push().setValue(model, (error, reference) -> {
if (null != error) {
subject.onError(error.toException());
} else {
subject.onNext(new Response(model));
subject.onComplete();
}
});
return subject;
}
5
Encadenando todo

flatMap?
Resolviendo el
Activity
public class FlightActivity extends AppCompatActivity {

private FlightWorker worker;

@Override protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.flight_activity);
worker = new FlightWorker();
worker.bind(new RealFlightView(findViewById(android.R.id.content)));
}

@Override protected void onDestroy() {


super.onDestroy();
worker.unbind();
}
}
Implementando el
Rx View
class RealFlightView implements FlightView {

private static final int MIN_LENGTH = 3;


private static final int DELAY = 500;
private final View parent;
private final View loadingView;
private final FlightToolbar toolbar;
private final AutocompleteField fromView;
private final AutocompleteField toView;
private final FlightListWidget flightsView;
private final Button flightsSave;
RealFlightView(View view) {
parent = view;
loadingView = view.findViewById(R.id.flight_loading);
toolbar = view.findViewById(R.id.flight_toolbar);
fromView = view.findViewById(R.id.flight_from);
toView = view.findViewById(R.id.flight_to);
flightsView = view.findViewById(R.id.flight_list);
flightsSave = view.findViewById(R.id.flight_save);
}
}
class RealFlightView implements FlightView {
...
@Override public Observable<String> fromChanges() {
return RxTextView.textChanges(fromView).compose(autocomplete());
}
@Override public Observable<String> toChanges() {
return RxTextView.textChanges(toView).compose(autocomplete());
}
@Override public Observable<FlightModel> saveClicked() {
return RxView.clicks(flightsSave)
.map(object -> new FlightModel(fromView.content(), toView.content()));
}
@Override public Consumer<List<String>> showFrom() {return this::showFrom;}
@Override public Consumer<List<String>> showTo() {return this::showTo;}
@Override public Consumer<Throwable> showError() {return this::showError;}
@Override public Consumer<Object> showLoading() {return this::showLoading;}
@Override public Consumer<FlightModel> showSaved() {return this::addFlight;}
@Override public Consumer<Boolean> showButton() {return this::showButton;}
...
}
class RealFlightView implements FlightView {
...
private void showError(Throwable throwable) {
loadingView.setVisibility(View.GONE);
Toast.makeText(parent.getContext(), throwable.getMessage(), Toast.LENGTH_SHORT).show();
}
private void showLoading(Object object) {
loadingView.setVisibility(View.VISIBLE);
}
private void addFlight(FlightModel flightModel) {
loadingView.setVisibility(View.GONE);
flightsView.add(flightModel);
}
private void showFrom(List<String> cities) {
ArrayAdapter<String> adapter =
new ArrayAdapter<>(fromView.getContext(), android.R.layout.simple_list_item_1, cities);
fromView.setAdapter(adapter);
}
}
class RealFlightView implements FlightView {
...
private void showTo(List<String> cities) {
ArrayAdapter<String> adapter =
new ArrayAdapter<>(toView.getContext(), android.R.layout.simple_list_item_1, cities);
toView.setAdapter(adapter);
}
private void showButton(Boolean valid) {
flightsSave.setEnabled(valid);
}
private ObservableTransformer<CharSequence, String> autocomplete() {
return observable -> observable.debounce(DELAY, TimeUnit.MILLISECONDS)
.map(CharSequence::toString)
.filter(s -> s.length() > MIN_LENGTH)
.distinctUntilChanged()
.share();
}
}
Gracias!
Preguntas?
encuéntrame:
@iyubinest
github/twitter/instragram

Você também pode gostar