Aspectos básicos de Kotlin para Android 05.2: LiveData y observadores de LiveData

Este codelab es parte del curso Conceptos básicos de Kotlin para Android. Aprovecharás al máximo este curso si trabajas con los codelabs de forma secuencial. Todos los codelabs del curso se enumeran en la página de destino de los codelabs de Android Kotlin Fundamentals.

Introducción

En el codelab anterior, usaste un ViewModel en la app de GuessTheWord para permitir que los datos de la app sobrevivan a los cambios de configuración del dispositivo. En este codelab, aprenderás a integrar LiveData con los datos de las clases ViewModel. LiveData, que es uno de los componentes de arquitectura de Android, te permite compilar objetos de datos que notifican a las vistas cuando cambia la base de datos subyacente.

Para usar la clase LiveData, debes configurar "observadores" (por ejemplo, actividades o fragmentos) que observen los cambios en los datos de la app. LiveData está optimizado para los ciclos de vida, por lo que solo actualiza los observadores de componentes de la app que están en un estado de ciclo de vida activo.

Conocimientos que ya deberías tener

  • Cómo crear apps básicas para Android en Kotlin
  • Cómo navegar entre los destinos de tu app
  • Ciclo de vida de la actividad y el fragmento
  • Cómo usar objetos ViewModel en tu app
  • Cómo crear objetos ViewModel con la interfaz ViewModelProvider.Factory

Qué aprenderás

  • Qué hace que los objetos LiveData sean útiles
  • Cómo agregar LiveData a los datos almacenados en un ViewModel
  • Cuándo y cómo usar MutableLiveData
  • Cómo agregar métodos de observador para observar cambios en LiveData.
  • Cómo encapsular LiveData con una propiedad de respaldo
  • Cómo comunicarse entre un controlador de IU y su ViewModel correspondiente

Actividades

  • Usa LiveData para la palabra y la puntuación en la app de GuessTheWord.
  • Agrega observadores que detecten cuando cambie la palabra o la puntuación.
  • Actualiza las vistas de texto que muestran los valores modificados.
  • Usa el patrón de observador LiveData para agregar un evento de juego finalizado.
  • Implementa el botón Volver a jugar.

En los codelabs de la lección 5, desarrollarás la app de GuessTheWord, comenzando con el código de inicio. GuessTheWord es un juego de adivinanzas para dos jugadores en el que ambos colaboran para obtener la puntuación más alta posible.

El primer jugador mira las palabras en la app y representa cada una por turnos, asegurándose de no mostrarle la palabra al segundo jugador. El segundo jugador intenta adivinar la palabra.

Para jugar, el primer jugador abre la app en el dispositivo y ve una palabra, por ejemplo, "guitarra", como se muestra en la siguiente captura de pantalla.

El primer jugador representa la palabra, teniendo cuidado de no decirla.

  • Cuando el segundo jugador adivina la palabra correctamente, el primer jugador presiona el botón Entendido, lo que aumenta el recuento en uno y muestra la siguiente palabra.
  • Si el segundo jugador no puede adivinar la palabra, el primer jugador presiona el botón Omitir, lo que disminuye el recuento en uno y pasa a la siguiente palabra.
  • Para finalizar el juego, presiona el botón Finalizar juego. (Esta funcionalidad no se encuentra en el código de partida del primer codelab de la serie).

En este codelab, mejorarás la app de GuessTheWord agregando un evento para finalizar el juego cuando el usuario recorra todas las palabras de la app. También agregarás un botón Volver a jugar en el fragmento de puntuación para que el usuario pueda volver a jugar.

Pantalla de título

Pantalla del juego

Pantalla de puntuación

En esta tarea, ubicarás y ejecutarás el código de partida para este codelab. Puedes usar la app de GuessTheWord que compilaste en el codelab anterior como código de partida o descargar una app de partida.

  1. (Opcional) Si no usas el código del codelab anterior, descarga el código de partida para este codelab. Descomprime el código y abre el proyecto en Android Studio.
  2. Ejecuta la app y juega.
  3. Observa que el botón Omitir muestra la siguiente palabra y disminuye la puntuación en uno, y el botón Entendido muestra la siguiente palabra y aumenta la puntuación en uno. El botón End Game finaliza el juego.

