Visão geral do LiveData   Parte do Android Jetpack.

LiveData é uma classe armazenadora de dados observáveis. Diferente de um observável comum, o LiveData conta com reconhecimento de ciclo de vida, ou seja, ele respeita o ciclo de vida de outros componentes do app, como atividades, fragmentos ou serviços. Esse reconhecimento garante que o LiveData atualize apenas os observadores de componente do app que estão em um estado ativo no ciclo de vida.

O LiveData considera que um observador, que é representado pela classe Observer, encontra-se em um estado ativo se o ciclo de vida dele estiver no estado STARTED ou RESUMED. O LiveData só notifica observadores ativos sobre atualizações. Observadores inativos registrados para observar objetos LiveData não são notificados sobre mudanças.

Você pode registrar um observador pareado com um objeto que implementa a interface LifecycleOwner. Essa relação permite que o observador seja removido quando o estado do objeto Lifecycle correspondente for modificado para DESTROYED. Isso é especialmente útil para atividades e fragmentos, porque eles podem observar com segurança objetos LiveData e não se preocupar com vazamentos, já que a inscrição de atividades e fragmentos é cancelada instantaneamente quando os ciclos de vida deles são destruídos.

Para saber mais sobre como usar o LiveData, consulte Trabalhar com objetos LiveData.

As vantagens de usar o LiveData

O uso do LiveData oferece as seguintes vantagens:

Garantia de que a IU corresponde ao estado dos dados
O LiveData segue o padrão do observador. Ele notifica os objetos Observer quando o estado do ciclo de vida é modificado. Você pode consolidar seu código para atualizar a IU nesses objetos Observer. Em vez de atualizar a IU toda vez que os dados do app forem modificados, seu observador pode atualizar a IU sempre que houver uma mudança.
Sem vazamentos de memória
Observadores são vinculados a objetos Lifecycle e realizam a limpeza por si mesmos quando o ciclo de vida associado é destruído.
Sem falhas causadas por atividades interrompidas
Se o ciclo de vida do observador estiver inativo, como no caso de uma atividade na pilha de retorno, ele não receberá nenhum evento do LiveData.
Sem gerenciamento manual do ciclo de vida
Os componentes da IU apenas observam dados relevantes e não interrompem nem retomam a observação. O LiveData gerencia tudo isso automaticamente, já que conta com reconhecimento das mudanças relevantes no status do ciclo de vida durante a observação.
Dados sempre atualizados
Se um ciclo de vida se tornar inativo, ele receberá os dados mais recentes quando ficar ativo novamente. Por exemplo, uma atividade que estava em segundo plano receberá os dados mais recentes logo após retornar ao primeiro plano.
Mudanças de configuração apropriadas
Se uma atividade ou um fragmento for recriado devido a uma mudança na configuração, como a rotação do dispositivo, ela receberá imediatamente os dados mais recentes disponíveis.
Compartilhamento de recursos
Você pode estender um objeto LiveData usando o padrão singleton para unir os serviços do sistema de forma que eles possam ser compartilhados no seu app. O objeto LiveData se conecta ao serviço do sistema uma vez e, depois, qualquer observador que precise do recurso pode apenas assistir ao objeto LiveData. Para saber mais, consulte Estender o LiveData.

Trabalhar com objetos LiveData

Siga estas etapas para trabalhar com objetos LiveData:

  1. Crie uma instância do LiveData para conter um certo tipo de dados. Isso geralmente ocorre dentro da classe ViewModel.
  2. Crie um objeto Observer que defina o método onChanged(), que controla o que acontece quando os dados contidos no objeto LiveData são modificados. Geralmente, um objeto Observer é criado em um controlador de IU, como uma atividade ou um fragmento.
  3. Anexe o objeto Observer ao LiveData usando o método observe(). O método observe() aceita um objeto LifecycleOwner. Isso inscreve o objeto Observer no objeto LiveData para que ele seja notificado em caso de mudanças. Normalmente, o objeto Observer é anexado em um controlador de UI, como uma atividade ou um fragmento.

Quando você atualiza o valor armazenado no objeto LiveData, ele aciona todos os observadores registrados, desde que o LifecycleOwner anexado esteja no estado ativo.

O LiveData permite que observadores do controlador de IU inscrevam-se em atualizações. Quando os dados contidos pelo objeto LiveData são modificados, a IU é atualizada automaticamente em resposta.

Criar objetos LiveData

O LiveData é um wrapper que pode ser usado com qualquer dado, incluindo objetos que implementam Collections, como List. Um objeto LiveData normalmente é armazenado dentro de um objeto ViewModel, e é acessado por meio de um método getter, conforme demonstrado no exemplo a seguir:

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, os dados em um objeto LiveData não estão definidos.

