Aspectos básicos de Android Kotlin 05.2: LiveData y observadores 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 en secuencia. Todos los codelabs del curso se detallan en la página de destino de codelabs sobre los aspectos básicos de Kotlin para Android.

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 la arquitectura de Android, te permite compilar objetos de datos que notifican las vistas cuando cambia la base de datos subyacente.

Para usar la clase LiveData, configuras"observadores" (por ejemplo, actividades o fragmentos) que observan cambios en los datos de la app. LiveData está optimizado para los ciclos de vida, por lo que solo actualiza observadores de componentes de apps que tienen 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 actividades y fragmentos
  • Cómo usar objetos ViewModel en tu app
  • Cómo crear objetos ViewModel mediante la interfaz ViewModelProvider.Factory

Qué aprenderás

  • ¿Por qué son útiles los objetos LiveData?
  • Cómo agregar LiveData a los datos almacenados en una ViewModel
  • Cuándo y cómo usar MutableLiveData
  • Cómo agregar métodos de observador para observar cambios en LiveData.
  • Cómo encapsular LiveData mediante una propiedad de copia de seguridad
  • Cómo establecer una comunicación 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 vean cuando cambia 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 finalizado en el juego.
  • Implementa el botón para volver a jugar.

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

El primer jugador mira las palabras en la app y actúa a su vez de manera individual, 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, como "guitarra", como se muestra en la siguiente captura de pantalla.

El primer jugador representa la palabra y tiene cuidado de no decirla realmente.

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

En este codelab, mejorarás la app de GuessTheWord cuando agregues un evento para finalizar el juego cuando el usuario recorra todas las palabras de la app. También agregarás el botón Jugar de nuevo 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 tu código de inicio para este codelab. Puedes usar la app de GuessTheWord que creaste en el codelab anterior como código de inicio, o bien descargar una app de inicio.

  1. Si no usas el código del codelab anterior, descarga el código de inicio para este codelab (opcional). Descomprime el código y abre el proyecto en Android Studio.
  2. Ejecuta la app y comienza a jugar.
  3. Observa que el botón Omitir muestra la palabra siguiente y disminuye la puntuación en uno, y el botón Entendido muestra la siguiente palabra y aumenta la puntuación en una. 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 unir 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 a objetos LiveData convirtiendo los datos de puntuación y palabras actuales 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 contiene.
// 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 score, posiblemente con null. A continuación, corrige este error.
  2. Para resolver el error, agrega una marca de verificación null a score.value en onSkip(). Luego, llama a la función minus() en score, que realiza la resta con seguridad 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 marca 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 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 viewModel.score a 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 de null requerida.
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úrese de que no haya errores en su código. Compila y ejecuta tu app. Su funcionalidad debería ser la misma que antes.

Esta tarea se relaciona estrechamente con la tarea 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(), adjunta 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, pero 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 una Mac) y, luego, importa androidx.lifecycle.Observer.

  1. El observador que acabas de crear recibe un evento cuando cambian los datos retenidos por el objeto LiveData que se observa. Dentro del observador, actualiza la puntuación TextView con la puntuación nueva.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Adjunta un objeto Observer a la palabra actual LiveData. Hazlo de la misma manera 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 sus referencias. Ya no los necesitas, ya que los métodos del observador de LiveData actualizan las vistas de texto.
  2. Ejecuta la app. La app 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. Al usar 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 sea importante en la app que estás desarrollando en este codelab, pero en una app de producción, quieres controlar los datos en los objetos ViewModel.

Solo ViewModel debe editar los datos de tu app. Sin embargo, 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 en comparación con LiveData:

  • Los datos de un objeto MutableLiveData se pueden cambiar, como su nombre lo indica. Dentro de ViewModel, los datos deben poder editarse, por lo que usa MutableLiveData.
  • Los datos de un objeto LiveData se pueden leer, pero no se pueden 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, debes usar 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 copia de seguridad para los objetos score y word en la app de GuessTheWord.

Agrega una propiedad de copia de seguridad a la puntuación y palabra

  1. En GameViewModel, crea el objeto score actual private.
  2. Para seguir la convención de nombres usada en las propiedades de copia de seguridad, cambia score a _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. Ves un error de inicialización. Este error se produce porque dentro de GameFragment, score es una referencia LiveData y score ya no puede acceder a su método set. Para obtener más información sobre los métodos get y métodos set en Kotlin, consulta Métodos get y set.

    Para resolver el error, anula el método get() del objeto score en GameViewModel y muestra la propiedad de copia de seguridad, _score.
val score: LiveData<Int>
   get() = _score
  1. En GameViewModel, cambia las referencias de score a su versión mutable interna, _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 por _word y agrégale una propiedad de copia de seguridad, como lo 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)
   }
}

Buen trabajo. 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 pasen por todas las palabras. Cuando los jugadores terminen con la última palabra, el juego debe finalizar 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 la ViewModel cuando se hayan mostrado todas las palabras. Para hacerlo, usa el patrón del observador LiveData a fin de modelar un evento 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 "asunto" 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 fragmentos. Se produce un cambio de estado cada vez que cambian los datos unidos dentro de LiveData. Las clases LiveData son fundamentales para la comunicación desde ViewModel al fragmento.

Paso 1: Usa LiveData para detectar un evento finalizado