LiveData es una clase de retención de datos observable que tiene en cuenta el ciclo de vida. Por ejemplo, puedes envolver un LiveData alrededor de la puntuación actual en la app de GuessTheWord. En este codelab, aprenderás sobre varias características de LiveData:

  • LiveData es observable, lo que significa que se notifica a un observador cuando cambian los datos retenidos por el objeto LiveData.
  • LiveData contiene datos; LiveData es un wrapper que se puede usar con cualquier dato.
  • LiveData está optimizado para los ciclos de vida, lo que significa que solo actualiza observadores que están en estado de ciclo de vida activo, como STARTED o RESUMED.

En esta tarea, aprenderás a unir cualquier tipo de datos en objetos LiveData convirtiendo los datos de la puntuación actual y la palabra actual en GameViewModel a LiveData. En una tarea posterior, agregarás un observador a estos objetos LiveData y aprenderás a observar LiveData.

Paso 1: Cambia la puntuación y la palabra para usar LiveData

  1. En el paquete screens/game, abre el archivo GameViewModel.
  2. Cambia el tipo de las variables score y word a MutableLiveData.

    MutableLiveData es un LiveData cuyo valor se puede cambiar. MutableLiveData es una clase genérica, por lo que debes especificar el tipo de datos que retiene.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
  1. En GameViewModel, dentro del bloque init, inicializa score y word. Para cambiar el valor de una variable LiveData, usa el método setValue() en la variable. En Kotlin, puedes llamar a setValue() con la propiedad value.
init {

   word.value = ""
   score.value = 0
  ...
}

Paso 2: Actualiza la referencia del objeto LiveData

Las variables score y word ahora son del tipo LiveData. En este paso, cambiarás las referencias a estas variables con la propiedad value.

  1. En GameViewModel, en el método onSkip(), cambia score a score.value. Observa el error sobre la posibilidad de que score sea null. Corregirás este error a continuación.
  2. Para resolver el error, agrega una verificación de null a score.value en onSkip(). Luego, llama a la función minus() en score, que realiza la resta con seguridad de null.
fun onSkip() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.minus(1)
   }
   nextWord()
}
  1. Actualiza el método onCorrect() de la misma manera: agrega una verificación null a la variable score y usa la función plus().
fun onCorrect() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.plus(1)
   }
   nextWord()
}
  1. En GameViewModel, dentro del método nextWord(), cambia la referencia de word a word.value.
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       word.value = wordList.removeAt(0)
   }
}
  1. En GameFragment, dentro del método updateWordText(), cambia la referencia a viewModel.word por viewModel.word.value..
/** Methods for updating the UI **/
private fun updateWordText() {
   binding.wordText.text = viewModel.word.value
}
  1. En GameFragment, dentro del método updateScoreText(), cambia la referencia a viewModel.score por viewModel.score.value..
private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.value.toString()
}
  1. En GameFragment, dentro del método gameFinished(), cambia la referencia a viewModel.score por viewModel.score.value. Agrega la verificación de seguridad null obligatoria.
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   NavHostFragment.findNavController(this).navigate(action)
}
  1. Asegúrate de que no haya errores en tu código. Compila y ejecuta tu app. La funcionalidad de la app debería ser la misma que antes.

Esta tarea está estrechamente relacionada con la anterior, en la que convertiste los datos de puntuación y palabras en objetos LiveData. En esta tarea, adjuntarás objetos Observer a esos objetos LiveData.

  1. En GameFragment, dentro del método onCreateView(), conecta un objeto Observer al objeto LiveData para la puntuación actual, viewModel.score. Usa el método observe() y coloca el código después de la inicialización de viewModel. Usa una expresión lambda para simplificar el código. (Una expresión lambda es una función anónima que no se declara, sino que se pasa inmediatamente como una expresión).
viewModel.score.observe(this, Observer { newScore ->
})

Resuelve la referencia a Observer. Para ello, haz clic en Observer, presiona Alt+Enter (Option+Enter en Mac) y, luego, importa androidx.lifecycle.Observer.

  1. El observador que acabas de crear recibe un evento cuando cambian los datos que contiene el objeto LiveData observado. Dentro del observador, actualiza la puntuación TextView con la nueva puntuación.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Adjunta un objeto Observer al objeto LiveData de la palabra actual. Hazlo de la misma manera en que adjuntaste un objeto Observer a la puntuación actual.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})

