Descripción general de LiveData Parte de Android Jetpack

LiveData es una clase de retención de datos observable. A diferencia de una clase observable regular, LiveData está optimizada para ciclos de vida, lo que significa que respeta el ciclo de vida de otros componentes de las apps, como actividades, fragmentos o servicios. Esta optimización garantiza que LiveData solo actualice observadores de componentes de apps que tienen un estado de ciclo de vida activo.

LiveData considera que un observador, representado por la clase Observer, está en un estado activo si su ciclo de vida tiene el estado STARTED o RESUMED. LiveData solo notifica a los observadores activos sobre las actualizaciones. A los observadores inactivos registrados para observar objetos LiveData no se les notifica sobre los cambios.

Puedes registrar un observador sincronizado con un objeto que implementa la interfaz de LifecycleOwner. Esta relación permite quitar el observador cuando el estado del objeto Lifecycle correspondiente cambia a DESTROYED, lo que es particularmente útil para las actividades y los fragmentos porque pueden observar objetos LiveData de manera segura sin preocuparse por las pérdidas (se anula de inmediato la suscripción a las actividades y los fragmentos cuando se destruyen sus ciclos de vida).

Para obtener más información sobre cómo usar LiveData, consulta Cómo trabajar con objetos LiveData.

Ventajas de usar LiveData

El uso de LiveData brinda las siguientes ventajas:

Garantiza que la IU coincida con el estado de los datos
LiveData sigue el patrón del observador. LiveData notifica a los objetos Observer cuando el estado del ciclo de vida cambia. Puedes consolidar tu código para actualizar la IU en estos objetos Observer. En lugar de actualizar la IU cada vez que cambian los datos de la app, tu observador puede hacerlo cada vez que se produzca un cambio.
Ausencia de pérdidas de memoria
Los observadores están vinculados a objetos Lifecycle y borran lo que crean cuando se destruye su ciclo de vida asociado.
Actividades detenidas para evitar las fallas
Si el ciclo de vida del observador está inactivo, como en el caso de una actividad de la pila de actividades, no recibe ningún evento de LiveData.
No más manejo manual del ciclo de vida
Los componentes de IU solo observan los datos relevantes y no detienen ni reanudan la observación. LiveData se ocupa automáticamente de todo esto, ya que está al tanto de los cambios de estado del ciclo de vida relevantes mientras lleva a cabo la observación.
Datos siempre actualizados
Si un ciclo de vida queda inactivo, recibe los datos más recientes una vez que vuelve a activarse. Por ejemplo, una actividad que estuvo en segundo plano recibe los datos más recientes inmediatamente después de volver a ejecutarse en primer plano.
Cambios de configuración apropiados
Una actividad o un fragmento que se vuelve a crear debido a un cambio de configuración, como la rotación del dispositivo, recibe de inmediato los datos disponibles más recientes.
Compartir recursos
Puedes extender un objeto LiveData con el patrón singleton para agrupar los servicios del sistema y que se puedan compartir en tu app. El objeto LiveData se conecta al servicio del sistema una vez; luego, cualquier observador que necesite el recurso solo tendrá que mirar el objeto LiveData. Para obtener más información, consulta Cómo extender LiveData.

Cómo trabajar con objetos LiveData

Sigue estos pasos para trabajar con objetos LiveData:

  1. Crea una instancia de LiveData para retener un tipo de datos determinado. Por lo general, este proceso se lleva a cabo dentro de tu clase ViewModel.
  2. Crea un objeto Observer que defina el método onChanged(), que controla lo que sucede cuando cambian los datos retenidos del objeto LiveData. Por lo general, se crea el objeto Observer en un controlador de IU, como una actividad o un fragmento.
  3. Adjunta el objeto Observer al objeto LiveData mediante el método observe(). El método observe() toma un objeto LifecycleOwner. Esta acción suscribe el objeto Observer al objeto LiveData para que reciba notificaciones de los cambios. Por lo general, se conecta el objeto Observer en un controlador de IU, como una actividad o un fragmento.

