Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Descripción general de LiveData Parte de Android Jetpack

LiveData es una clase de contenedor 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, que está representado por la clase Observer, está en estado activo si su ciclo de vida está en STARTED o RESUMED. LiveData solo notifica a los observadores activos sobre las actualizaciones. Los observadores inactivos registrados para ver objetos LiveData no reciben notificaciones sobre los cambios.

Puedes registrar un observador vinculado con un objeto que implemente la interfaz LifecycleOwner. Esta relación permite quitar al observador cuando el estado del objeto Lifecycle correspondiente cambia a DESTROYED. Esto es especialmente útil para actividades y fragmentos, ya que pueden observar objetos LiveData de forma segura y no preocuparse por las filtraciones: las actividades y los fragmentos se anulan instantáneamente 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 cambia el estado del ciclo de vida. Puede 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 el 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 unir los servicios del sistema de modo que puedan compartirse en la app. El objeto LiveData se conecta al servicio del sistema una vez y, luego, cualquier observador que necesite el recurso puede simplemente 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 contener un tipo de datos determinado. Por lo general, esto se hace dentro de la clase ViewModel.
  2. Crea un objeto Observer que defina el método onChanged(), el cual controla lo que sucede cuando cambian los datos retenidos del objeto LiveData. Por lo general, creas un objeto Observer en un controlador de IU, como una actividad o un fragmento.
  3. Conecta el objeto Observer al objeto LiveData con el método observe(). El método observe() toma un objeto LifecycleOwner. Esto suscribe el objeto Observer al objeto LiveData para que se le notifiquen los cambios. Normalmente, adjuntas el objeto Observer en un controlador de IU, como una actividad o un fragmento.

Cuando actualizas el valor almacenado en el objeto LiveData, activa todos los observadores registrados, siempre que el LifecycleOwner conectado 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 en 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 puede acceder a este a través de un método get, como se muestra en 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...
    }
    

Inicialmente, los datos de un objeto LiveData no están configurados.

Puedes obtener más información sobre los beneficios 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() de un componente de la aplicación es el lugar adecuado para comenzar a observar un objeto LiveData, por los siguientes motivos:

  • Para garantizar que el sistema no realice llamadas redundantes desde el método onResume() de una actividad o un fragmento.
  • Para garantizar que la actividad o el fragmento tenga datos que pueda mostrar tan pronto como quede activo. Tan pronto como un componente de la aplicación se encuentra en el estado STARTED, recibe el valor más reciente de los objetos LiveData que está observando. Esto solo ocurre si se configuró el objeto LiveData a 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() {

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        private val model: NameViewModel by viewModels()

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

            // Other code to setup the activity...

            // 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 = new ViewModelProvider(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() y pasar nameObserver como parámetro, onChanged() se invoca de inmediato y proporciona el valor más reciente almacenado en mCurrentName. Si el objeto LiveData no estableció un valor 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) de forma pública; debes usarlos si necesitas editar el valor almacenado en un objeto LiveData. Por lo general, MutableLiveData se usa en ViewModel y, luego, 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 muestra en el siguiente ejemplo, que activa 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);
        }
    });
    

Llamar a setValue(T) en el ejemplo da como resultado que los observadores llamen a sus métodos onChanged() con el valor John Doe. En el ejemplo, se presiona un botón, pero se puede llamar a setValue() o postValue() para actualizar mName por varias razones; incluso en respuesta a una solicitud de red o para completar la carga de una base de datos. En todos los casos, la llamada a setValue() o postValue() activa observadores y actualiza la IU.

Cómo usar LiveData con Room

La biblioteca de persistencias Room admite consultas observables, que 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 los 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á en estado activo si el ciclo de vida del observador está en los estados 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 llama 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 llama cuando el objeto LiveData no tiene ningún observador activo. Dado que ningún observador tiene implementado un objeto de escucha, no hay razón para mantenerse 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

    public class MyFragment : Fragment() {
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val myPriceListener: LiveData<BigDecimal> = ...
            myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })
        }
    }
    

Java

    public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            LiveData<BigDecimal> myPriceListener = ...;
            myPriceListener.observe(getViewLifeycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
    

El método observe() pasa el LifecycleOwner asociado con la vista del fragmento como primer argumento. Hacer esto indica que este observador está vinculado al objeto Lifecycle asociado con el propietario, lo que significa que:

  • Si el objeto Lifecycle no está en estado activo, no se llama al observador, aunque cambie el valor.
  • Después de que se destruye el objeto Lifecycle, el observador se quita automáticamente.

El hecho de que los objetos LiveData tengan en cuenta el ciclo de vida significa que puedes compartirlos entre varias actividades, fragmentos y servicios. Para que el ejemplo sea simple, 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 onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })

        }
    

Java

    public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
                // Update the UI.
            });
        }
    }
    

Múltiples 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

Es posible que desees realizar cambios en el valor almacenado en un objeto LiveData antes de despacharlo los observadores, o tal vez debas devolver una instancia LiveData diferente según el valor de otro. El paquete Lifecycle proporciona la clase Transformations, que incluye métodos de ayuda que admiten 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 separa y despacha el resultado en sentido descendente. La función que se pasa a switchMap() debe mostrar un objeto LiveData, como se indica 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é viendo el objeto LiveData que se mostró. 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, una transformación probablemente sea una 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 ingenuo para este componente como se muestra 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 anular el registro del objeto LiveData anterior y registrarse en la nueva instancia cada vez que llama a getPostalCode(). Además, si se vuelve a crear el componente de IU, se activa otra llamada al método repository.getPostCode() en lugar de usar el resultado 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 y cuando la aplicación tenga un observador activo asociado con el campo postalCode, el valor del campo se recalcula y se recupera cuando cambia addressInput.

Este mecanismo permite que los niveles inferiores de la app creen objetos LiveData que se calculan de forma diferida 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 implementa objetos de escucha para otros objetos LiveData y procesa los eventos emitidos por estos. MediatorLiveData propaga correctamente su estado al objeto LiveData de origen. Para obtener más información sobre este patrón, consulta la documentación de referencia de la clase Transformations.

Cómo fusionar varias fuentes de LiveData

MediatorLiveData es una subclase de LiveData que te permite combinar varias fuentes de LiveData. Los observadores de los objetos MediatorLiveData se activan cada vez que cambia alguno de los objetos de origen de LiveData.

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

  • Un objeto LiveData asociado con los datos almacenados en la base de datos.
  • Un objeto LiveData asociado con los datos a los que se accede 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