Cuando cambia el valor de score o word, el score o word que se muestra en la pantalla ahora se actualiza automáticamente.

  1. En GameFragment, borra los métodos updateWordText() y updateScoreText(), y todas las referencias a ellos. Ya no los necesitas, porque los métodos de observador LiveData actualizan las vistas de texto.
  2. Ejecuta la app. Tu app de juego debería funcionar exactamente como antes, pero ahora usa observadores LiveData y LiveData.

El encapsulamiento es una forma de restringir el acceso directo a algunos campos de un objeto. Cuando encapsulas un objeto, expones un conjunto de métodos públicos que modifican los campos internos privados. Cuando usas encapsulamiento, controlas la manera en que otras clases manipulan estos campos internos.

En tu código actual, cualquier clase externa puede modificar las variables score y word con la propiedad value, por ejemplo, con viewModel.score.value. Es posible que no importe en la app que desarrollas en este codelab, pero en una app de producción, querrás controlar los datos en los objetos ViewModel.

Solo el ViewModel debe editar los datos en tu app, pero los controladores de IU deben leer los datos, por lo que los campos de datos no pueden ser completamente privados. Para encapsular los datos de tu app, usa objetos MutableLiveData y LiveData.

MutableLiveData vs. LiveData:

  • Los datos de un objeto MutableLiveData se pueden cambiar, como lo indica su nombre. Dentro de ViewModel, los datos deben poder editarse, por lo que se usa MutableLiveData.
  • Los datos de un objeto LiveData se pueden leer, pero no cambiar. Desde fuera de ViewModel, los datos deben ser legibles, pero no editables. Por lo tanto, los datos deben exponerse como LiveData.

Para llevar a cabo esta estrategia, usa una propiedad de copia de seguridad de Kotlin. Una propiedad de copia de seguridad te permite mostrar algo de un método get que no sea el objeto exacto. En esta tarea, implementarás una propiedad de respaldo para los objetos score y word en la app de GuessTheWord.

Cómo agregar una propiedad de respaldo a la puntuación y la palabra

  1. En GameViewModel, haz que el objeto score actual sea private.
  2. Para seguir la convención de nombres que se usa en las propiedades de copia de seguridad, cambia score por _score. La propiedad _score ahora es la versión mutable de la puntuación del juego, que se usará de forma interna.
  3. Crea una versión pública del tipo LiveData, llamada score.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. Aparece un error de inicialización. Este error se produce porque, dentro de GameFragment, score es una referencia de LiveData y score ya no puede acceder a su setter. Para obtener más información sobre los métodos get y set en Kotlin, consulta Getters and Setters.

    Para resolver el error, anula el método get() para el objeto score en GameViewModel y devuelve la propiedad de respaldo, _score.
val score: LiveData<Int>
   get() = _score
  1. En GameViewModel, cambia las referencias de score a su versión interna mutable, _score.
init {
   ...
   _score.value = 0
   ...
}

...
fun onSkip() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.minus(1)
   }
  ...
}

fun onCorrect() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.plus(1)
   }
   ...
}
  1. Cambia el nombre del objeto word a _word y agrégale una propiedad de respaldo, como hiciste con el objeto score.
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
   get() = _word
...
init {
   _word.value = ""
   ...
}
...
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       _word.value = wordList.removeAt(0)
   }
}

Bien hecho. Encapsulaste los objetos LiveData, word y score.

Tu app actual navega a la pantalla de puntuación cuando el usuario presiona el botón End Game. También quieres que la app navegue a la pantalla de puntuación cuando los jugadores hayan completado todas las palabras. Después de que los jugadores terminen con la última palabra, quieres que el juego finalice automáticamente para que el usuario no tenga que presionar el botón.

Para implementar esta funcionalidad, necesitas que se active un evento y se comunique al fragmento desde ViewModel cuando se hayan mostrado todas las palabras. Para ello, usarás el patrón de observador LiveData para modelar un evento de juego finalizado.

El patrón de observador

El patrón de observador es un patrón de diseño de software. Especifica la comunicación entre objetos: un observable (el "sujeto" de la observación) y los observadores. Un observable es un objeto que notifica a los observadores sobre los cambios en su estado.

