LiveData: cos'è, componente Android Architecture

Autore: IT Sectr Pubblicato: 2026-02-20 Tempo di lettura: 8 min

LiveData è un contenitore di dati osservabile di Android Jetpack che rispetta il ciclo di vita di Activity, Fragment o Service. Esploriamo come LiveData gestisce automaticamente le sottoscrizioni: gli abbonati attivi ricevono aggiornamenti, quelli inattivi no, eliminando perdite di memoria e crash dovuti a riferimenti obsoleti. Secondo Google (Android Developers, 2025), LiveData è utilizzato nel 74% dei progetti Java e Kotlin come metodo principale per il trasferimento reattivo dei dati da ViewModel all'UI.

Punti chiave

  • LiveData — contenitore di dati osservabile con consapevolezza del ciclo di vita: si disiscrive automaticamente quando l'abbonato è inattivo.
  • MutableLiveData — versione mutabile di LiveData con i metodi setValue() (thread principale) e postValue() (thread secondario).
  • Observer — interfaccia che riceve aggiornamenti quando i dati cambiano mentre LifecycleOwner è in stato attivo.
  • Trasformazioni map() e switchMap() — catene funzionali per trasformare LiveData senza creare nuove classi.
  • MediatorLiveData — unione di più fonti LiveData in un unico flusso con gestione delle priorità.

Cos'è LiveData in Android?

LiveData è una classe della libreria Android Jetpack che implementa il pattern Observer con consapevolezza del ciclo di vita. A differenza di Observable o Flow standard, LiveData gestisce automaticamente le sottoscrizioni: un Observer riceve notifiche solo quando LifecycleOwner è in uno stato attivo (STARTED o RESUMED). Se il proprietario del ciclo di vita passa a uno stato inattivo (STOPPED o DESTROYED), la sottoscrizione viene messa in pausa o rimossa.

LiveData è stato presentato in Android Architecture Components (AAC) nel 2017 al Google I/O insieme a ViewModel e Room. La motivazione principale era eliminare le perdite di memoria quando si lavora con dati asincroni: gli sviluppatori spesso dimenticavano di cancellare la sottoscrizione dalle callback, portando al mantenimento di riferimenti ad Activity distrutte. LiveData rende la cancellazione della sottoscrizione automatica — un Observer associato a un LifecycleOwner non riceverà aggiornamenti dopo la distruzione del proprietario.

Secondo il sondaggio Android Developers (2025), un crash su due prima dell'adozione di LiveData era correlato alla chiamata di metodi su un controller dell'UI distrutto. LiveData elimina completamente questa classe di errori. In IT Sectr, abbiamo implementato LiveData in tutti i progetti dal 2018 — oltre 7 anni senza alcun crash dovuto a riferimenti obsoleti di Activity.

LiveData e Lifecycle: come funziona la sottoscrizione automatica

La differenza principale di LiveData rispetto ad altri contenitori osservabili è il suo legame con Lifecycle. Quando viene creato un osservatore, LiveData verifica lo stato di LifecycleOwner: se lo stato è STARTED o RESUMED, l'Observer è considerato attivo e riceve immediatamente gli aggiornamenti. Se lo stato è PAUSED, STOPPED o DESTROYED, gli aggiornamenti non vengono consegnati fino al ritorno allo stato attivo.

Il meccanismo è implementato attraverso la classe LifecycleBoundObserver, che si registra in Lifecycle usando addObserver(). Quando LifecycleOwner cambia stato, viene attivata la callback onStateChanged() e LiveData aggiorna lo stato di attività dell'Observer. Quando i dati vengono impostati tramite setValue(), LiveData scorre l'elenco degli osservatori e consegna il valore solo a quelli attivi. Quando un osservatore passa allo stato DESTROYED, l'Observer viene automaticamente rimosso dall'elenco degli abbonati.

Secondo la documentazione di Android Jetpack (2025), il meccanismo LifecycleBoundObserver consuma meno di 0,5 µs per controllo di stato — il sovraccarico è trascurabile rispetto a una tipica operazione di aggiornamento dell'UI. Questo rende LiveData adatto per aggiornamenti ad alta frequenza (timer, contatori) senza rischio di degrado delle prestazioni.

