$30 off During Our Annual Pro Sale. View Details »

Intro a RxJava

Intro a RxJava

Charla introductoria a RxJava, sin entrar en mucho detalle, intento acercar la programación reactiva de un modo ameno y sencillo.

Antonio Nicolás Pina

December 13, 2016
Tweet

More Decks by Antonio Nicolás Pina

Other Decks in Programming

Transcript

  1. Intro a RxJava
    Antonio Nicolás Pina
    Android & Go developer
    @anpez
    https://www.anpez.es Murcia Android Developers
    S02E01
    13 diciembre 2016

    View Slide

  2. 1.
    Motivación
    Programación
    reactiva
    funcional

    View Slide

  3. Programación reactiva
    ▣ Orientada a flujos de datos. Casos típicos:
    □ Entrada del teclado
    □ Red
    □ Base de datos
    ▣ Desacopla la fuente de los datos de los
    receptores.
    □ Evita el uso de listeners que acoplan el código.

    View Slide

  4. Programación funcional
    ▣ Orientada a funciones matemáticas.
    ▣ Reutilización de funciones existentes.
    ▣ Composición de funciones sencillas.
    ▣ Filosofía UNIX.

    View Slide

  5. ‘’ Make each program do
    one thing well.
    To do a new job, build
    afresh rather than
    complicate old programs
    by adding new "features".
    Doug McIlroy
    The Bell System Technical Journal
    1978

    View Slide

  6. Programación funcional reactiva
    ▣ Introducida en 1997
    ▣ Combina los dos modelos anteriores
    ▣ Nos permite manejar cadenas de datos o
    “eventos”, aplicando funciones (operadores)
    sobre los mismos.
    ▣ RxJava se basa en los Observables de .NET 4
    (año 2010).

    View Slide

  7. 2.
    Observable /
    Observer Ideas básicas

    View Slide

  8. Observable
    ▣ Interfaz que define la “fuente” de los datos.
    ▣ Sólo un método: OnSubscribe.
    ▣ Decenas de implementaciones.

    View Slide

  9. Observer
    ▣ Define una única interfaz común para todos
    los objetos que se suscriben a Observables.
    ▣ Dispone de tres métodos:
    □ onNext() Llamada con cada elemento emitido.
    □ onError() Para situaciones de error.
    □ onCompleted() El procesamiento acabó
    correctamente.

    View Slide

  10. Observer
    ▣ Se podría esquematizar así.
    try {
    for(Elem e: sequence) {
    observer.onNext(e);
    }
    observer.onCompleted();
    } catch (Exception e) {
    observer.onError();
    }

    View Slide

  11. Recuerda: siempre manejas streams

    View Slide

  12. Creación de Observable
    ▣ La creación se hace (preferentemente) a
    través de métodos estáticos de Observable.
    □ Observable.just(elemento)
    □ Observable.just(elemento 1, elemento 2, ...)
    □ Observable.from(iterable)
    □ Observable.interval(3, TimeUnit.SECONDS)

    View Slide

  13. 3.
    Operadores Modificación
    del stream

    View Slide

  14. Operadores sobre el stream
    ▣ Hay cientos de operadores aplicables sobre
    un stream.
    ▣ Se pueden definir operadores propios,
    aunque es un caso poco habitual.
    ▣ Con ellos, podemos:
    □ Modificar eventos del stream y reemitir la versión
    modificada.
    □ Filtrar elementos, para que no continúen
    downstream.
    □ Combinar elementos.
    □ ¡Multithreading!

    View Slide

  15. Operadores sobre el stream
    ▣ Casi cualquier cosa que se te ocurra, se puede
    hacer combinando operadores.
    flatmapthatshit.com

    View Slide

  16. Operadores sobre el stream
    ▣ Hay cientos de operadores aplicables sobre
    un stream.
    ▣ Se pueden definir operadores propios,
    aunque es un caso poco habitual.
    ▣ Con ellos, podemos:
    □ Modificar eventos del stream y reemitir la versión
    modificada.
    □ Filtrar elementos, para que no continúen
    downstream.
    □ Combinar elementos.
    □ ¡Multithreading!

    View Slide

  17. RX marbles

    View Slide

  18. RX marbles

    View Slide

  19. Un ejemplo de stream
    Observable.range(0, 5)
    .map(x -> x*x)
    .filter(x -> x>4)
    .subscribe(x-> print(x))

    View Slide

  20. Una nota sobre funciones lambda
    ▣ RxJava funciona sobre Java6, es decir,
    Android 2.3.
    ▣ Java no soporta funciones lambda hasta Java
    8.
    ▣ Para ello, yo utilizo Retrolambda, que permite
    utilizar dichas funciones compiladas con
    bytecode Java 6.

    View Slide

  21. Código para Java 6
    Observable.just("http://example.com/image.jpg")
    .map(new Func1() {
    @Override
    public Bitmap call(String url) {
    return loader.load(url);
    }
    })
    .subscribe(...)

    View Slide

  22. Código para Java 8
    Observable.just("http://example.com/image.jpg")
    .map(url -> loader.load(url))
    .subscribe(...)

    View Slide

  23. Warning: bytecode ahead

    View Slide

  24. Cuidado!
    ▣ Bytecode de la versión lambda
    iconst_1
    invokestatic java/lang/Integer
    valueOf((I)Ljava/lang/Integer;);
    invokestatic rx/Observable
    just((Ljava/lang/Object;)Lrx/Observable;);
    invokedynamic call(rx.functions.Func1);
    .map(x -> x*x)

    View Slide

  25. Cuidado!
    ▣ Bytecode de la versión con new Func1
    iconst_1
    invokestatic java/lang/Integer
    valueOf((I)Ljava/lang/Integer;);
    invokestatic rx/Observable
    just((Ljava/lang/Object;)Lrx/Observable;);
    new anpez/Test$1
    dup
    aload0 // reference to self
    invokespecial anpez/Test$1 ((Lanpez/Test;)V);
    .map(x -> x*x)

    View Slide

  26. 4.
    Threading .observeOn /
    .subscribeOn

    View Slide

  27. subscribeOn vs observeOn
    ▣ Estos dos operadores especiales se utilizan
    para indicar a RxJava en qué hilos se ha de
    ejecutar cada paso.
    ▣ subscribeOn() indica en qué hilo se han de
    emitir los eventos.
    ▣ observeOn() especifica en qué hilo se ejecuta
    el .onNext().
    ▣ Por defecto, todo ocurre en el mismo hilo en
    que se suscribe el Observer.

    View Slide

  28. Un ejemplo completo (I)
    ▣ En este ejemplo cargamos una imagen de red.
    Observable.just(url)
    .map(url -> loader.load(url))
    .subscribe(ImageView::setBitmap);

    View Slide

  29. Un ejemplo completo (II)
    ▣ Ahora la carga se produce en un hilo de I/O.
    Observable.just(url)
    .map(url -> loader.load(url))
    .subscribeOn(Schedulers.io())
    .subscribe(ImageView::setBitmap);

    View Slide

  30. Un ejemplo completo (y III)
    ▣ De este modo, la vista se toca en el hilo
    correcto.
    Observable.just(url)
    .map(url -> loader.load(url))
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(ImageView::setBitmap);

    View Slide

  31. 5.
    Casos de uso Ejemplos reales

    View Slide

  32. Chisme for Sensu
    Ejemplos extraídos de la
    última aplicación que he
    desarrollado utilizando
    RxJava: un cliente para Sensu

    View Slide

  33. Retrofit
    ▣ Con Retrofit podemos obtener un Observable
    a partir de una llamada.
    public interface ChecksService {
    @GET("checks")
    Observable> all();
    @POST("request")
    Observable issue(@Body
    Map body);
    }

    View Slide

  34. Retrofit
    ▣ Y podemos aplicarle los operadores que
    queramos para transformar el resultado.
    public Observable issue(Check check) {
    Map params = new HashMap<>();
    params.put("check", check.name());
    return checksService.issue(params)
    .map(IssueResponse::issued)
    .map(time -> time*1000)
    .map(Date::new);
    }

    View Slide

  35. RxBinding
    ▣ Librería de Jake Wharton para utilizar RxJava
    sobre vistas de Android.
    ▣ Elimina la necesidad de utilizar listeners.
    ▣ Simplifica operaciones muy comunes.
    RxTextView.textChanges(hostTextView)
    .subscribe(this::checkHost);

    View Slide

  36. @Mock HostsRepository hostsRepository;
    Host host = new Host(1, "[email protected]", "https",
    "test.com", 443, "user", "pass");
    when(hostsRepository
    .all())
    .thenReturn(Observable.just(host));
    Test!
    ▣ RxJava también te ayuda a escribir tests.
    ▣ Utilizando Espresso con Mockito, es muy fácil
    mockear un método que devuelve un
    Observable.

    View Slide

  37. Observable.just(drawerBuilder)
    .subscribeOn(Schedulers.computation())
    .observeOn(Schedulers.computation())
    ... // Build drawer on background
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(builder -> {
    drawer = builder.build();
    mainViewModel.initialize();
    });
    Whatever!
    ▣ Lo utilizo casi para cualquier cosa.

    View Slide

  38. Observable.interval(swapMs, TimeUnit.MILLISECONDS)
    .limit(swapRepeats)
    .observeOn(AndroidSchedulers.mainThread())
    .doOnNext(t -> moveShellVS.execute())
    .doOnCompleted(() -> {
    status.set(RUNNING);
    currentTry.set(1);
    })
    .subscribe();
    Whatever!

    View Slide

  39. 6.
    Conclusiones Warnings varios

    View Slide

  40. View Slide

  41. Gotchas
    ▣ Es muy importante definir un .doOnError()
    que recoja los posibles errores.
    ▣ Los Observers deben poder procesar eventos
    más rápido de lo que los emiten los
    Observables (Backpressure).
    □ No suele ser un problema, hasta que intentamos
    hacer algo avanzado.
    ▣ NO utilizar Observable.create(). Si usamos
    RxJava2, utilizar Flowable.create().

    View Slide

  42. Gotchas
    ▣ No usarlo ciegamente, implica una
    penalización de rendimiento.
    ▣ La documentación a veces no es demasiado
    clara.
    □ Los comentarios de StackOverflow tampoco suelen
    ayudar mucho.
    ▣ Los tutoriales que verás, suelen utilizar
    incorrectamente Observable.create().

    View Slide

  43. Conclusiones
    ▣ Se puede usar RxJava para reemplazar
    completamente las AsyncTask.
    ▣ Por norma general, se puede usar RxJava (via
    RxBinding o hecho por nosotros mismos), en
    lugar de Listeners.
    ▣ Para tareas de entrada/salida o de
    computación es perfecto, ya que la
    sobrecarga es mínima en este caso.

    View Slide

  44. Credits
    Special thanks to all the people who made and
    released these awesome resources for free:
    ▣ Presentation template by SlidesCarnival

    View Slide