En el caso de LiveData en esta app, el observable (sujeto) es el objeto LiveData, y los observadores son los métodos en los controladores de IU, como los fragmentos. Se produce un cambio de estado cada vez que cambian los datos incluidos en LiveData. Las clases LiveData son fundamentales para la comunicación del ViewModel al fragmento.

Paso 1: Usa LiveData para detectar un evento de finalización del juego

En esta tarea, usarás el patrón de observador LiveData para modelar un evento de finalización del juego.

  1. En GameViewModel, crea un objeto Boolean MutableLiveData llamado _eventGameFinish. Este objeto contendrá el evento de finalización del juego.
  2. Después de inicializar el objeto _eventGameFinish, crea e inicializa una propiedad de respaldo llamada eventGameFinish.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
   get() = _eventGameFinish
  1. En GameViewModel, agrega un método onGameFinish(). En el método, configura el evento de juego finalizado, eventGameFinish, en true.
/** Method for the game completed event **/
fun onGameFinish() {
   _eventGameFinish.value = true
}
  1. En GameViewModel, dentro del método nextWord(), finaliza el juego si la lista de palabras está vacía.
private fun nextWord() {
   if (wordList.isEmpty()) {
       onGameFinish()
   } else {
       //Select and remove a _word from the list
       _word.value = wordList.removeAt(0)
   }
}
  1. En GameFragment, dentro de onCreateView(), después de inicializar viewModel, adjunta un observador a eventGameFinish. Usa el método observe(). Dentro de la función lambda, llama al método gameFinished().
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
   if (hasFinished) gameFinished()
})
  1. Ejecuta la app, juega y prueba todas las palabras. La app navega automáticamente a la pantalla de puntuación, en lugar de permanecer en el fragmento del juego hasta que toques Finalizar juego.

    Después de que la lista de palabras esté vacía, se establece eventGameFinish, se llama al método de observador asociado en el fragmento del juego y la app navega al fragmento de la pantalla.
  2. El código que agregaste introdujo un problema de ciclo de vida. Para comprender el problema, en la clase GameFragment, comenta el código de navegación en el método gameFinished(). Asegúrate de mantener el mensaje Toast en el método.
private fun gameFinished() {
       Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
//        val action = GameFragmentDirections.actionGameToScore()
//        action.score = viewModel.score.value?:0
//        NavHostFragment.findNavController(this).navigate(action)
   }
  1. Ejecuta la app, juega y prueba todas las palabras. En la parte inferior de la pantalla del juego, aparece brevemente un mensaje de aviso que dice "El juego acaba de finalizar", que es el comportamiento esperado.

Ahora, rota el dispositivo o el emulador. El aviso se vuelve a mostrar. Rota el dispositivo algunas veces más y es probable que veas el mensaje emergente cada vez. Esto es un error, ya que el mensaje emergente solo debería mostrarse una vez, cuando finaliza el juego. El mensaje emergente no debe mostrarse cada vez que se vuelve a crear el fragmento. Resolverás este problema en la siguiente tarea.

Paso 2: Restablece el evento de juego finalizado

Por lo general, LiveData solo brinda actualizaciones a los observadores cuando cambian los datos. Una excepción a este comportamiento es que los observadores también reciben actualizaciones cuando cambian de un estado inactivo a un estado activo.

Por eso, la notificación de juego finalizado se activa repetidamente en tu app. Cuando se vuelve a crear el fragmento del juego después de una rotación de pantalla, pasa de un estado inactivo a uno activo. El observador del fragmento se vuelve a conectar al ViewModel existente y recibe los datos actuales. Se vuelve a activar el método gameFinished() y se muestra el mensaje emergente.

En esta tarea, corregirás este problema y mostrarás el mensaje emergente solo una vez. Para ello, restablecerás la marca eventGameFinish en GameViewModel.

  1. En GameViewModel, agrega un método onGameFinishComplete() para restablecer el evento de juego finalizado, _eventGameFinish.
/** Method for the game completed event **/