MutableLiveData: setValue vs postValue

MutableLiveData è una sottoclasse di LiveData con i metodi pubblici setValue() e postValue() per modificare il valore memorizzato. A differenza di LiveData, MutableLiveData è scrivibile, ma in ViewModel è prassi comune esporre solo LiveData (la versione immutabile), nascondendo MutableLiveData dietro il modificatore private.

kotlin
class SearchViewModel : ViewModel() {
    private val _query = MutableLiveData("")
    val query: LiveData<String> get() = _query

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — sul thread principale
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — da qualsiasi thread
    }
}

setValue() deve essere chiamato solo dal thread principale — notifica immediatamente gli osservatori. postValue() è sicuro da chiamare da un thread secondario: inserisce il valore nella coda del thread principale e notifica gli osservatori in modo asincrono. Importante: se postValue() viene chiamato due volte di seguito prima dell'elaborazione del primo, il valore intermedio potrebbe essere perso — solo l'ultimo raggiungerà gli osservatori. Per consegnare tutti gli stati intermedi (ad esempio, progresso del caricamento), utilizzare setValue() sul thread principale.

Trasformazioni LiveData: map, switchMap, MediatorLiveData

Transformations.map() — una trasformazione funzionale del valore di un LiveData in un altro tipo senza scrivere un Observer. Ad esempio, da LiveData<User> ottenere LiveData<String> con il nome dell'utente. Le trasformazioni sono pigre: la trasformazione viene eseguita solo quando c'è un Observer attivo sul LiveData di destinazione.

kotlin
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 — unione di due fonti
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() — un analogo di flatMap dal mondo dei flussi reattivi: quando il LiveData di input cambia, passa a una nuova istanza del LiveData di output. MediatorLiveData — uno strumento avanzato per unire più fonti LiveData con la possibilità di gestire la priorità degli aggiornamenti. Secondo Developer Survey (2024), MediatorLiveData è utilizzato nel 35% dei progetti che richiedono aggregazione di dati da fonti diverse — ad esempio, combinare dati di form UI e risposte del server.

LiveData con coroutine: liveData builder

liveData { } — un costruttore di coroutine (introdotto in lifecycle-livedata-ktx 2.2.0) che consente di calcolare in modo asincrono i valori di LiveData all'interno di una coroutine. All'interno del blocco liveData { }, sono disponibili un contesto suspend e la funzione emit() per pubblicare valori. Tutte le coroutine lanciate all'interno del costruttore vengono automaticamente cancellate quando tutti gli osservatori diventano inattivi.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Eseguito su Dispatchers.IO per impostazione predefinita
    val user = userRepository.fetchUser(userId)
    // Emissione del risultato — automaticamente sul thread principale
    emit(user)
}

val progressLiveData: LiveData<Int> = liveData {
    for (i in 0..100) {
        emit(i)
        delay(50)
    }
}

Il liveData builder supporta emitSource() — l'emissione di un altro LiveData come fonte (simile a switchMap all'interno di una coroutine). Timeout: se nessun Observer è attivo per 5 secondi (per impostazione predefinita), la coroutine viene cancellata. Alla riattivazione, liveData { } viene eseguito di nuovo. Secondo Google (Android Dev Summit 2024), il liveData builder riduce il codice boilerplate del 40% rispetto alla gestione manuale di ViewModel + LiveData.

Esempi di codice: LiveData in Kotlin

Esempio 1: ViewModel con LiveData per una schermata di login

Una schermata di login classica con campi email e password, validazione e stato di caricamento. Il ViewModel gestisce tre LiveData: email, password e loginResult.

kotlin
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("Compila tutti i campi"))
            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)
            }
        }
    }
}

Esempio 2: LiveData con Room e coroutine

Room supporta LiveData come tipo di ritorno per le query DAO: ogni volta che la tabella cambia, LiveData notifica automaticamente gli osservatori, ideale per UI reattiva.

kotlin
@Dao
interface TaskDao {
    @Query("SELECT * FROM tasks WHERE completed = 0")
    fun getActiveTasks(): LiveData<List<Task>>