Saiba mais sobre os benefícios e o uso da classe ViewModel no Guia do ViewModel.

Observar objetos LiveData

Na maioria dos casos, o método onCreate() de um componente do app é o lugar certo para começar a observar um objeto LiveData pelos seguintes motivos:

  • Para garantir que o sistema não faça chamadas redundantes de uma atividade ou do método onResume() do fragmento.
  • Para garantir que a atividade ou o fragmento tenha dados que possam ser exibidos assim que se tornarem ativos. Assim que um componente do app atingir o estado STARTED, ele receberá o valor mais recente dos objetos LiveData que está observando. Isso ocorrerá apenas se o objeto LiveData a ser observado tiver sido definido.

Geralmente, o LiveData oferece atualizações apenas quando os dados são modificados e somente para observadores ativos. Uma exceção desse comportamento é que os observadores também recebem uma atualização quando passam de um estado inativo para um ativo. Além disso, se o observador passar de inativo para ativo uma segunda vez, ele só receberá uma atualização se o valor tiver sido modificado desde a última vez em que se tornou ativo.

A amostra de código a seguir ilustra como começar a observar um 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);
        }
    }
    

Depois de observe() ser chamado com nameObserver passado como parâmetro, onChanged() é imediatamente invocado, fornecendo o valor mais recente armazenado em mCurrentName. Se o objeto LiveData não tiver definido um valor em mCurrentName, onChanged() não será chamado.

Atualizar objetos LiveData

O LiveData não tem métodos disponíveis publicamente para atualizar os dados armazenados. A classe MutableLiveData expõe os métodos setValue(T) e postValue(T) publicamente, e você precisará usá-los se for necessário editar o valor armazenado em um objeto LiveData. Geralmente, MutableLiveData é usado no ViewModel e, em seguida, o ViewModel expõe apenas os objetos LiveData imutáveis para os observadores.

Depois de configurar o relacionamento do observador, você poderá atualizar o valor do objeto LiveData, conforme ilustrado no exemplo a seguir, que aciona todos os observadores quando o usuário toca em um botão:

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);
        }
    });
    

No exemplo, chamar setValue(T) faz com que os observadores chamem os métodos onChanged() deles com o valor John Doe. O exemplo mostra um pressionamento de botão, mas setValue() ou postValue() poderia ser chamado para atualizar mName por diversos motivos, inclusive em resposta a uma solicitação de rede ou a um preenchimento de carga de banco de dados. Em todo caso, a chamada para setValue() ou postValue() aciona os observadores e atualiza a IU.

Usar o LiveData com a Room

A biblioteca de persistência Room é compatível com consultas observáveis, que retornam objetos LiveData. Consultas observáveis são escritas como parte de um objeto de acesso ao banco de dados (DAO, na sigla em inglês).

A Room gera todo o código necessário para atualizar o objeto LiveData quando um banco de dados é atualizado. O código gerado executa a consulta de forma assíncrona em uma linha de execução em segundo plano, se necessário. Esse padrão é útil para manter os dados exibidos em uma IU sincronizada com os dados armazenados em um banco de dados. Saiba mais sobre os DAOs e a Room no guia sobre a biblioteca persistente Room.

Usar corrotinas com LiveData

O LiveData inclui compatibilidade com corrotinas de Kotlin. Para mais informações, consulte Usar corrotinas de Kotlin com Componentes da arquitetura do Android.

Estender LiveData

O LiveData considera que um observador estará em estado ativo se o ciclo de vida do observador estiver nos estados STARTED ou RESUMED. A amostra de código a seguir ilustra como estender a classe 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);
        }
    }
    

A implementação do listener de preços nesse exemplo inclui os seguintes métodos importantes:

  • O método onActive() é chamado quando o objeto LiveData tem um observador ativo. Isso significa que você precisa começar a observar as atualizações de preços das ações desse método.
  • O método onInactive() é chamado quando o objeto LiveData não tem nenhum observador ativo. Como nenhum observador está escutando, não há motivo para permanecer conectado ao serviço de StockManager.
  • O método setValue(T) atualiza o valor da instância de LiveData e notifica qualquer observador ativo sobre a mudança.

É possível usar a classe StockLiveData da seguinte maneira:

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.
            });
        }
    }
    