fun onGameFinishComplete() {
   _eventGameFinish.value = false
}
  1. En GameFragment, al final de gameFinished(), llama a onGameFinishComplete() en el objeto viewModel. (Por el momento, deja comentado el código de navegación en gameFinished()).
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. Ejecuta la app y juega. Repasa todas las palabras y, luego, cambia la orientación de la pantalla del dispositivo. El mensaje emergente se muestra solo una vez.
  2. En GameFragment, dentro del método gameFinished(), quita las marcas de comentario del código de navegación.

    Para quitar las marcas de comentario en Android Studio, selecciona las líneas que tienen marcas de comentario y presiona Control+/ (Command+/ en Mac).
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   findNavController(this).navigate(action)
   viewModel.onGameFinishComplete()
}

Si Android Studio lo solicita, importa androidx.navigation.fragment.NavHostFragment.findNavController.

  1. Ejecuta la app y juega. Asegúrate de que la app navegue automáticamente a la pantalla de puntuación final después de que repases todas las palabras.

¡Bien hecho! Tu app usa LiveData para activar un evento de juego terminado y comunicar desde GameViewModel al fragmento del juego que la lista de palabras está vacía. Luego, el fragmento del juego navega al fragmento de la puntuación.

En esta tarea, cambiarás la puntuación a un objeto LiveData en el ScoreViewModel y le adjuntarás un observador. Esta tarea es similar a la que realizaste cuando agregaste LiveData a GameViewModel.

Realizas estos cambios en ScoreViewModel para que esté completo, de modo que todos los datos de tu app usen LiveData.

  1. En ScoreViewModel, cambia el tipo de variable score a MutableLiveData. Cambia el nombre según la convención a _score y agrega una propiedad de respaldo.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
   get() = _score
  1. En ScoreViewModel, dentro del bloque init, inicializa _score. Puedes quitar o dejar el registro en el bloque init como quieras.
init {
   _score.value = finalScore
}
  1. En ScoreFragment, dentro de onCreateView(), después de inicializar viewModel, adjunta un observador para el objeto de puntuación LiveData. Dentro de la expresión lambda, establece el valor de la puntuación en la vista de texto de la puntuación. Quita el código que asigna directamente la vista de texto con el valor de la puntuación de ViewModel.

Código para agregar:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})

Código que debes quitar:

binding.scoreText.text = viewModel.score.toString()

Cuando Android Studio te lo solicite, importa androidx.lifecycle.Observer.

  1. Ejecuta la app y juega. La app debería funcionar como antes, pero ahora usa LiveData y un observador para actualizar la puntuación.

En esta tarea, agregarás un botón Play Again a la pantalla de puntuación y, luego, implementarás su objeto de escucha de clics con un evento LiveData. El botón activa un evento para navegar de la pantalla de puntuación a la pantalla del juego.

El código de partida de la app incluye el botón Play Again, pero está oculto.

  1. En res/layout/score_fragment.xml, para el botón play_again_button, cambia el valor del atributo visibility a visible.
<Button
   android:id="@+id/play_again_button"
...
   android:visibility="visible"
 />
  1. En ScoreViewModel, agrega un objeto LiveData para contener un Boolean llamado _eventPlayAgain. Este objeto se usa para guardar el evento LiveData y navegar desde la pantalla de puntuación a la pantalla del juego.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
   get() = _eventPlayAgain
  1. En ScoreViewModel, define métodos para establecer y restablecer el evento, _eventPlayAgain.
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. En ScoreFragment, agrega un observador para eventPlayAgain. Coloca el código al final de onCreateView(), antes de la sentencia return. Dentro de la expresión lambda, vuelve a la pantalla del juego y restablece eventPlayAgain.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
   if (playAgain) {
      findNavController().navigate(ScoreFragmentDirections.actionRestart())
       viewModel.onPlayAgainComplete()
   }
})

Cuando Android Studio te lo solicite, importa androidx.navigation.fragment.findNavController.

  1. En ScoreFragment, dentro de onCreateView(), agrega un objeto de escucha de clics al botón PlayAgain y llama a viewModel.onPlayAgain().
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. Ejecuta la app y juega. Cuando finaliza el juego, en la pantalla de puntuación se muestra la puntuación final y el botón Volver a jugar. Presiona el botón PlayAgain y la app navegará a la pantalla del juego para que puedas volver a jugarlo.

¡Buen trabajo! Cambiaste la arquitectura de tu app para usar objetos LiveData en el ViewModel y adjuntaste observadores a los objetos LiveData. LiveData notifica a los objetos observadores cuando cambia el valor que contiene LiveData.

Proyecto de Android Studio: GuessTheWord