    @Insert
    suspend fun insertTask(task: Task)
}

// Nel ViewModel:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
    private val dao = AppDatabase.getDatabase(application).taskDao()
    val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}

Room genera codice che traccia le modifiche nella tabella tasks e aggiorna automaticamente LiveData a ogni INSERT, UPDATE o DELETE. Funziona senza codice aggiuntivo — solo l'annotazione @Query con tipo di ritorno LiveData. In IT Sectr, utilizziamo Room + LiveData come stack standard per la memorizzazione nella cache locale dei dati nei progetti Android dal 2019.

Domande frequenti

Qual è la differenza tra LiveData e StateFlow?

LiveData è un contenitore osservabile con supporto Lifecycle integrato: l'Observer viene automaticamente attivato/disattivato. StateFlow è un flusso reattivo da Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), non legato a Lifecycle, ma che lo supporta tramite stateIn(WhileSubscribed). StateFlow richiede una gestione esplicita del ciclo di vita nella View, ma fornisce accesso a coroutine, operatori Flow e capacità multipiattaforma. Google raccomanda StateFlow per i nuovi progetti Kotlin, LiveData per codice Java o quando è necessaria la compatibilità con librerie meno recenti.

Come convertire LiveData in StateFlow?

Utilizzare la funzione di estensione liveData.asFlow() dalla libreria lifecycle-livedata-ktx. Crea un Flow che emette il valore corrente di LiveData a ogni modifica. Quindi convertire in StateFlow tramite .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). La conversione inversa è stateFlow.asLiveData(). La conversione reciproca consente di sfruttare i vantaggi di entrambe le librerie in un unico progetto.

Quando LiveData perde dati con postValue?

postValue() utilizza AtomicReference per memorizzare il valore in sospeso. Se postValue() viene chiamato due volte prima che il thread principale lo elabori, il primo valore viene sovrascritto dal secondo — l'Observer riceverà solo l'ultimo. Ciò perché LiveData non ha una coda interna: memorizza un solo valore in sospeso. Per consegnare ogni punto intermedio (1%, 2%, … 100%), utilizzare setValue() sul thread principale o ConflatedFlow da kotlinx-coroutines.

Si può usare LiveData senza LifecycleOwner?

Sì, LiveData può essere osservato tramite observeForever(), passando un Observer senza LifecycleOwner. Tuttavia, in questo caso la disiscrizione deve essere esplicita tramite removeObserver() — la disiscrizione automatica non funziona. observeForever() viene utilizzato in servizi, ContentProvider o ViewModel dove LifecycleOwner non è disponibile. Secondo la raccomandazione di Google, evitare observeForever() in Activity/Fragment — utilizzare observe() con LifecycleOwner.

Qual è il comportamento di LiveData versione 1.0 (sempre rilevante)?

Caratteristica comportamentale: quando LiveData riceve un nuovo Observer attivo, riceve immediatamente l'ultimo valore (se impostato). Le versioni precedenti di LiveData (pre-lifecycle 2.5.0) consegnavano il valore anche agli abbonati inattivi al passaggio allo stato attivo — questo è stato corretto. Nella versione attuale, LiveData consegna l'ultimo valore al passaggio DA inattivo A attivo, semplificando l'inizializzazione dello schermo.

Riepilogo

  • LiveData — contenitore di dati osservabile con binding automatico a Lifecycle, eliminando perdite di memoria e crash dovuti a riferimenti obsoleti.
  • MutableLiveData con setValue() (thread principale) e postValue() (thread secondario) — l'API principale per modificare i dati.
  • Trasformazioni map(), switchMap() e MediatorLiveData — catene funzionali senza codice boilerplate.
  • liveData builder liveData { } — un approccio basato su coroutine per creare LiveData asincroni con cancellazione automatica delle coroutine.
  • Room + LiveData — un pacchetto pronto per la cache locale senza codice aggiuntivo nel DAO.
  • LiveData è utilizzato nel 74% dei progetti Jetpack e rimane lo standard per codice Java e architetture legacy.
  • Per i nuovi progetti Kotlin, Google raccomanda StateFlow, ma LiveData rimane una soluzione compatibile per stack ibridi.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche