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 objetosObserver
. 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 objetoLiveData
se conecta ao serviço do sistema uma vez e, depois, qualquer observador que precise do recurso pode apenas observar o objetoLiveData
. Para saber mais, consulte Estender o LiveData.
Trabalhar com objetos LiveData
Siga estas etapas para trabalhar com objetos LiveData
:
- Crie uma instância do
LiveData
para conter um certo tipo de dados. Isso geralmente ocorre dentro da classeViewModel
. - Crie um objeto
Observer
que defina o métodoonChanged()
, que controla o que acontece quando os dados retidos do objetoLiveData
são modificados. Geralmente, um objetoObserver
é criado em um controlador de IU, como uma atividade ou um fragmento. Anexe o objeto
Observer
ao objetoLiveData
usando o métodoobserve()
. O métodoobserve()
usa um objetoLifecycleOwner
. Isso inscreve o objetoObserver
no objetoLiveData
para que ele seja notificado em caso de mudanças. Normalmente, o objetoObserver
é 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 objetosLiveData
que está observando. Isso só ocorrerá se o objetoLiveData
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 objetoLiveData
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 objetoLiveData
não tem observadores ativos. Como nenhum observador está escutando, não há motivo para permanecer conectado ao serviço deStockManager
. - O método
setValue(T)
atualiza o valor da instânciaLiveData
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 objetoLiveData
e separa e envia o resultado para os itens descendentes. A função transmitida paraswitchMap()
precisa retornar um objetoLiveData
, 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
- Android Room com View (Java) (Kotlin)
- Conheça corrotinas avançadas com fluxo do Kotlin e LiveData
Blogs
- ViewModels e LiveData: Padrões + AntiPadrões (link em inglês)
- LiveData além do ViewModel: padrões reativos com Transformações e MediatorLiveData (link em inglês)
- LiveData com SnackBar, Navigation e outros eventos (o caso SingleLiveEvent) (link em inglês)
Vídeos
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Usar corrotinas do Kotlin com componentes que reconhecem o ciclo de vida
- Como gerenciar ciclos de vida com componentes que os reconhecem
- Testar a implementação da Paging