Visão geral do LiveData   Parte do Android Jetpack.

LiveData é uma classe armazenadora de dados observável. 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 está 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 muda para DESTROYED. Isso é útil principalmente para atividades e fragmentos porque eles podem observar com segurança objetos LiveData sem se preocupar com vazamentos. Atividades e fragmentos são cancelados instantaneamente quando os ciclos de vida 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 objetos Observer quando os dados subjacentes são modificados. Você pode consolidar seu código para atualizar a IU nesses objetos Observer. Assim, não vai ser preciso atualizar a IU sempre que os dados do app forem modificados, já que o observador fará isso por você.
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 modo 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 observar o 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 retidos do 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 objeto LiveData usando o método observe(). O método observe() usa 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 IU, 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

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 um método getter, conforme demonstrado no seguinte exemplo:

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 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 só ocorrerá se o objeto LiveData a ser observado tiver sido definido.

Geralmente, o LiveData oferece atualizações apenas quando os dados são alterados 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 estado 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 alterado desde a última vez em que se tornou ativo.

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

Depois que observe() é chamado com nameObserver transmitido 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. Normalmente, MutableLiveData é usado no ViewModel e, em seguida, o ViewModel expõe apenas objetos LiveData imutáveis aos 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() 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 preenchimento de carga de banco de dados. Em todo caso, a chamada para setValue() ou postValue() aciona 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 sincronizados com os dados armazenados em um banco de dados. Saiba mais sobre os DAOs e a Room no guia sobre a biblioteca de persistência Room.

Usar corrotinas com LiveData

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

LiveData na arquitetura de um app

O LiveData tem reconhecimento do ciclo de vida, seguindo o ciclo de vida das entidades, como atividades e fragmentos. Use o LiveData para a comunicação entre esses proprietários de ciclo de vida e outros objetos com uma duração diferente, como objetos ViewModel. A principal responsabilidade do ViewModel é carregar e gerenciar dados relacionados à IU, o que o torna um ótimo candidato ao armazenamento de objetos LiveData. Crie objetos LiveData no ViewModel e use-os para expor o estado à camada da IU.

As atividades e os fragmentos não podem conter instâncias LiveData porque o papel delas é mostrar dados, não armazenar estados. Além disso, manter atividades e fragmentos livres do armazenamento de dados facilita a criação de testes de unidade.

Pode ser tentador usar objetos LiveData na classe da camada de dados, mas o LiveData não foi projetado para processar fluxos de dados assíncronos. Ainda que seja possível usar transformações do LiveData e MediatorLiveData para isso, essa abordagem tem desvantagens: a capacidade de combinar fluxos de dados é muito limitada, e todos os objetos LiveData, incluindo os criados por transformações, são observados na linha de execução principal. O código abaixo é um exemplo de como o armazenamento de um LiveData no Repository pode bloquear a linha de execução principal:

Kotlin

class UserRepository {

    // DON'T DO THIS! LiveData objects should not live in the repository.
    fun getUsers(): LiveData<List<User>> {
        ...
    }

    fun getNewPremiumUsers(): LiveData<List<User>> {
        return getUsers().map { users ->
            // This is an expensive call being made on the main thread and may
            // cause noticeable jank in the UI!
            users
                .filter { user ->
                  user.isPremium
                }
          .filter { user ->
              val lastSyncedTime = dao.getLastSyncedTime()
              user.timeCreated > lastSyncedTime
                }
    }
}

Java

class UserRepository {

    // DON'T DO THIS! LiveData objects should not live in the repository.
    LiveData<List<User>> getUsers() {
        ...
    }

    LiveData<List<User>> getNewPremiumUsers() {
    return Transformations.map(getUsers(),
        // This is an expensive call being made on the main thread and may cause
        // noticeable jank in the UI!
        users -> users.stream()
            .filter(User::isPremium)
            .filter(user ->
                user.getTimeCreated() > dao.getLastSyncedTime())
            .collect(Collectors.toList()));
    }
}

Se você precisar usar fluxos de dados em outras camadas do app, considere usar fluxos Kotlin e os converter para LiveData no ViewModel usando o método asLiveData(). Acesse este codelab para aprender a usar Flow do Kotlin com o LiveData. Para bases de código criadas com Java, use executores em conjunto com callbacks ou RxJava.

Estender LiveData

O LiveData considera que um observador encontra-se em um estado ativo se o ciclo de vida dele está nos estados STARTED ou RESUMED. O código de exemplo 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 dos preços das ações desse método.
  • O método onInactive() é chamado quando o objeto LiveData não tem observadores ativos. 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 LiveData e notifica os observadores ativos sobre a mudança.

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

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(getViewLifecycleOwner(), price -> {
            // Update the UI.
        });
    }
}

O método observe() passa o LifecycleOwner associado à visualização do fragmento como o primeiro argumento. Fazer isso indica que esse observador está vinculado ao objeto Lifecycle associado ao proprietário, o que significa o seguinte:

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

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> = userLiveData.map {
    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 transmitida 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 = userId.switchMap { 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 o objeto LiveData retornado esteja sendo observado. Como as transformações são calculadas lentamente, o comportamento relacionado ao ciclo de vida é transmitido implicitamente sem exigir chamadas ou dependências explícitas adicionais.

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 retorne o CEP desse endereço. Você pode implementar o ViewModel simples para esse componente, conforme ilustrado pela seguinte amostra de código:

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> = addressInput.switchMap {
            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 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 de origem. Para saber mais sobre esse padrão, consulte a documentação de referência da classe Transformations.

Mesclar várias fontes 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 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 da arquitetura do app.

Outros recursos

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

Amostras

  • Sunflower (link em inglês), um app que demonstra as práticas recomendadas com os componentes de arquitetura

Codelabs

Blogs

Vídeos