O método observe() transmite o fragmento, que é uma instância de LifecycleOwner, como o primeiro argumento. Fazer isso indica que esse observador está vinculado ao objeto Lifecycle associado ao proprietário, o que significa que:

  • se o objeto Lifecycle não estiver em um estado ativo, o observador não será chamado mesmo que o valor seja modificado;
  • depois que o objeto Lifecycle for destruído, o observador será removido automaticamente.

O fato de os objetos LiveData contarem com reconhecimento de ciclo de vida significa que você pode compartilhá-los entre várias atividades, fragmentos e serviços. Para manter o exemplo simples, você pode implementar a classe LiveData como um singleton da seguinte maneira:

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);
        }
    }
    

E é possível usá-la no fragmento da seguinte forma:

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.
            });
        }
    }
    

Vários fragmentos e atividades podem observar a instância MyPriceListener. O LiveData se conectará ao serviço do sistema apenas se um ou mais deles estiverem visíveis e ativos.

Transformar LiveData

É recomendável fazer mudanças no valor armazenado em um objeto LiveData antes de enviá-lo para os observadores, ou poderá ser necessário retornar uma instância de LiveData diferente dependendo do valor do outro. O pacote Lifecycle fornece a classe Transformations, que inclui métodos auxiliares compatíveis com esses cenários.

Transformations.map()
Aplica uma função ao valor armazenado no objeto LiveData e propaga o resultado para os itens descendentes.

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 forma semelhante a map(), aplica uma função ao valor armazenado no objeto LiveData e separa e envia o resultado para os itens descendentes. A função passada para switchMap() precisa retornar um objeto LiveData, conforme ilustrado pelo exemplo a seguir:

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) );
    

Você pode usar métodos de transformação para transportar informações pelo ciclo de vida do observador. As transformações não são calculadas a menos que um observador esteja assistindo ao objeto LiveData retornado. Como as transformações são calculadas lentamente, o comportamento relacionado ao ciclo de vida é transmitido implicitamente sem exigir outras chamadas ou dependências explícitas.

Se você acha que precisará de um objeto Lifecycle dentro de um objeto ViewModel, usar uma transformação provavelmente é uma solução melhor. Por exemplo, suponha que você tem um componente de IU que aceita um endereço e retorna o CEP desse endereço. Você pode implementar o ViewModel simples para esse componente, conforme ilustrado pela amostra de código a seguir:

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);
        }
    }
    

O componente de IU precisará cancelar a inscrição do objeto LiveData anterior e fazer o registro dele na nova instância sempre que ele chamar getPostalCode(). Além disso, se o componente de IU for recriado, ele acionará outra chamada para o método repository.getPostCode() em vez de usar o resultado da chamada anterior.

Em vez disso, é possível implementar a pesquisa de CEP como uma transformação da entrada de endereço, conforme mostrado no exemplo a seguir:

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);
      }
    }
    

Nesse caso, o campo postalCode é definido como uma transformação de addressInput. Contanto que seu app tenha um observador ativo associado ao campo postalCode, o valor do campo será recalculado e recuperado sempre que addressInput mudar.

Esse mecanismo permite que os níveis mais baixos do app criem objetos LiveData que são calculados lentamente sob demanda. Um objeto ViewModel pode receber facilmente as referências a objetos LiveData e, em seguida, definir as regras de transformação acima deles.

Criar novas transformações

Há várias transformações específicas diferentes que podem ser úteis para seu app, mas elas não são fornecidas por padrão. Para implementar sua própria transformação, você pode usar a classe MediatorLiveData, que ouve outros objetos LiveData e processa eventos emitidos por eles. O MediatorLiveData propaga corretamente o próprio estado para o objeto LiveData da origem. Para saber mais sobre esse padrão, consulte a documentação de referência da classe Transformations.

Mesclar várias origens de LiveData

MediatorLiveData é uma subclasse de LiveData que permite mesclar várias origens de LiveData. Observadores de objetos MediatorLiveData são então acionados sempre que qualquer um dos objetos da origem de LiveData original é modificado.

Por exemplo, se você tiver um objeto LiveData na sua IU que possa ser atualizado a partir de um banco de dados local ou de uma rede, será possível adicionar as seguintes origens ao objeto MediatorLiveData:

  • Um objeto LiveData associado aos dados armazenados no banco de dados.
  • Um objeto LiveData associado aos dados acessados a partir da rede.

Sua atividade só precisa observar o objeto MediatorLiveData para receber atualizações de ambas as origens. Para ver um exemplo detalhado, consulte a seção Adendo: como exibir o status da rede do Guia para a arquitetura do app.

Outros recursos

Para saber mais sobre a classe LiveData, consulte os seguintes recursos.

Amostras

Codelabs

Blogs (links em inglês)

Vídeos (em inglês)