LiveData

  • LiveData es una clase de retención de datos observable que tiene en cuenta el ciclo de vida y es uno de los componentes de la arquitectura de Android.
  • Puedes usar LiveData para habilitar la actualización automática de la IU cuando se actualicen los datos.
  • LiveData es observable, lo que significa que se puede notificar a un observador, como una actividad o un fragmento, cuando cambian los datos retenidos por el objeto LiveData.
  • LiveData contiene datos; es un wrapper que se puede usar con cualquier dato.
  • LiveData está optimizado para los ciclos de vida, lo que significa que solo actualiza observadores que están en estado de ciclo de vida activo, como STARTED o RESUMED.

Cómo agregar LiveData

  • Cambia el tipo de las variables de datos en ViewModel a LiveData o MutableLiveData.

MutableLiveData es un objeto LiveData cuyo valor se puede cambiar. MutableLiveData es una clase genérica, por lo que debes especificar el tipo de datos que retiene.

  • Para cambiar el valor de los datos que contiene el LiveData, usa el método setValue() en la variable LiveData.

Para encapsular LiveData

  • El LiveData dentro de ViewModel debe poder editarse. Fuera de ViewModel, el LiveData debe ser legible. Esto se puede implementar con una propiedad de copia de seguridad de Kotlin.
  • Una propiedad de respaldo de Kotlin te permite devolver algo de un getter que no sea el objeto exacto.
  • Para encapsular el LiveData, usa private MutableLiveData dentro del ViewModel y devuelve una propiedad de respaldo de LiveData fuera del ViewModel.

LiveData observable

  • LiveData sigue un patrón de observador. El objeto "observable" es el objeto LiveData, y los observadores son los métodos en los controladores de IU, como los fragmentos. Cada vez que cambian los datos incluidos en LiveData, se notifica a los métodos de observador en los controladores de IU.
  • Para que el LiveData sea observable, adjunta un objeto observador a la referencia de LiveData en los observadores (como actividades y fragmentos) con el método observe().
  • Este patrón de observador LiveData se puede usar para comunicarse desde el ViewModel a los controladores de la IU.

Curso de Udacity:

Documentación para desarrolladores de Android:

Otro:

En esta sección, se enumeran las posibles actividades para el hogar para los alumnos que trabajan en este codelab como parte de un curso dirigido por un instructor. Depende del instructor hacer lo siguiente:

  • Si es necesario, asigna una tarea.
  • Comunicarles a los alumnos cómo enviar las actividades para el hogar.
  • Califica las actividades para el hogar.

Los instructores pueden usar estas sugerencias en la medida que quieran y deben asignar cualquier otra actividad para el hogar que consideren apropiada.

Si estás trabajando en este codelab por tu cuenta, usa estas actividades para el hogar para probar tus conocimientos.

Responde estas preguntas:

Pregunta 1

¿Cómo se encapsula el LiveData almacenado en un ViewModel para que los objetos externos lean datos sin poder actualizarlos?

  • En el objeto ViewModel, cambia el tipo de datos a private LiveData. Usa una propiedad de copia de seguridad para exponer datos de solo lectura de tipo MutableLiveData.
  • En el objeto ViewModel, cambia el tipo de datos a private MutableLiveData. Usa una propiedad de copia de seguridad para exponer datos de solo lectura de tipo LiveData.
  • Dentro del controlador de IU, cambia el tipo de datos a private MutableLiveData. Usa una propiedad de respaldo para exponer datos de solo lectura de tipo LiveData.
  • En el objeto ViewModel, cambia el tipo de datos a LiveData. Usa una propiedad de respaldo para exponer datos de solo lectura de tipo LiveData.

Pregunta 2

¿En cuál de los siguientes estados debe encontrarse el controlador de IU (como un fragmento) para que LiveData lo actualice?

  • Reanudado
  • En segundo plano
  • En pausa
  • Detenida

Pregunta 3

En el patrón del observador de LiveData, ¿cuál es el elemento observable (lo que se observa)?

  • El método de observador
  • Los datos en un objeto LiveData
  • El controlador de IU
  • El objeto ViewModel

Comienza la siguiente lección: 5.3: Vinculación de datos con ViewModel y LiveData

Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de los codelabs de Conceptos básicos de Kotlin para Android.