Cuando actualizas el valor almacenado en el objeto LiveData, se activan todos los observadores registrados siempre que el LifecycleOwner esté en el estado activo.

LiveData permite que los observadores del controlador de IU se suscriban a actualizaciones. Cuando los datos retenidos por el objeto LiveData cambian, la IU se actualiza automáticamente como respuesta.

Cómo crear objetos LiveData

LiveData es un wrapper que se puede usar con cualquier dato, incluidos los objetos que implementan Collections, como List. Por lo general, un objeto LiveData se almacena dentro de un objeto ViewModel, y se accede a él través de un método captador, como lo demuestra el siguiente ejemplo:

Kotlin

    class NameViewModel : ViewModel() {

        // Create a LiveData with a String
        val currentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }

        // Rest of the ViewModel...
    }
    

Java

    public class NameViewModel extends ViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> currentName;

        public MutableLiveData<String> getCurrentName() {
            if (currentName == null) {
                currentName = new MutableLiveData<String>();
            }
            return currentName;
        }

    // Rest of the ViewModel...
    }
    

En un principio, los datos en un objeto LiveData no están establecidos.

Puedes obtener más información sobre las ventajas y el uso de la clase ViewModel en la guía de ViewModel.

Cómo observar objetos LiveData

En la mayoría de los casos, el método onCreate() del componente de una app es el lugar adecuado para comenzar a observar un objeto LiveData debido a los siguientes motivos:

  • Para garantizar que el sistema no realice llamadas redundantes desde el método onResume() de un fragmento o una actividad.
  • Para garantizar que la actividad o el fragmento tenga datos que pueda mostrar tan pronto como quede activo. Cuando el componente de una app está en el estado STARTED, recibe inmediatamente el valor más reciente a partir de los objetos LiveData que observa. Esto solo sucede si se configuró el objeto LiveData que se observará.

En general, LiveData solo brinda actualizaciones cuando los datos cambian, y solo a observadores activos. Una excepción a este comportamiento es que los observadores también reciben una actualización cuando cambian de un estado activo a un estado inactivo. Además, si el observador cambia de inactivo a activo por segunda vez, solo recibe una actualización si el valor cambió desde la última vez que estuvo activo.

En el siguiente código de ejemplo, se muestra cómo comenzar a observar un objeto LiveData:

Kotlin

    class NameActivity : AppCompatActivity() {

        private lateinit var model: NameViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            // Other code to setup the activity...

            // Get the ViewModel.
            model = ViewModelProviders.of(this).get(NameViewModel::class.java)

            // Create the observer which updates the UI.
            val nameObserver = Observer<String> { newName ->
                // Update the UI, in this case, a TextView.
                nameTextView.text = newName
            }

            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            model.currentName.observe(this, nameObserver)
        }
    }
    

Java

    public class NameActivity extends AppCompatActivity {

        private NameViewModel model;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Other code to setup the activity...

            // Get the ViewModel.
            model = ViewModelProviders.of(this).get(NameViewModel.class);

            // Create the observer which updates the UI.
            final Observer<String> nameObserver = new Observer<String>() {
                @Override
                public void onChanged(@Nullable final String newName) {
                    // Update the UI, in this case, a TextView.
                    nameTextView.setText(newName);
                }
            };

            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            model.getCurrentName().observe(this, nameObserver);
        }
    }
    

Después de llamar a observe() con nameObserver como parámetro, se invoca inmediatamente a onChanged() y se proporciona el valor almacenado en mCurrentName más reciente. Si el objeto LiveData no tiene un valor definido en mCurrentName, no se llama a onChanged().

Cómo actualizar objetos LiveData

