Este es un repositorio con ejemplos útiles del mundo real sobre el uso de RxJava con Android. Por lo general, estará en un estado constante de "Trabajo en progreso" (WIP).
También he estado dando charlas sobre Learning Rx utilizando muchos de los ejemplos enumerados en este repositorio.
using
)Un requisito común es descargar operaciones intensivas de E/S largas y pesadas a un subproceso en segundo plano (subproceso que no es de UI) y enviar los resultados a la UI/subproceso principal, al finalizar. Esta es una demostración de cómo se pueden descargar operaciones de larga duración a un hilo en segundo plano. Una vez realizada la operación, volvemos al hilo principal. ¡Todo usando RxJava! Piense en esto como un reemplazo de AsyncTasks.
La operación larga se simula mediante una llamada de bloqueo a Thread.sleep (dado que esto se hace en un hilo en segundo plano, nuestra interfaz de usuario nunca se interrumpe).
Para ver realmente brillar este ejemplo. Presione el botón varias veces y vea cómo el clic en el botón (que es una operación de la interfaz de usuario) nunca se bloquea porque la operación larga solo se ejecuta en segundo plano.
Esta es una demostración de cómo se pueden acumular eventos usando la operación "búfer".
Se proporciona un botón y acumulamos la cantidad de clics en ese botón, durante un período de tiempo y luego escupimos los resultados finales.
Si presiona el botón una vez, recibirá un mensaje que indica que el botón se presionó una vez. Si lo presiona 5 veces seguidas en un lapso de 2 segundos, obtendrá un solo registro que indica que presionó ese botón 5 veces (frente a 5 registros individuales que dicen "Botón presionado una vez").
Nota:
Si está buscando una solución más infalible que acumule toques "continuos" en lugar de solo el número de toques dentro de un período de tiempo, mire la demostración de EventBus donde se utiliza una combinación de operadores publish
y buffer
. Para obtener una explicación más detallada, también puede consultar esta publicación de blog.
Esta es una demostración de cómo se pueden asimilar los eventos de manera que solo se respete el último. Un ejemplo típico de esto son los cuadros de resultados de búsqueda instantánea. Mientras escribes la palabra "Bruce Lee", no querrás ejecutar búsquedas de B, Br, Bru, Bruce, Bruce, Bruce L... etc. Sino esperar inteligentemente un par de momentos, asegurarte de que el usuario ha terminado de escribir la palabra completa y luego lanza una sola llamada para "Bruce Lee".
A medida que escribe en el cuadro de entrada, no generará mensajes de registro con cada cambio de carácter de entrada, sino que solo seleccionará el último evento emitido (es decir, entrada) y lo registrará.
Este es el método antirrebote/throttleWithTimeout en RxJava.
Retrofit de Square es una biblioteca increíble que ayuda a establecer conexiones en red fácilmente (incluso si aún no has dado el salto a RxJava, deberías echarle un vistazo). Funciona aún mejor con RxJava y estos son ejemplos de la API de GitHub, tomados directamente de la charla del desarrollador semidiós de Android Jake Wharton en Netflix. Puedes ver la charla en este enlace. Por cierto, mi motivación para usar RxJava fue asistir a esta charla en Netflix.
(Nota: es más probable que alcances la cuota de API de GitHub bastante rápido, así que envía un token de OAuth como parámetro si deseas seguir ejecutando estos ejemplos con frecuencia).
Las vistas de actualización automática son algo genial. Si ha tratado con Angular JS antes, tienen un concepto bastante ingenioso llamado "enlace de datos bidireccional", por lo que cuando un elemento HTML está vinculado a un objeto modelo/entidad, constantemente "escucha" los cambios en esa entidad y actualiza automáticamente su estado según el modelo. Utilizando la técnica de este ejemplo, podría utilizar un patrón como el patrón Modelo de vista de presentación con gran facilidad.
Si bien el ejemplo aquí es bastante rudimentario, la técnica utilizada para lograr el doble enlace utilizando un Publish Subject
es mucho más interesante.
Este es un ejemplo de sondeo utilizando RxJava Schedulers. Esto es útil en los casos en los que desea sondear constantemente un servidor y posiblemente obtener nuevos datos. La llamada de red se "simula", por lo que fuerza un retraso antes de devolver una cadena resultante.
Para ello existen dos variantes:
El segundo ejemplo es básicamente una variante del retroceso exponencial.
En lugar de utilizar RetryWithDelay, aquí utilizamos repetirWithDelay. Para comprender la diferencia entre Reintentar (cuando) y Repetir (cuando), sugeriría la fantástica publicación de Dan sobre el tema.
Un enfoque alternativo al sondeo retrasado sin el uso de repeatWhen
sería utilizar observables de retraso anidados encadenados. Consulte startExecutingWithExponentialBackoffDelay en el ejemplo de ExponentialBackOffFragment.
El retroceso exponencial es una estrategia en la que, en función de la retroalimentación de una determinada salida, alteramos la velocidad de un proceso (generalmente reduciendo el número de reintentos o aumentando el tiempo de espera antes de reintentar o volver a ejecutar un determinado proceso).
El concepto tiene más sentido con ejemplos. RxJava hace que sea (relativamente) sencillo implementar dicha estrategia. Mi agradecimiento a Mike por sugerir la idea.
Digamos que tiene una falla en la red. Una estrategia sensata sería NO seguir reintentando la llamada de red cada segundo. En cambio, sería inteligente (no... ¡elegante!) volver a intentarlo con retrasos cada vez mayores. Entonces intentas en el segundo 1 ejecutar la llamada de red, ¿no? inténtalo después de 10 segundos... ¿negativo? Intente después de 20 segundos, ¿no hay galletas? inténtalo después de 1 minuto. Si esto sigue fallando, ¡tienes que renunciar a la red!
Simulamos este comportamiento usando RxJava con el operador retryWhen
.
Fragmento de código de RetryWithDelay
cortesía:
Mire también el ejemplo de Encuesta donde utilizamos un mecanismo de retroceso exponencial muy similar.
Otra variante de la estrategia de retroceso exponencial es ejecutar una operación un número determinado de veces pero con intervalos retrasados. Entonces, ejecuta una determinada operación dentro de 1 segundo, luego la ejecuta nuevamente dentro de 10 segundos, luego ejecuta la operación dentro de 20 segundos. Después de un total de 3 veces, dejas de ejecutar.
Simular este comportamiento es en realidad mucho más simple que el mecanismo de reintento anterior. Puede utilizar una variante del operador delay
para lograr esto.
.combineLatest
)Gracias a Dan Lew por darme esta idea en el podcast fragmentado: episodio 4 (alrededor del minuto 4:30).
.combineLatest
le permite monitorear el estado de múltiples observables a la vez de manera compacta en una sola ubicación. El ejemplo demostrado muestra cómo puede utilizar .combineLatest
para validar un formulario básico. Hay 3 entradas principales para que este formulario se considere "válido" (un correo electrónico, una contraseña y un número). El formulario se volverá válido (el texto a continuación se vuelve azul :P) una vez que todas las entradas sean válidas. Si no es así, se muestra un error en las entradas no válidas.
Tenemos 3 observables independientes que rastrean los cambios de texto/entrada para cada uno de los campos del formulario ( WidgetObservable
de RxAndroid es útil para monitorear los cambios de texto). Después de que se nota un cambio de evento en las 3 entradas, el resultado se "combina" y se evalúa la validez del formulario.
Tenga en cuenta que la función Func3
que verifica la validez se activa solo después de que TODAS las 3 entradas hayan recibido un evento de cambio de texto.
El valor de esta técnica se vuelve más evidente cuando tiene más campos de entrada en un formulario. Manejarlo de otra manera con un montón de valores booleanos hace que el código esté desordenado y sea un poco difícil de seguir. Pero al usar .combineLatest
toda esa lógica se concentra en un bonito bloque de código compacto (todavía uso valores booleanos, pero eso fue para hacer que el ejemplo sea más legible).
Tenemos dos Observables de origen: un caché de disco (rápido) y una llamada de red (nueva). Normalmente, el disco Observable es mucho más rápido que la red Observable. Pero para demostrar el funcionamiento, también hemos utilizado un caché de disco falso "más lento" sólo para ver cómo se comportan los operadores.
Esto se demuestra utilizando 4 técnicas:
.concat
.concatEager
.merge
.publish
selector + fusionar + tomar hastaLa cuarta técnica es probablemente la que querrás utilizar eventualmente, pero es interesante analizar la progresión de las técnicas para entender por qué.
concat
es genial. Recupera información del primer Observable (caché de disco en nuestro caso) y luego del Observable de red posterior. Dado que el caché del disco es presumiblemente más rápido, todo parece estar bien y el caché del disco se carga rápidamente, y una vez que finaliza la llamada de red, intercambiamos los resultados "nuevos".
El problema con concat
es que el observable posterior ni siquiera comienza hasta que se completa el primer Observable. Eso puede ser un problema. Queremos que todos los observables comiencen simultáneamente pero produzcan los resultados de la manera que esperamos. Afortunadamente, RxJava presentó concatEager
, que hace exactamente eso. Inicia ambos observables pero almacena en buffer el resultado del último hasta que finaliza el primero. Esta es una opción completamente viable.
A veces, sin embargo, lo único que desea es comenzar a mostrar los resultados de inmediato. Suponiendo que el primer observable (por alguna extraña razón) tarda mucho en ejecutar todos sus elementos, incluso si los primeros elementos del segundo observable han bajado por el cable, se pondrá en cola por la fuerza. No necesariamente deseas "esperar" en ningún Observable. En estas situaciones, podríamos utilizar el operador merge
. Intercala elementos a medida que se emiten. Esto funciona muy bien y comienza a mostrar los resultados tan pronto como se muestran.
Similar al operador concat
, si su primer Observable es siempre más rápido que el segundo Observable, no tendrá ningún problema. Sin embargo, el problema con merge
es: si por alguna extraña razón el caché o el observable más lento emite un elemento después del observable más nuevo/más reciente, sobrescribirá el contenido más nuevo. Haga clic en el botón "FUSIONAR (DISCO MÁS LENTO)" en el ejemplo para ver este problema en acción. ¡Las contribuciones de @JakeWharton y @swankjesse llegan a 0! En el mundo real, esto podría ser malo, ya que significaría que los datos nuevos serían anulados por datos del disco obsoletos.
Para resolver este problema, puede utilizar la combinación en combinación con el operador publish
súper ingenioso que incluye un "selector". Escribí sobre este uso en una publicación de blog, pero tengo que agradecer a Jedi JW por recordarme esta técnica. publish
el observable de la red y le proporcionamos un selector que comienza a emitir desde la memoria caché del disco, hasta el punto en que el observable de la red comienza a emitir. Una vez que el observable de la red comienza a emitir, ignora todos los resultados del observable del disco. Esto es perfecto y soluciona cualquier problema que podamos tener.
Anteriormente, estaba usando el operador merge
, pero superé el problema de que los resultados se sobrescribieran al monitorear "resultAge". Consulte el antiguo ejemplo PseudoCacheMergeFragment
si tiene curiosidad por ver esta implementación anterior.
Este es un ejemplo súper simple y directo que le muestra cómo usar los operadores de timer
, interval
y delay
de RxJava para manejar una serie de casos en los que desea ejecutar una tarea en intervalos específicos. Básicamente di NO a los TimerTask
de Android.
Casos demostrados aquí:
Hay publicaciones de blog adjuntas que explican mucho mejor los detalles de esta demostración:
Una pregunta común que se hace al usar RxJava en Android es: "¿Cómo puedo reanudar el trabajo de un observable si se produce un cambio de configuración (rotación de actividad, cambio de idioma, etc.)?".
Este ejemplo le muestra una estrategia, a saber. utilizando fragmentos retenidos. Comencé a usar fragmentos retenidos como "fragmentos de trabajadores" después de leer esta fantástica publicación de Alex Lockwood hace bastante tiempo.
Presione el botón de inicio y gire la pantalla al contenido de su corazón; verás que el observable continúa desde donde lo dejó.
Hay ciertas peculiaridades sobre el "calor" de la fuente observable utilizada en este ejemplo. Consulte la publicación de mi blog donde explico los detalles.
Desde entonces, reescribí este ejemplo utilizando un enfoque alternativo. Si bien el enfoque ConnectedObservable
funcionó, ingresa al terreno de la "multidifusión", que puede ser complicado (seguridad de subprocesos, .refcount, etc.). Por otra parte, los temas son mucho más simples. Puedes verlo reescrito usando un Subject
aquí.
Escribí otra publicación de blog sobre cómo pensar en los temas donde entro en algunos detalles.
Volley es otra biblioteca de redes presentada por Google en IO '13. Un amable ciudadano de github aportó este ejemplo para que sepamos cómo integrar Volley con RxJava.
Aprovecho el uso simple de un Asunto aquí. Honestamente, si aún no tiene sus elementos disponibles a través de un Observable
(como a través de Retrofit o una solicitud de red), no hay una buena razón para usar Rx y complicar las cosas.
Este ejemplo básicamente envía el número de página a un Asunto, y el asunto se encarga de agregar los elementos. Observe el uso de concatMap
y el retorno de un Observable<List>
de _itemsFromNetworkCall
.
Por diversión, también he incluido un ejemplo PaginationAutoFragment
, que se "pagina automáticamente" sin que tengamos que presionar un botón. Debería ser sencillo de seguir si entiendes cómo funciona el ejemplo anterior.
Aquí hay algunas otras implementaciones sofisticadas (aunque disfruté leyéndolas, no terminé usándolas para mi aplicación del mundo real porque personalmente no creo que sea necesario):
El siguiente diagrama ascii expresa la intención de nuestro próximo ejemplo con garbo. f1,f2,f3,f4,f5 son esencialmente llamadas de red que, cuando se realizan, devuelven un resultado necesario para un cálculo futuro.
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
El código para este ejemplo ya ha sido escrito por un tal Sr. Skehlet en Internet. Dirígete a la esencia del código. Está escrito en Java puro (6), por lo que es bastante comprensible si has entendido los ejemplos anteriores. Lo publicaré aquí nuevamente cuando el tiempo lo permita o se me hayan acabado otros ejemplos convincentes.
Este es un ejemplo sencillo que demuestra el uso del operador .timeout
. El Botón 1 completará la tarea antes de la restricción de tiempo de espera, mientras que el Botón 2 forzará un error de tiempo de espera.
Observe cómo podemos proporcionar un Observable personalizado que indique cómo reaccionar ante una excepción de tiempo de espera.
using
) El operador using
es relativamente menos conocido y notoriamente difícil para Google. Es una API hermosa que ayuda a configurar un recurso (costoso), usarlo y luego desecharlo de manera limpia.
Lo bueno de este operador es que proporciona un mecanismo para utilizar recursos potencialmente costosos de una manera muy limitada. usando -> configurar, usar y desechar. Piense en conexiones de base de datos (como instancias de Realm), conexiones de socket, bloqueos de subprocesos, etc.
La multidifusión en Rx es como un arte oscuro. No mucha gente sabe cómo lograrlo sin preocuparse. Este ejemplo condiciona dos suscriptores (en forma de botones) y le permite agregar/eliminar suscriptores en diferentes momentos y ver cómo se comportan los diferentes operadores en esas circunstancias.
El observable de origen es un observable de temporizador ( interval
) y la razón por la que se eligió esto fue para elegir intencionalmente un observable sin terminación, para que pueda probar/confirmar si su experimento de multidifusión tendrá fugas.
También di una charla detallada sobre Multicasting en 360|Andev. Si tiene ganas y tiempo, le sugiero que vea esa charla primero (específicamente el segmento de permutación del operador de multidifusión) y luego juegue con el ejemplo aquí.
Todos los ejemplos aquí se han migrado para usar RxJava 2.X.
Usamos la biblioteca Interop de David Karnok en algunos casos, ya que ciertas bibliotecas como RxBindings, RxRelays, RxJava-Math, etc. aún no se han portado a 2.x.
Intento asegurarme de que los ejemplos no sean demasiado artificiales sino que reflejen un caso de uso del mundo real. Si tiene ejemplos útiles similares que demuestren el uso de RxJava, no dude en enviar una solicitud de extracción.
También estoy entendiendo RxJava, así que si crees que hay una mejor manera de hacer uno de los ejemplos mencionados anteriormente, abre un número que explique cómo. Aún mejor, envíe una solicitud de extracción.
El subprocesamiento de recetas es un asunto complicado. Para ayudar, este proyecto utiliza herramientas de análisis YourKit.
YourKit admite proyectos de código abierto con herramientas innovadoras e inteligentes para monitorear y crear perfiles de aplicaciones Java. YourKit es el creador de YourKit Java Profiler.
Licenciado bajo la Licencia Apache, Versión 2.0 (la "Licencia"). Puede obtener una copia de la Licencia en
http://www.apache.org/licenses/LICENSE-2.0
A menos que lo exija la ley aplicable o se acuerde por escrito, el software distribuido bajo la Licencia se distribuye "TAL CUAL", SIN GARANTÍAS NI CONDICIONES DE NINGÚN TIPO, ya sean expresas o implícitas. Consulte la Licencia para conocer el idioma específico que rige los permisos y limitaciones de la Licencia.
Usted acepta que todas las contribuciones a este repositorio, en forma de correcciones, solicitudes de extracción, nuevos ejemplos, etc., siguen la licencia mencionada anteriormente.