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