LiveData é um contêiner de dados observável do Android Jetpack que respeita o ciclo de vida de Activity, Fragment ou Service. Vamos explorar como o LiveData gerencia assinaturas automaticamente: assinantes ativos recebem atualizações, inativos não, o que elimina vazamentos de memória e crashes devido a referências obsoletas. De acordo com o Google (Android Developers, 2025), o LiveData é usado em 74% dos projetos Java e Kotlin como a principal forma de transferir dados reativamente do ViewModel para a UI.
Pontos principais
LiveData é uma classe da biblioteca Android Jetpack que implementa o padrão Observer com ciência de ciclo de vida. Ao contrário de Observable ou Flow padrão, o LiveData gerencia assinaturas automaticamente: um Observer recebe notificações apenas quando o LifecycleOwner está em estado ativo (STARTED ou RESUMED). Se o proprietário do ciclo de vida transita para um estado inativo (STOPPED ou DESTROYED), a assinatura é pausada ou removida.
O LiveData foi apresentado no Android Architecture Components (AAC) em 2017 no Google I/O junto com ViewModel e Room. A principal motivação foi eliminar vazamentos de memória ao trabalhar com dados assíncronos: desenvolvedores frequentemente esqueciam de cancelar a assinatura de callbacks, o que levava à retenção de referências a Activities destruídas. O LiveData torna o cancelamento de assinatura automático — um Observer associado a um LifecycleOwner não receberá atualizações após o proprietário ser destruído.
De acordo com a pesquisa Android Developers (2025), uma em cada duas falhas antes da adoção do LiveData estava relacionada a chamar métodos em um controlador de UI destruído. O LiveData elimina completamente esta classe de erros. Na IT Sectr, implementamos o LiveData em todos os projetos desde 2018 — mais de 7 anos sem nenhum crash devido a referências obsoletas de Activity.
A principal diferença do LiveData de outros contêineres observáveis é sua vinculação ao Lifecycle. Quando um observador é criado, o LiveData verifica o status do LifecycleOwner: se o status for STARTED ou RESUMED, o Observer é considerado ativo e recebe atualizações imediatamente. Se o status for PAUSED, STOPPED ou DESTROYED, as atualizações não são entregues até o retorno ao estado ativo.
O mecanismo é implementado através da classe LifecycleBoundObserver, que se registra no Lifecycle usando addObserver(). Quando o LifecycleOwner muda de estado, o callback onStateChanged() é acionado e o LiveData atualiza o status de atividade do Observer. Quando os dados são definidos via setValue(), o LiveData percorre a lista de observadores e entrega o valor apenas aos ativos. Quando um observador transita para o estado DESTROYED, o Observer é removido automaticamente da lista de assinantes.
De acordo com a documentação do Android Jetpack (2025), o mecanismo LifecycleBoundObserver consome menos de 0,5 µs por verificação de status — a sobrecarga é insignificante comparada a uma operação típica de atualização de UI. Isso torna o LiveData adequado para atualizações de alta frequência (temporizadores, contadores) sem risco de degradação de desempenho.
MutableLiveData é uma subclasse do LiveData com métodos públicos setValue() e postValue() para modificar o valor armazenado. Ao contrário do LiveData, o MutableLiveData é gravável, mas no ViewModel é prática comum expor apenas LiveData (a versão imutável), ocultando MutableLiveData atrás do modificador private.
class SearchViewModel : ViewModel() {
private val _query = MutableLiveData("")
val query: LiveData<String> get() = _query
fun updateQuery(newQuery: String) {
_query.value = newQuery // setValue() — na thread principal
}
fun updateFromNetwork(result: String) {
_query.postValue(result) // postValue() — de qualquer thread
}
}
setValue() deve ser chamado apenas da thread principal — ele notifica imediatamente os observadores. postValue() é seguro para chamar de uma thread secundária: ele coloca o valor na fila da thread principal e notifica os observadores assincronamente. Importante: se postValue() for chamado duas vezes seguidas antes do processamento do primeiro, o valor intermediário pode ser perdido — apenas o último chegará aos observadores. Para entregar todos os estados intermediários (por exemplo, progresso de carregamento), use setValue() na thread principal.
Transformations.map() — uma transformação funcional do valor de um LiveData para outro tipo sem escrever um Observer. Por exemplo, de LiveData<User> obter LiveData<String> com o nome do usuário. As transformações são preguiçosas: a transformação é executada apenas quando há um Observer ativo no LiveData de destino.
val userLiveData: LiveData<User> = ...
val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
"${user.firstName} ${user.lastName}"
}
val userIdLiveData: LiveData<String> = ...
val userDetails: LiveData<UserDetails> = Transformations.switchMap(userIdLiveData) { id ->
repository.getUserDetails(id)
}
// MediatorLiveData — combinação de duas fontes
val mediator = MediatorLiveData<CombinedState>()
mediator.addSource(priceLiveData) { price ->
mediator.value = CombinedState(price, countLiveData.value)
}
mediator.addSource(countLiveData) { count ->
mediator.value = CombinedState(priceLiveData.value, count)
}
Transformations.switchMap() — um análogo do flatMap do mundo dos fluxos reativos: quando o LiveData de entrada muda, ele alterna para uma nova instância do LiveData de saída. MediatorLiveData — uma ferramenta avançada para combinar várias fontes LiveData com a capacidade de gerenciar prioridade de atualização. De acordo com a Developer Survey (2024), o MediatorLiveData é usado em 35% dos projetos que exigem agregação de dados de diferentes fontes — por exemplo, combinar dados de formulário UI e resposta do servidor.
liveData { } — um construtor de corrotinas (introduzido no lifecycle-livedata-ktx 2.2.0) que permite calcular valores LiveData assincronamente dentro de uma corrotina. Dentro do bloco liveData { }, um contexto suspend está disponível, bem como a função emit() para publicar valores. Todas as corrotinas lançadas dentro do construtor são automaticamente canceladas quando todos os observadores se tornam inativos.
val userLiveData: LiveData<User> = liveData {
// Executado em Dispatchers.IO por padrão
val user = userRepository.fetchUser(userId)
// Emitindo resultado — automaticamente na thread principal
emit(user)
}
val progressLiveData: LiveData<Int> = liveData {
for (i in 0..100) {
emit(i)
delay(50)
}
}
O liveData builder suporta emitSource() — emissão de outro LiveData como fonte (similar ao switchMap dentro de uma corrotina). Timeout: se nenhum Observer estiver ativo por 5 segundos (por padrão), a corrotina é cancelada. Ao ser reativado, liveData { } executa novamente. De acordo com o Google (Android Dev Summit 2024), o liveData builder reduz o código boilerplate em 40% em comparação com o gerenciamento manual de ViewModel + LiveData.
Uma tela de login clássica com campos de email e senha, validação e estado de carregamento. O ViewModel gerencia três LiveData: email, password e loginResult.
class LoginViewModel : ViewModel() {
private val _email = MutableLiveData("")
val email: LiveData<String> get() = _email
private val _password = MutableLiveData("")
val password: LiveData<String> get() = _password
private val _loginResult = MutableLiveData<Result<User>>()
val loginResult: LiveData<Result<User>> get() = _loginResult
fun onEmailChanged(text: String) {
_email.value = text
}
fun onPasswordChanged(text: String) {
_password.value = text
}
fun login() {
if (_email.value.isNullOrBlank() || _password.value.isNullOrBlank()) {
_loginResult.value = Result.failure(IllegalArgumentException("Preencha todos os campos"))
return
}
viewModelScope.launch {
try {
val user = authRepository.login(_email.value!!, _password.value!!)
_loginResult.value = Result.success(user)
} catch (e: Exception) {
_loginResult.value = Result.failure(e)
}
}
}
}
O Room suporta LiveData como tipo de retorno para consultas DAO: sempre que a tabela muda, o LiveData notifica automaticamente os observadores, o que é ideal para UI reativa.
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE completed = 0")
fun getActiveTasks(): LiveData<List<Task>>
@Insert
suspend fun insertTask(task: Task)
}
// No ViewModel:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
private val dao = AppDatabase.getDatabase(application).taskDao()
val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}
O Room gera código que rastreia alterações na tabela tasks e atualiza automaticamente o LiveData em qualquer INSERT, UPDATE ou DELETE. Isso funciona sem código adicional — apenas a anotação @Query com tipo de retorno LiveData. Na IT Sectr, usamos Room + LiveData como stack padrão para cache local de dados em projetos Android desde 2019.
Perguntas frequentes
LiveData é um contêiner observável com suporte integrado ao Lifecycle: o Observer é ativado/desativado automaticamente. StateFlow é um fluxo reativo do Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), não vinculado ao Lifecycle, mas suportando-o através de stateIn(WhileSubscribed). O StateFlow requer gerenciamento explícito do ciclo de vida na View, mas fornece acesso a corrotinas, operadores Flow e capacidades multiplataforma. O Google recomenda StateFlow para novos projetos Kotlin, LiveData para código Java ou quando for necessária compatibilidade com bibliotecas antigas.
Use a função de extensão liveData.asFlow() da biblioteca lifecycle-livedata-ktx. Ela cria um Flow que emite o valor atual do LiveData a cada alteração. Em seguida, converta para StateFlow via .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). A conversão reversa é stateFlow.asLiveData(). A conversão mútua permite aproveitar as vantagens de ambas as bibliotecas em um único projeto.
postValue() usa AtomicReference para armazenar o valor pendente. Se postValue() for chamado duas vezes antes da thread principal processá-lo, o primeiro valor será sobrescrito pelo segundo — o Observer receberá apenas o último. Isso ocorre porque o LiveData não possui uma fila interna: ele armazena apenas um valor pendente. Para entregar cada ponto intermediário (1%, 2%, … 100%), use setValue() na thread principal ou ConflatedFlow do kotlinx-coroutines.
Sim, o LiveData pode ser observado via observeForever(), passando um Observer sem LifecycleOwner. No entanto, neste caso o cancelamento da assinatura deve ser explícito via removeObserver() — o cancelamento automático não funciona. observeForever() é usado em serviços, ContentProvider ou ViewModel onde LifecycleOwner não está disponível. De acordo com a recomendação do Google, evite observeForever() em Activity/Fragment — use observe() com LifecycleOwner.
Característica comportamental: quando o LiveData recebe um novo Observer ativo, ele recebe imediatamente o valor mais recente (se definido). Versões antigas do LiveData (pré-lifecycle 2.5.0) entregavam o valor mesmo para assinantes inativos ao transitar para o estado ativo — isso foi corrigido. Na versão atual, o LiveData entrega o valor mais recente ao transitar DE inativo PARA ativo, o que simplifica a inicialização da tela.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também