LiveData no tiene métodos disponibles públicamente para actualizar los datos almacenados. La clase MutableLiveData expone los métodos setValue(T) y postValue(T) en público y debes usarlos si necesitas editar el valor almacenado en un objeto LiveData. Por lo general, MutableLiveData se usa en el ViewModel y, luego, el ViewModel solo expone objetos LiveData inmutables a los observadores.

Después de configurar la relación del observador, puedes actualizar el valor del objeto LiveData, como se indica en el siguiente ejemplo, lo que activa a todos los observadores cuando el usuario presiona un botón:

Kotlin

    button.setOnClickListener {
        val anotherName = "John Doe"
        model.currentName.setValue(anotherName)
    }
    

Java

    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            String anotherName = "John Doe";
            model.getCurrentName().setValue(anotherName);
        }
    });
    

En el ejemplo, llamar a setValue(T) provoca que los observadores llamen a sus métodos onChanged() con el valor John Doe. En el ejemplo, se muestra que se presiona un botón, pero también se podría llamar a setValue() o postValue() para actualizar mName por varios motivos, por ejemplo, como respuesta a una solicitud de red o la carga de una base de datos que se completa. En cualquier caso, la llamada a setValue() o postValue() activa los observadores y actualiza la IU.

Cómo usar LiveData con Room

La biblioteca de persistencias Room admite consultas observables, y estas muestran objetos LiveData. Las consultas observables se escriben como parte del objeto de acceso a la base de datos (DAO).

Room genera todo el código necesario para actualizar el objeto LiveData cuando se actualiza una base de datos. El código generado ejecuta la consulta de manera asíncrona en un subproceso en segundo plano cuando es necesario. Este patrón es útil para mostrar los datos en una IU sincronizada con los datos almacenados en una base de datos. Puedes obtener más información sobre Room y DAO en la guía sobre la biblioteca de persistencias Room.

Cómo usar corrutinas con LiveData

LiveData incluye compatibilidad con corrutinas de Kotlin. Para obtener más información, consulta Cómo usar corrutinas de Kotlin con componentes de la arquitectura de Android.

Cómo extender LiveData

LiveData considera que un observador está activo si su ciclo de vida tiene el estado STARTED o RESUMED. En el siguiente código de ejemplo, se muestra cómo extender la clase LiveData:

Kotlin

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager = StockManager(symbol)

        private val listener = { price: BigDecimal ->
            value = price
        }

        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }

        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    }
    

Java

    public class StockLiveData extends LiveData<BigDecimal> {
        private StockManager stockManager;

        private SimplePriceListener listener = new SimplePriceListener() {
            @Override
            public void onPriceChanged(BigDecimal price) {
                setValue(price);
            }
        };

        public StockLiveData(String symbol) {
            stockManager = new StockManager(symbol);
        }

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
    

La implementación del objeto de escucha de precio de este ejemplo incluye los siguientes métodos importantes:

  • El método onActive() se invoca cuando el objeto LiveData tiene un observador activo. Esto quiere decir que debes comenzar a observar las actualizaciones de cotización de las acciones a partir de este método.
  • El método onInactive() se invoca cuando el objeto LiveData no tiene ningún observador activo. Como ningún observador está escuchando, no es necesario permanecer conectado al servicio StockManager.
  • El método setValue(T) actualiza el valor de la instancia LiveData y notifica a los observadores activos sobre el cambio.

Puedes usar la clase StockLiveData de la siguiente manera:

Kotlin

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(this, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
    

Java

    public class MyFragment extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            LiveData<BigDecimal> myPriceListener = ...;
            myPriceListener.observe(this, price -> {
                // Update the UI.
            });
        }
    }
    

El método observe() transfiere el fragmento, que es una instancia de LifecycleOwner, como primer argumento. Eso implica que este observador está vinculado al objeto Lifecycle asociado con el propietario, es decir:

  • Si el objeto Lifecycle no está en estado activo, no se invoca al observador aunque el valor cambie.
  • Una vez que se destruye el objeto Lifecycle, se quita automáticamente el observador.