En esta tarea, usarás el patrón del observador LiveData a fin de modelar un evento finalizado.

  1. En GameViewModel, crea un objeto Boolean MutableLiveData llamado _eventGameFinish. Este objeto conservará el evento finalizado.
  2. Después de inicializar el objeto _eventGameFinish, crea e inicializa una propiedad de copia de seguridad 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, establece el evento finalizado el juego, 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 tu app, juega y explora todas las palabras. La app navega a la pantalla de puntuación automáticamente, en lugar de permanecer en el fragmento del juego hasta que presiones End Game.

    Una vez que la lista de palabras esté vacía, se establecerá eventGameFinish, se llamará al método del observador asociado en el fragmento del juego y la app navegará al fragmento de la pantalla.
  2. El código que agregaste introdujo un problema del ciclo de vida. Para comprender el problema, comenta el código de navegación en el método gameFinished() de la clase GameFragment. 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 tu app, juega y explora todas las palabras. En la parte inferior de la pantalla del juego, aparece un mensaje de aviso que dice "El juego acaba de finalizar" este es el comportamiento esperado.

Ahora, rota el dispositivo o el emulador. El aviso se vuelve a mostrar. Rota el dispositivo un par de veces más, y es probable que se muestre el aviso cada vez. Este error se debe a que el aviso solo se debe mostrar una vez, cuando se termine el juego. El aviso no se debe mostrar cada vez que se vuelva a crear el fragmento. Resolverás este problema en la siguiente tarea.

Paso 2: Restablece el evento finalizado

Por lo general, LiveData entrega actualizaciones a los observadores solo 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 activo.

Es por eso que el aviso finalizado del juego se activa repetidamente en la app. Cuando se vuelve a crear el fragmento del juego después de una rotación de pantalla, este pasa de inactivo a activo. El observador en el fragmento se vuelve a conectar al ViewModel existente y recibe los datos actuales. Se volverá a activar el método gameFinished(), y se mostrará el aviso.

En esta tarea, corregirás el problema y se mostrará el aviso solo una vez. Para ello, restablece la marca eventGameFinish en GameViewModel.

  1. En GameViewModel, agrega un método onGameFinishComplete() para restablecer el evento finalizado del juego, _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 ahora, deja el código de navegación en gameFinished()).
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. Ejecuta la app y comienza a jugar. Revisa todas las palabras y luego cambia la orientación de la pantalla del dispositivo. El aviso solo se muestra una vez.
  2. En GameFragment, dentro del método gameFinished(), quita los comentarios del código de navegación.

    Para quitar los comentarios de Android Studio, selecciona las líneas que están comentadas y presiona Control+/ (Command+/ en una 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 comienza a jugar. Asegúrate de que la app navegue automáticamente a la pantalla de puntuación final después de leer todas las palabras.

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

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

Realiza estos cambios en ScoreViewModel para garantizar la integridad, 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 por convención a _score y agrega una propiedad de copia de seguridad.
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 LiveData de la puntuación. Dentro de la expresión lambda, configura el valor de puntuación como la vista de texto de puntuación. Quita el código que asigna directamente la vista de texto con el valor de 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 para 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 el 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 desde la pantalla de puntuación hasta la pantalla del juego.

El código de inicio de la app incluye el botón Play Again, pero el botón 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 conservar un Boolean llamado _eventPlayAgain. Este objeto se usa para guardar el evento LiveData para navegar de 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 los métodos para configurar y restablecer el evento, _eventPlayAgain.
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. En ScoreFragment, agrega un observador de 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 termine el juego, la pantalla de puntuación mostrará la puntuación final y el botón Jugar de nuevo. Presiona el botón PlayAgain y la app navegará a la pantalla del juego para que puedas volver a jugar.

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

Proyecto de Android Studio: GuessTheWord

LiveData

  • LiveData es una clase de retención de datos observable que está optimizada para los ciclos de vida, uno de los componentes de la arquitectura de Android.
  • Puedes usar LiveData para habilitar tu IU para que se actualice automáticamente 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 de 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 contiene.

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

Cómo encapsular LiveData

  • Se debe poder editar el LiveData dentro de ViewModel. Fuera de ViewModel, el elemento LiveData debe ser legible. Esto se puede implementar con una propiedad de copia de seguridad de Kotlin.
  • Una propiedad de copia de seguridad de Kotlin te permite mostrar algo de un método get que no sea el objeto exacto.
  • Para encapsular el LiveData, usa private MutableLiveData dentro de ViewModel y muestra una propiedad de copia de seguridad LiveData fuera de ViewModel.

LiveData observable

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

Curso de Udacity:

Documentación para desarrolladores de Android:

Otro:

En esta sección, se enumeran las posibles tareas para los alumnos que trabajan con este codelab como parte de un curso que dicta un instructor. Depende del instructor hacer lo siguiente:

  • Si es necesario, asigna la tarea.
  • Informa a los alumnos cómo enviar los deberes.
  • Califica las tareas.

Los instructores pueden usar estas sugerencias lo poco o lo que quieran, y deben asignar cualquier otra tarea que consideren apropiada.

Si estás trabajando en este codelab por tu cuenta, usa estas tareas para poner a prueba tus conocimientos.

Responde estas preguntas

Pregunta 1

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

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

Pregunta 2

¿En cuál de los siguientes estados LiveData se actualiza un controlador de IU (como un fragmento)?

  • Reanudado
  • En segundo plano
  • Detenido
  • Detenido

Pregunta 3

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

  • El método de observador
  • Los datos de 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 ver vínculos a otros codelabs de este curso, consulta la página de destino de codelabs sobre aspectos básicos de Kotlin para Android.