RxJava es una de las bibliotecas más populares para llevar la programación reactiva a la plataforma Android, y en esta serie de tres partes, le he estado mostrando cómo comenzar a usar esta biblioteca en sus propios proyectos con Android.
En Introducción a RxJava 2 para Android, analizamos qué es RxJava y lo que ofrece a los desarrolladores de Android, antes de crear una aplicación Hello World que demostrara los tres componentes principales de RxJava: un Observable
, un Observador
, y una suscripción.
En el tutorial de Operadores de programación reactiva en RxJava 2, observamos cómo realizar transformaciones de datos complejas utilizando operadores, y cómo puede combinar Operador
s y Programador
s para que finalmente el multihilo en Android sea una experiencia sin dolor.
También abordamos RxAndroid, una biblioteca diseñada específicamente para ayudarte a utilizar RxJava en tus proyectos de Android, pero hay mucho más que explorar en RxAndroid. Entonces, en este post, me centraré únicamente en la familia de bibliotecas RxAndroid.
Al igual que RxJava, RxAndroid se sometió a una revisión masiva en su versión 2. El equipo de RxAndroid decidió modularizar la biblioteca, trasladando gran parte de su funcionalidad a los módulos adicionales de RxAndroid dedicados.
En este artículo, le mostraré cómo configurar y usar algunos de los módulos RxAndroid más populares y potentes, incluida una biblioteca que puede hacer escuchas, controladores y Observadores de texto
una cosa del pasado, dándote la capacidad de manejar alguna Evento de la interfaz de usuario de Android como Observable
.
Y como las pérdidas de memoria causadas por suscripciones incompletas son el mayor inconveniente de usar RxJava en sus aplicaciones de Android, también le mostraré cómo usar un módulo RxAndroid que puede manejar el proceso de suscripción por usted. Al final de este artículo, sabrás cómo usar RxJava en cualquier Actividad
o Fragmento
, Sin correr el riesgo de encontrarse. alguna Fugas de memoria relacionadas con RxJava.
Reaccionar a los eventos de la IU, como los toques, los golpes y la entrada de texto, es una parte fundamental del desarrollo de casi cualquier aplicación de Android, pero el manejo de los eventos de la IU de Android no es particularmente sencillo..
Por lo general, reaccionarás a los eventos de la interfaz de usuario utilizando una combinación de escuchas y controladores., Observadores de texto
, y posiblemente otros componentes dependiendo del tipo de IU que esté creando. Cada uno de estos componentes requiere que usted escriba una cantidad significativa de código repetitivo, y para empeorar las cosas, no hay coherencia en la forma en que implementa estos diferentes componentes. Por ejemplo, manejas Al hacer clic
eventos mediante la implementación de una OnClickListener
:
Button button = (Button) findViewById (R.id.button); button.setOnClickListener (new View.OnClickListener () @Override public void onClick (View v) // Realizar algunos trabajos //);
Pero esto es completamente diferente a cómo implementarías un TextWatcher:
final EditText name = (EditText) v.findViewById (R.id.name); // Cree un TextWatcher y especifique que este TextWatcher debe llamarse cada vez que el contenido de EditText cambie // name.addTextChangedListener (new TextWatcher () @Override public void beforeTextChanged (CharSequence s, int start, int after, int after) @ Anular el vacío público onTextChanged (CharSequence s, int start, int before, int count) // Realizar algún trabajo // @ Anular el void public void afterTextChanged (Editable s) );
Esta falta de coherencia puede potencialmente agregar mucha complejidad a su código. Y si tiene componentes de UI que dependen de la salida de otros componentes de UI, entonces prepárese para que las cosas se compliquen aún más. Incluso un caso de uso simple, como pedirle al usuario que escriba su nombre en un Editar texto
Para que puedas personalizar el texto que aparece en las siguientes. Visiones de texto
-requiere devoluciones de llamada anidadas, que son muy difíciles de implementar y mantener. (Algunas personas se refieren a devoluciones de llamada anidadas como "infierno de devolución de llamada").
Claramente, un enfoque estandarizado para el manejo de eventos de UI tiene el potencial de simplificar enormemente su código, y RxBinding es una biblioteca que se compromete a hacer precisamente eso, al proporcionar enlaces que le permiten convertir cualquier Android Ver
evento en un Observable
.
Una vez que haya convertido un evento de visualización en un Observable
, emitirá sus eventos de IU como flujos de datos a los que puede suscribirse exactamente de la misma manera que lo haría con cualquier otro Observable
.
Ya hemos visto cómo capturarías un evento de clic con el estándar de Android OnClickListener
, veamos cómo lograrías los mismos resultados usando RxBinding:
importar com.jakewharton.rxbinding.view.RxView;… Button button = (Button) findViewById (R.id.button); RxView.clicks (button) .subscribe (aVoid -> // Realice algunos trabajos aquí //);
Este enfoque no solo es más conciso, sino que es una implementación estándar que puede aplicar a todos los eventos de IU que se producen a lo largo de su aplicación. Por ejemplo, la captura de entrada de texto sigue el mismo patrón que la captura de eventos de clic:
RxTextView.textChanges (editText) .subscribe (charSequence -> // Realice algunos trabajos aquí //);
Para que pueda ver exactamente cómo RxBinding puede simplificar el código relacionado con la interfaz de usuario de su aplicación, creemos una aplicación que demuestre algunos de estos enlaces en acción. También voy a incluir un Ver
eso depende de la salida de otro Ver
, para demostrar cómo RxBinding simplifica la creación de relaciones entre los componentes de la interfaz de usuario.
Esta aplicación va a consistir en:
Botón
que muestra un tostada
cuando se toca.Editar texto
que detecta cambios de texto.Vista de texto
que se actualiza para mostrar los contenidos de la Editar texto
.Cree un proyecto de Android Studio con la configuración de su elección y luego abra su nivel de módulo construir.gradle Archivo y agregue la última versión de la biblioteca RxBinding como una dependencia de proyecto. Con el fin de mantener el código de repetición al mínimo, también voy a utilizar lambdas, así que he actualizado mi construir.gradle Archivo para soportar esta característica de Java 8:
aplique el complemento: 'com.android.application' android compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig applicationId "com.jessicathornsby.myapplication" ".nal" "" ". AndroidJUnitRunner "// Habilitar la cadena de herramientas de Jack // jackOptions enabled true buildTypes release minifyEnabled false proguardFiles getDefaultProguardFile ('proguard-android.txt'), 'proguard-rules.pro' // Set sourceCompatibility and targetCompatibility to JavaVersion.VERSION_1_8 // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 dependencies compile fileTree (dir: 'libs', include: ['* .jar']) androidTestCompile ('com.android.support.estport) : espresso-core: 2.2.2 ', exclude group:' com.android.support ', módulo:' support-anotaciones ') // Agregue la biblioteca RxBinding central // compile' com.jakewharton.rxbinding: rxbinding: 0.4.0 'compilar' com .android.support: appcompat-v7: 25.3.0 '// No olvide agregar las dependencias RxJava y RxAndroid // compile' io.reactivex.rxjava2: rxandroid: 2.0.1 'compile' io.reactivex.rxjava2: rxjava: 2.0.5 'testCompile' junit: junit: 4.12 '
Cuando trabaja con varias bibliotecas de RxJava, es posible que encuentre un Archivos duplicados copiados en APK META-INF / DEPENDENCIAS Mensaje de error en tiempo de compilación. Si encuentra este error, la solución es suprimir estos archivos duplicados agregando lo siguiente a su nivel de módulo construir.gradle expediente:
android packagingOptions // Use "exclude" para señalar el archivo (o archivos) específico sobre el que Android Studio se está quejando // exclude 'META-INF / rxjava.properties'
Sincronice sus archivos de Gradle, y luego cree un diseño que consiste en un Botón
, un Editar texto
, y un Vista de texto
:
Ahora veamos cómo usaría estos RxBinding para capturar los diversos eventos de IU a los que debe reaccionar nuestra aplicación. Para empezar, declara tus importaciones y define el Actividad principal
clase.
paquete com.jessicathornsby.myapplication; importar android.os.Bundle; importar android.support.v7.app.AppCompatActivity; importar android.widget.Button; importar android.widget.EditText; importar android.widget.TextView; importar android.widget.Toast; // Importe la clase view.RxView, para que pueda usar RxView.clicks // import com.jakewharton.rxbinding.view.RxView; // Importe widget.RxTextView para que pueda usar RxTextView.textChanges // import com.jakewharton.rxbinding.widget.RxTextView; la clase pública MainActivity extiende AppCompatActivity @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); Button button = (Button) findViewById (R.id.button); TextView textView = (TextView) findViewById (R.id.textView); EditText editText = (EditText) findViewById (R.id.editText); // El código para los enlaces va aquí // // ... //
Ahora puede comenzar a agregar enlaces para responder a eventos de IU. los RxView.clicks
El método se utiliza para enlazar eventos de clic. Cree un enlace para mostrar un brindis cada vez que se haga clic en el botón:
RxView.clicks (botón) .subscribe (aVoid -> Toast.makeText (MainActivity.this, "RxView.clicks", Toast.LENGTH_SHORT) .show (););
A continuación, utilice el RxTextView.textChanges ()
Método para reaccionar ante un evento de cambio de texto actualizando el Vista de texto
con los contenidos de nuestra Editar texto
.
RxTextView.textChanges (editText) .subscribe (charSequence -> textView.setText (charSequence););
Cuando ejecute su aplicación, terminará con una pantalla como la siguiente.
Instale su proyecto en un teléfono inteligente o tableta Android física o un AVD compatible, y luego pase un tiempo interactuando con los diversos elementos de la interfaz de usuario. Su aplicación debe reaccionar al hacer clic en eventos e ingreso de texto como de costumbre, y todo sin un oyente, TextWatcher o devolución de llamada a la vista!
Si bien la biblioteca RxBinding principal proporciona enlaces para todos los elementos de la interfaz de usuario que conforman la plataforma estándar de Android, también hay módulos hermanos RxBinding que proporcionan enlaces para las Vistas que se incluyen como parte de las diversas bibliotecas de soporte de Android..
Si ha agregado una o más bibliotecas de soporte a su proyecto, entonces, por lo general, también querrá agregar el módulo RxBinding correspondiente..
Estos módulos hermanos siguen una convención de nomenclatura sencilla que facilita la identificación de la biblioteca de soporte de Android correspondiente: cada módulo hermano simplemente toma el nombre de la biblioteca de soporte y reemplaza com.android
con com.jakewharton.rxbinding2: rxbinding
.
compile com.jakewharton.rxbinding2: rxbinding-recyclerview-v7: 2.0.0 '
compile 'com.jakewharton.rxbinding2: rxbinding-support-v4: 2.0.0'
compile 'com.jakewharton.rxbinding2: rxbinding-appcompat-v7: 2.0.0'
compile 'com.jakewharton.rxbinding2: rxbinding-design: 2.0.0'
compile 'com.jakewharton.rxbinding2: rxbinding-recyclerview-v7: 2.0.0'
compile 'com.jakewharton.rxbinding2: rxbinding-leanback-v17: 2.0.0'
Si está utilizando Kotlin en sus proyectos de Android, también hay una versión de Kotlin disponible para cada módulo RxBinding. Para acceder a la versión de Kotlin, simplemente agregue -Kotlin
al nombre de la biblioteca con la que desea trabajar, por lo que:
compile 'com.jakewharton.rxbinding2: rxbinding-design: 2.0.0'
Se convierte en
compile 'com.jakewharton.rxbinding2: rxbinding-design-kotlin: 2.0.0'
Una vez que hayas convertido un Ver
evento en un Observable
, todos esos eventos se emiten como un flujo de datos. Como ya hemos visto, puede suscribirse a estas transmisiones y luego realizar cualquier tarea que necesite para desencadenar este evento de IU en particular, como mostrar una tostada
o actualizando un Vista de texto
. Sin embargo, también puede aplicar cualquiera de la enorme colección de operadores de RxJava a este flujo observable e incluso encadenar varios operadores para realizar transformaciones complejas en sus eventos de IU..
Hay demasiados operadores para discutir en un solo artículo (y la documentación oficial enumera a todos los operadores de todos modos) pero cuando se trata de trabajar con eventos de la interfaz de usuario de Android, hay algunos operadores que pueden ser particularmente útiles..
rebotar ()
OperadorEn primer lugar, si le preocupa que un usuario impaciente pueda tocar repetidamente un elemento de la interfaz de usuario, lo que podría confundir su aplicación, entonces puede usar la rebotar ()
operador para filtrar los eventos de IU que se emiten en rápida sucesión.
En el siguiente ejemplo, estoy especificando que este botón debe reaccionar a una Al hacer clic
evento solo si ha habido al menos un espacio de 500 milisegundos desde el evento de clic anterior:
RxView.clicks (botón) .debounce (500, TimeUnit.MILLISECONDS) .subscribe (aVoid -> Toast.makeText (MainActivity.this, "RxView.clicks", Toast.LENGTH_SHORT) .show (););
publicar()
OperadorTambién puedes usar el publicar()
operador para adjuntar varios oyentes a la misma vista, algo que tradicionalmente ha sido difícil de implementar en Android.
los publicar()
operador convierte un estándar Observable
en una conectable observable. Mientras que un observable normal comienza a emitir elementos tan pronto como el primer observador se suscribe a él, un observable conectable no emitirá nada hasta que usted lo indique explícitamente, aplicando el conectar()
operador. Esto le brinda una oportunidad para suscribir múltiples observadores, sin que los elementos observables comiencen a emitir tan pronto como se realice la primera suscripción..
Una vez que haya creado todas sus suscripciones, simplemente aplique el conectar()
El operador y el observable comenzarán a emitir datos a todos sus observadores asignados.
Como hemos visto a lo largo de esta serie, RxJava puede ser una herramienta poderosa para crear aplicaciones de Android más reactivas e interactivas, con mucho menos código del que normalmente necesitaría para obtener los mismos resultados solo con Java. Sin embargo, hay un gran inconveniente en el uso de RxJava en sus aplicaciones de Android: el potencial de pérdidas de memoria causadas por suscripciones incompletas..
Estas fugas de memoria ocurren cuando el sistema Android intenta destruir una Actividad
que contiene una carrera Observable
. Como el observable se está ejecutando, su observador seguirá manteniendo una referencia a la actividad, y el sistema no podrá recolectar la basura como resultado de ello..
Desde que Android destruye y recrea Actividad
s cada vez que cambia la configuración del dispositivo, su aplicación podría estar creando un duplicado Actividad
cada vez que el usuario cambia entre modo vertical y horizontal, así como cada vez que abre y cierra el teclado de su dispositivo.
Estas actividades se mantendrán en segundo plano, posiblemente nunca se recogerá la basura. Dado que las Actividades son objetos grandes, esto puede llevar rápidamente a grave problemas de administración de memoria, especialmente porque los teléfonos inteligentes y las tabletas con Android tienen una memoria limitada, para empezar. La combinación de una gran pérdida de memoria y una memoria limitada puede resultar rápidamente en una Sin memoria error.
Las fugas de memoria de RxJava pueden causar estragos en el rendimiento de su aplicación, pero hay una biblioteca de RxAndroid que le permite usar RxJava en su aplicación sin tener que preocuparse por las fugas de memoria..
La biblioteca RxLifecycle, desarrollada por Trello, proporciona las API de manejo del ciclo de vida que puede utilizar para limitar la vida útil de un Observable
al ciclo de vida de un Actividad
o Fragmento
. Una vez que se establece esta conexión, RxLifecycle terminará la secuencia del observable en respuesta a los eventos del ciclo de vida que ocurren en la actividad o fragmento asignado de ese observable. Esto significa que puede crear un observable que finaliza automáticamente cada vez que se destruye una actividad o un fragmento.
Tenga en cuenta que estamos hablando de terminando una secuencia, y no darse de baja. Aunque a menudo se habla de RxLifecycle en el contexto de la gestión del proceso de suscripción / cancelación de la suscripción., técnicamente no anula la suscripción de un observador. En su lugar, la biblioteca RxLifecycle termina la secuencia observable emitiendo cualquiera de los dos onComplete ()
o onError ()
método. Cuando se da de baja, el observador deja de recibir notificaciones de su observable, incluso si ese observable sigue emitiendo elementos. Si específicamente requiere un comportamiento para darse de baja, entonces eso es algo que deberá implementar usted mismo.
Para usar RxLifecycle en sus proyectos de Android, abra su nivel de módulo construir.gradle Archivo y agregue la última versión de la biblioteca RxLifeycle, más la biblioteca RxLifecycle de Android:
dependencias … compile 'com.trello.rxlifecycle2: rxlifecycle: 2.0.1' compile 'com.trello.rxlifecycle2: rxlifecycle-android: 2.0.1'
Luego, en el Actividad
o Fragmento
donde desee utilizar las API de manejo del ciclo de vida de la biblioteca, amplíe RxActividad
, RxAppCompatActivity
o RxFragment
, y agregue la declaración de importación correspondiente, por ejemplo:
importar com.trello.rxlifecycle2.components.support.RxAppCompatActivity;… clase pública MainActivity extiende RxAppCompatActivity
Cuando se trata de unir un Observable
al ciclo de vida de un Actividad
o Fragmento
, puede especificar el evento del ciclo de vida donde el observable debe terminar, o puede dejar que la biblioteca RxLifecycle decida cuándo debe terminar la secuencia observable.
De manera predeterminada, RxLifecycle terminará un evento observable en el ciclo de vida complementario al evento en el que se produjo esa suscripción, por lo que si se suscribe a un observable durante su actividad onCreate ()
método, entonces RxLifecycle terminará la secuencia observable durante esa actividad onDestroy ()
método. Si te suscribes durante un Fragmento
es onAttach ()
método, entonces RxLifecycle terminará esta secuencia en el onDetach ()
método.
Puede dejar esta decisión hasta RxLifecycle, usando RxLifecycleAndroid.bindActivity
:
ObservablemyObservable = Observable.range (0, 25);… @Override public void onResume () super.onResume (); myObservable .compose (RxLifecycleAndroid.bindActivity (lifecycle)) .subscribe ();
Alternativamente, puede especificar el evento del ciclo de vida donde RxLifecycle debe terminar un Observable
secuencia, usando RxLifecycle.bindUntilEvent
.
Aquí, estoy especificando que la secuencia observable debe terminarse en onDestroy ()
:
@Override public void onResume () super.onResume (); myObservable .compose (RxLifecycle.bindUntilEvent (lifecycle, ActivityEvent.DESTROY)) .subscribe ();
La biblioteca final que veremos es RxPermissions, que fue diseñada para ayudarte a usar RxJava con el nuevo modelo de permisos introducido en Android 6.0. Esta biblioteca también le permite emitir una solicitud de permiso y manejar el resultado del permiso en la misma ubicación, en lugar de solicitar el permiso en un lugar y luego manejar sus resultados por separado, en Activity.onRequestPermissionsResult ()
.
Comience agregando la biblioteca RxPermissions a su construir.gradle expediente:
compile 'com.tbruyelle.rxpermissions2: rxpermissions: 0.9.3@aar'
Luego, crea una instancia de RxPermissions:
RxPermissions rxPermissions = new RxPermissions (this);
Entonces está listo para comenzar a realizar solicitudes de permisos a través de la biblioteca RxPermissions, utilizando la siguiente fórmula:
rxPermissions.request (Manifest.permission.READ_CONTACTS) .subscribe (concedido -> si (otorgado) // El permiso ha sido otorgado // else // El permiso ha sido denegado //);
Dónde emitir su solicitud de permisos es crucial, ya que siempre existe la posibilidad de que el alojamiento Actividad
se puede destruir y luego volver a crear mientras el diálogo de permisos está en pantalla, generalmente debido a un cambio de configuración, como que el usuario se mueva entre los modos vertical y horizontal. Si esto ocurre, es posible que su suscripción no se vuelva a crear, lo que significa que no estará suscrito a RxPermissions observable y no recibirá la respuesta del usuario al cuadro de diálogo de solicitud de permiso. Para garantizar que su aplicación recibe la respuesta del usuario., siempre invocar su solicitud durante una fase de inicialización como Activity.onCreate ()
, Activity.onResume ()
, o View.onFinishInflate ()
.
No es raro que las funciones requieran varios permisos. Por ejemplo, enviar un mensaje SMS generalmente requiere que su aplicación tenga la ENVIAR SMS
y LEER_CONTACTOS
permisos La biblioteca RxPermissions proporciona un método conciso para emitir múltiples solicitudes de permisos y luego combinar las respuestas del usuario en una sola falso
(uno o más permisos fueron denegados) o cierto
(Todos los permisos fueron otorgados) respuesta a la que luego puede reaccionar.
RxPermissions.getInstance (this) .request (Manifest.permission.SEND_SMS, Manifest.permission.READ_CONTACTS) .subscribe (concedido -> if (concedido) // Todos los permisos fueron otorgados // else // Uno o más permisos fue negado// );
Por lo general, deseará desencadenar una solicitud de permiso en respuesta a un evento de IU, como que el usuario toque un elemento o botón del menú, por lo que RxPermissions y RxBiding son dos bibliotecas que funcionan particularmente bien juntas.
El manejo del evento UI como un observable y la solicitud de permiso a través de RxPermissions le permite realizar una gran cantidad de trabajo con solo unas pocas líneas de código:
RxView.clicks (findViewById (R.id.enableBluetooth)) .compose (RxPermissions.getInstance (this) .ensure (Manifest.permission.BLUETOOTH_ADMIN)) .subscribe (concedido -> // Se ha hecho clic en el botón 'enableBluetooth' / /);
Después de leer este artículo, tiene algunas ideas de cómo eliminar una gran cantidad de código repetitivo de sus aplicaciones de Android: usar RxJava para manejar todos los eventos de IU de su aplicación y emitir sus solicitudes de permiso a través de RxPermissions. También vimos cómo puedes usar RxJava en cualquier Android Actividad
o Fragmento
, sin tener que preocuparse por las pérdidas de memoria que pueden ser causadas por suscripciones incompletas.
Hemos explorado algunas de las bibliotecas más populares y útiles de RxJava y RxAndroid de esta serie, pero si está ansioso por ver qué más tiene RxJava para ofrecer a los desarrolladores de Android, consulte algunas de las muchas otras bibliotecas de RxAndroid. Encontrará una lista completa de bibliotecas RxAndroid adicionales en GitHub.
Mientras tanto, echa un vistazo a algunas de nuestras otras publicaciones de desarrollo de Android aquí en Envato Tuts+!