El hecho de que los objetos LiveData estén optimizados para ciclos de vida quiere decir que puedes compartirlos entre varios fragmentos, actividades y servicios. Para no complicar el ejemplo, puedes implementar la clase LiveData como un singleton de la siguiente manera:

Kotlin

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager: StockManager = StockManager(symbol)

        private val listener = { price: BigDecimal ->
            value = price
        }

        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }

        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }

        companion object {
            private lateinit var sInstance: StockLiveData

            @MainThread
            fun get(symbol: String): StockLiveData {
                sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
                return sInstance
            }
        }
    }
    

Java

    public class StockLiveData extends LiveData<BigDecimal> {
        private static StockLiveData sInstance;
        private StockManager stockManager;

        private SimplePriceListener listener = new SimplePriceListener() {
            @Override
            public void onPriceChanged(BigDecimal price) {
                setValue(price);
            }
        };

        @MainThread
        public static StockLiveData get(String symbol) {
            if (sInstance == null) {
                sInstance = new StockLiveData(symbol);
            }
            return sInstance;
        }

        private StockLiveData(String symbol) {
            stockManager = new StockManager(symbol);
        }

        @Override
        protected void onActive() {
            stockManager.requestPriceUpdates(listener);
        }

        @Override
        protected void onInactive() {
            stockManager.removeUpdates(listener);
        }
    }
    

Además, puedes usarlo en el fragmento de la siguiente manera:

Kotlin

    class MyFragment : Fragment() {

        override fun onActivityCreated(savedInstanceState: Bundle?) {
            StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })

        }
    

Java

    public class MyFragment extends Fragment {
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            StockLiveData.get(symbol).observe(this, price -> {
                // Update the UI.
            });
        }
    }
    

Varios fragmentos y actividades pueden observar la instancia MyPriceListener. LiveData solo se conecta al servicio del sistema si uno o más de ellos están visibles y activos.

Cómo transformar LiveData

Quizás desees cambiar el valor almacenado en un objeto LiveData antes de despacharlo a los observadores, o bien es posible que debas mostrar una instancia de LiveData diferente en función del valor de otra instancia. El paquete Lifecycle incluye la clase Transformations, que cuenta con métodos de ayuda compatibles con estas situaciones.

Transformations.map()
Aplica una función al valor almacenado en el objeto LiveData y propaga el resultado de manera descendente.

Kotlin

    val userLiveData: LiveData<User> = UserLiveData()
    val userName: LiveData<String> = Transformations.map(userLiveData) {
        user -> "${user.name} ${user.lastName}"
    }
    

Java

    LiveData<User> userLiveData = ...;
    LiveData<String> userName = Transformations.map(userLiveData, user -> {
        user.name + " " + user.lastName
    });
    
Transformations.switchMap()
De manera similar a map(), aplica una función al valor almacenado en el objeto LiveData y desenvuelve y despacha el resultado de manera descendente. La función que pasó a switchMap() debe devolver un objeto LiveData, como se muestra en el siguiente ejemplo:

Kotlin

    private fun getUser(id: String): LiveData<User> {
      ...
    }
    val userId: LiveData<String> = ...
    val user = Transformations.switchMap(userId) { id -> getUser(id) }
    

Java

    private LiveData<User> getUser(String id) {
      ...;
    }

    LiveData<String> userId = ...;
    LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
    

Puedes usar métodos de transformación para transportar información durante el ciclo de vida del observador. Las transformaciones no se calculan a menos que un observador esté mirando el objeto LiveData que se muestra. Debido a que las transformaciones se calculan lentamente, el comportamiento relacionado con el ciclo de vida se traslada de manera implícita, sin requerir llamadas o dependencias explícitas adicionales.

Si crees que necesitas un objeto Lifecycle dentro de un objeto ViewModel, es posible que una transformación sea la mejor solución. Por ejemplo, imagina que tienes un componente de IU que acepta una dirección y muestra el código postal de esa dirección. Puedes implementar el ViewModel simple para este componente como se indica en el siguiente código de ejemplo:

Kotlin

    class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

        private fun getPostalCode(address: String): LiveData<String> {
            // DON'T DO THIS
            return repository.getPostCode(address)
        }
    }
    

Java

    class MyViewModel extends ViewModel {
        private final PostalCodeRepository repository;
        public MyViewModel(PostalCodeRepository repository) {
           this.repository = repository;
        }

        private LiveData<String> getPostalCode(String address) {
           // DON'T DO THIS
           return repository.getPostCode(address);
        }
    }
    

Luego, el componente de IU debe cancelar el registro del objeto LiveData anterior y registrarse en la instancia nueva cada vez que llama a getPostalCode(). Además, si se recrea el componente de IU, activa otra llamada al método repository.getPostCode(), en lugar de usar el método de la llamada anterior.

En cambio, puedes implementar la búsqueda del código postal como una transformación de la entrada de dirección, lo que se muestra en el siguiente ejemplo:

Kotlin

    class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
        private val addressInput = MutableLiveData<String>()
        val postalCode: LiveData<String> = Transformations.switchMap(addressInput) {
                address -> repository.getPostCode(address) }

        private fun setInput(address: String) {
            addressInput.value = address
        }
    }
    

Java

    class MyViewModel extends ViewModel {
        private final PostalCodeRepository repository;
        private final MutableLiveData<String> addressInput = new MutableLiveData();
        public final LiveData<String> postalCode =
                Transformations.switchMap(addressInput, (address) -> {
                    return repository.getPostCode(address);
                 });

      public MyViewModel(PostalCodeRepository repository) {
          this.repository = repository
      }

      private void setInput(String address) {
          addressInput.setValue(address);
      }
    }
    

En este caso, el campo postalCode se define como una transformación de addressInput. Siempre que tu app tenga un observador activo asociado al campo postalCode, se recalculará y recuperará el valor del campo cada vez que cambie addressInput.

Este mecanismo permite que las versiones anteriores de la app creen objetos LiveData que se calculan de manera diferida y a pedido. Un objeto ViewModel puede obtener fácilmente referencias a objetos LiveData y, luego, definir reglas de transformación sobre ellos.

Cómo crear transformaciones nuevas

Hay decenas de transformaciones específicas que pueden ser útiles para tu app, pero no se proporcionan de manera predeterminada. Para implementar tu propia transformación, puedes usar la clase MediatorLiveData, que escucha a otros objetos LiveData y procesa los eventos que ellos emiten. MediatorLiveData propaga correctamente su estado al objeto LiveData fuente. Para obtener más información sobre este patrón, consulta la documentación de referencia sobre la clase Transformations.

Cómo fusionar varias fuentes de LiveData

MediatorLiveData es una subclase de LiveData que te permite fusionar varias fuentes de LiveData. Luego, los observadores de objetos MediatorLiveData se activan cada vez cambia que uno de los objetos fuente de LiveData originales.

Por ejemplo, si en tu IU tienes un objeto LiveData que se puede actualizar a partir de una base de datos o una red local, puedes agregar las siguientes fuentes al objeto MediatorLiveData:

  • Un objeto LiveData asociado a los datos almacenados en la base de datos
  • Un objeto LiveData asociado a los datos a los que se accedió desde la red

Tu actividad solo necesita observar el objeto MediatorLiveData para recibir actualizaciones de ambas fuentes. Si deseas ver un ejemplo detallado, consulta la sección Apéndice: Cómo exponer el estado de la red de la Guía de arquitectura de apps.

Recursos adicionales

Para obtener más información sobre la clase LiveData, consulta los siguientes recursos.

Ejemplos

Codelabs

Blogs

Videos