LiveData: τι είναι, συστατικό του Android Architecture

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-02-20 Χρόνος ανάγνωσης: 8 λεπ

LiveData — ένα παρατηρήσιμο δοχείο δεδομένων από το Android Jetpack που λαμβάνει υπόψη τον κύκλο ζωής Activity, Fragment ή Service. Αναλύουμε πώς το LiveData διαχειρίζεται αυτόματα τις συνδρομές: οι ενεργοί συνδρομητές λαμβάνουν ενημερώσεις, οι ανενεργοί όχι, εξαλείφοντας διαρροές μνήμης και crashes λόγω παρωχημένων αναφορών. Σύμφωνα με δεδομένα της Google (Android Developers, 2025), το LiveData χρησιμοποιείται στο 74% των έργων Java και Kotlin ως ο κύριος τρόπος μεταφοράς αντιδραστικών δεδομένων από το ViewModel στο UI.

Κύρια σημεία

  • LiveData — παρατηρήσιμο δοχείο δεδομένων με κύκλο ζωής: διαγράφεται αυτόματα όταν ο συνδρομητής είναι ανενεργός.
  • MutableLiveData — τροποποιήσιμη έκδοση του LiveData με μεθόδους setValue() (κύριο νήμα) και postValue() (νήμα παρασκηνίου).
  • Observer — διεπαφή που λαμβάνει ενημερώσεις όταν αλλάζουν τα δεδομένα, όσο το LifecycleOwner είναι σε ενεργή κατάσταση.
  • Μετασχηματισμοί map() και switchMap() — λειτουργικές αλυσίδες μετασχηματισμού του LiveData χωρίς δημιουργία νέων κλάσεων.
  • MediatorLiveData — συνδυασμός πολλαπλών πηγών LiveData σε μία ροή με διαχείριση προτεραιότητας.

Τι είναι το LiveData στο Android;

LiveData — είναι μια κλάση από τη βιβλιοθήκη Android Jetpack που υλοποιεί το μοτίβο Observer λαμβάνοντας υπόψη τον κύκλο ζωής. Σε αντίθεση με τα τυπικά Observable ή Flow, το LiveData διαχειρίζεται αυτόματα τις συνδρομές: ο Observer λαμβάνει ειδοποιήσεις μόνο όταν το LifecycleOwner βρίσκεται σε ενεργή κατάσταση (STARTED ή RESUMED). Εάν ο κάτοχος κύκλου ζωής μεταβεί σε ανενεργή κατάσταση (STOPPED ή DESTROYED), η συνδρομή αναστέλλεται ή αφαιρείται.

Το LiveData παρουσιάστηκε στο Android Architecture Components (AAC) το 2017 στο Google I/O μαζί με το ViewModel και το Room. Το κύριο κίνητρο — η εξάλειψη του προβλήματος διαρροών μνήμης κατά την εργασία με ασύγχρονα δεδομένα: οι προγραμματιστές συχνά ξεχνούσαν να διαγραφούν από callbacks, οδηγώντας σε διατήρηση αναφορών σε κατεστραμμένες Activity. Το LiveData αυτοματοποιεί τη διαγραφή — ο Observer που συνδέεται με το LifecycleOwner δεν λαμβάνει ενημερώσεις μετά την καταστροφή του κατόχου.

Σύμφωνα με έρευνα των Android Developers (2025), κάθε δεύτερο crash πριν από την εφαρμογή του LiveData σχετιζόταν με κλήση μεθόδων σε κατεστραμμένο ελεγκτή UI. Το LiveData εξαλείφει πλήρως αυτή την κατηγορία σφαλμάτων. Στην IT Sectr εφαρμόσαμε το LiveData σε όλα τα έργα από το 2018 — σε 7 χρόνια ούτε ένα crash λόγω παρωχημένης αναφοράς σε Activity.

LiveData και Lifecycle: πώς λειτουργεί η αυτόματη συνδρομή

Η βασική διαφορά του LiveData από άλλα παρατηρήσιμα δοχεία — η σύνδεση με το Lifecycle. Κατά τη δημιουργία ενός παρατηρητή, το LiveData ελέγχει την κατάσταση του LifecycleOwner: εάν η κατάσταση είναι STARTED ή RESUMED, ο Observer θεωρείται ενεργός και λαμβάνει άμεσα ενημερώσεις. Εάν η κατάσταση είναι PAUSED, STOPPED ή DESTROYED, οι ενημερώσεις δεν παραδίδονται μέχρι την επιστροφή σε ενεργή κατάσταση.

Ο μηχανισμός υλοποιείται μέσω της κλάσης LifecycleBoundObserver που εγγράφεται στο Lifecycle με addObserver(). Όταν το LifecycleOwner αλλάζει κατάσταση, ενεργοποιείται το callback onStateChanged() και το LiveData ενημερώνει την κατάσταση δραστηριότητας του Observer. Κατά τον ορισμό δεδομένων μέσω setValue(), το LiveData διατρέχει τη λίστα παρατηρητών και παραδίδει την τιμή μόνο σε ενεργούς. Όταν ο παρατηρητής μεταβαίνει σε κατάσταση DESTROYED, ο Observer αφαιρείται αυτόματα από τη λίστα συνδρομητών.

Σύμφωνα με την τεκμηρίωση του Android Jetpack (2025), ο μηχανισμός LifecycleBoundObserver καταναλώνει λιγότερο από 0,5 µs για τον έλεγχο κατάστασης — η επιβάρυνση είναι αμελητέα σε σύγκριση με μια τυπική λειτουργία ενημέρωσης UI. Αυτό καθιστά το LiveData κατάλληλο για ενημερώσεις υψηλής συχνότητας (χρονομετρητές, μετρητές) χωρίς κίνδυνο υποβάθμισης απόδοσης.

MutableLiveData: setValue vs postValue

MutableLiveData — κληρονομεί από το LiveData με δημόσιες μεθόδους setValue() και postValue() για την αλλαγή της αποθηκευμένης τιμής. Σε αντίθεση με το LiveData, το MutableLiveData είναι διαθέσιμο για εγγραφή, αλλά στο ViewModel συνηθίζεται να δημοσιεύεται μόνο το LiveData (η αμετάβλητη έκδοση), κρύβοντας το MutableLiveData κάτω από τον τροποποιητή private.

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

    fun updateQuery(newQuery: String) {
        _query.value = newQuery  // setValue() — στο κύριο νήμα
    }

    fun updateFromNetwork(result: String) {
        _query.postValue(result)  // postValue() — από οποιοδήποτε νήμα
    }
}

Το setValue() πρέπει να καλείται μόνο από το κύριο νήμα (main thread) — ειδοποιεί αμέσως τους παρατηρητές. Το postValue() είναι ασφαλές για κλήση από νήμα παρασκηνίου: τοποθετεί την τιμή στην ουρά του κύριου νήματος και ειδοποιεί τους παρατηρητές ασύγχρονα. Σημαντικό: εάν το postValue() κληθεί δύο φορές διαδοχικά πριν από την επεξεργασία της πρώτης, η ενδιάμεση τιμή μπορεί να χαθεί — οι παρατηρητές θα λάβουν μόνο την τελευταία. Για τη μετάδοση όλων των ενδιάμεσων καταστάσεων (π.χ. πρόοδος φόρτωσης) χρησιμοποιήστε setValue() στο κύριο νήμα.

Μετασχηματισμοί LiveData: map, switchMap, MediatorLiveData

Transformations.map() — λειτουργικός μετασχηματισμός της τιμής ενός LiveData σε άλλο τύπο χωρίς σύνταξη Observer. Για παράδειγμα, από LiveData<User> να λάβετε LiveData<String> με το όνομα χρήστη. Οι μετασχηματισμοί είναι τεμπέληδες: ο μετασχηματισμός εκτελείται μόνο όταν υπάρχει ενεργός Observer στο στόχο LiveData.

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 — συνδυασμός δύο πηγών
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() — ανάλογο του flatMap από τον κόσμο των αντιδραστικών ροών: κατά την αλλαγή του εισερχόμενου LiveData, μεταβαίνει σε ένα νέο στιγμιότυπο του εξερχόμενου LiveData. MediatorLiveData — προηγμένο εργαλείο για το συνδυασμό πολλαπλών πηγών LiveData με δυνατότητα διαχείρισης προτεραιότητας ενημερώσεων. Σύμφωνα με το Developer Survey (2024), το MediatorLiveData χρησιμοποιείται στο 35% των έργων όπου απαιτείται συγκέντρωση δεδομένων από διάφορες πηγές — για παράδειγμα, συνδυασμός δεδομένων φόρμας UI και απάντησης διακομιστή.

LiveData με coroutines: liveData builder

liveData { } — coroutine builder (εμφανίστηκε στο lifecycle-livedata-ktx 2.2.0) που επιτρέπει τον ασύγχρονο υπολογισμό της τιμής LiveData μέσα σε ένα coroutine. Μέσα στο μπλοκ liveData { } είναι διαθέσιμο το περιβάλλον suspend, καθώς και η συνάρτηση emit() για τη δημοσίευση τιμών. Όλα τα coroutine που εκκινούνται μέσα στο builder ακυρώνονται αυτόματα όταν όλοι οι παρατηρητές είναι ανενεργοί.

kotlin
val userLiveData: LiveData<User> = liveData {
    // Εκτελείται στο Dispatchers.IO από προεπιλογή
    val user = userRepository.fetchUser(userId)
    // Εκπέμπουμε το αποτέλεσμα — αυτόματα στο κύριο νήμα
    emit(user)
}

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

Το liveData builder υποστηρίζει emitSource() — εκπομπή άλλου LiveData ως πηγή (ανάλογο του switchMap μέσα σε coroutine). Χρονικό όριο: εάν κανένας Observer δεν είναι ενεργός για 5 δευτερόλεπτα (προεπιλογή), το coroutine ακυρώνεται. Κατά την επανενεργοποίηση, το liveData { } εκτελείται ξανά. Σύμφωνα με την Google (Android Dev Summit 2024), το liveData builder μειώνει 40% του κώδικα boilerplate σε σύγκριση με τη χειροκίνητη διαχείριση ViewModel + LiveData.

Παραδείγματα κώδικα: LiveData σε Kotlin

Παράδειγμα 1: ViewModel με LiveData για οθόνη σύνδεσης

Κλασική οθόνη σύνδεσης με πεδία email και κωδικού πρόσβασης, επικύρωση και κατάσταση φόρτωσης. Το ViewModel διαχειρίζεται τρία LiveData: email, password και 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("Συμπληρώστε όλα τα πεδία"))
            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)
            }
        }
    }
}

Παράδειγμα 2: LiveData με Room και coroutines

Το Room υποστηρίζει LiveData ως τύπο επιστροφής ερωτήματος DAO: με κάθε αλλαγή πίνακα, το LiveData ειδοποιεί αυτόματα τους παρατηρητές, ιδανικό για αντιδραστικό UI.

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

    @Insert
    suspend fun insertTask(task: Task)
}

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

Το Room παράγει κώδικα που παρακολουθεί αλλαγές στον πίνακα tasks και ενημερώνει αυτόματα το LiveData σε κάθε INSERT, UPDATE ή DELETE. Αυτό λειτουργεί χωρίς επιπλέον κώδικα — μόνο ο σχολιασμός @Query με τύπο επιστροφής LiveData αρκεί. Στην IT Sectr χρησιμοποιούμε Room + LiveData ως τυπική στοίβα για τοπική προσωρινή αποθήκευση δεδομένων σε έργα Android από το 2019.

Συχνές ερωτήσεις

Ποια είναι η διαφορά μεταξύ LiveData και StateFlow;

LiveData — παρατηρήσιμο δοχείο με ενσωματωμένη υποστήριξη Lifecycle: ο Observer ενεργοποιείται/απενεργοποιείται αυτόματα. StateFlow — αντιδραστική ροή από Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), μη συνδεδεμένο με Lifecycle, αλλά το υποστηρίζει μέσω stateIn(WhileSubscribed). Το StateFlow απαιτεί ρητή διαχείριση κύκλου ζωής στο View, αλλά παρέχει πρόσβαση σε coroutines, τελεστές Flow και πολλαπλές πλατφόρμες. Η Google συνιστά StateFlow για νέα έργα σε Kotlin, LiveData για κώδικα Java ή όταν απαιτείται συμβατότητα με παλιές βιβλιοθήκες.

Πώς μετατρέπεται το LiveData σε StateFlow;

Χρησιμοποιήστε τη συνάρτηση επέκτασης liveData.asFlow() από τη βιβλιοθήκη lifecycle-livedata-ktx. Δημιουργεί ένα Flow που εκπέμπει την τρέχουσα τιμή του LiveData σε κάθε αλλαγή. Στη συνέχεια μετατρέψτε σε StateFlow μέσω .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Αντίστροφη μετατροπή — stateFlow.asLiveData(). Η αμοιβαία μετατροπή επιτρέπει τη χρήση των πλεονεκτημάτων και των δύο βιβλιοθηκών σε ένα έργο.

Πότε το LiveData χάνει δεδομένα στο postValue;

Το postValue() χρησιμοποιεί AtomicReference για την αποθήκευση της καθυστερημένης τιμής. Εάν το postValue() κληθεί δύο φορές πριν από την επεξεργασία από το κύριο νήμα, η πρώτη τιμή θα αντικατασταθεί από τη δεύτερη — το Observable θα λάβει μόνο την τελευταία. Αυτό συμβαίνει επειδή το LiveData δεν έχει εσωτερική ουρά: αποθηκεύει μόνο μία καθυστερημένη τιμή. Για τη μετάδοση κάθε ενδιάμεσου σημείου (1%, 2%, … 100%) χρησιμοποιήστε setValue() στο κύριο νήμα ή ConflatedFlow από kotlinx-coroutines.

Μπορεί να χρησιμοποιηθεί το LiveData χωρίς LifecycleOwner;

Ναι, το LiveData μπορεί να παρατηρηθεί μέσω observeForever(), μεταβιβάζοντας Observer χωρίς LifecycleOwner. Ωστόσο, σε αυτήν την περίπτωση η διαγραφή πρέπει να είναι ρητή μέσω removeObserver() — η αυτόματη διαγραφή δεν λειτουργεί. Το observeForever() εφαρμόζεται σε υπηρεσίες, ContentProvider ή ViewModel όπου το LifecycleOwner δεν είναι διαθέσιμο. Σύμφωνα με τη σύσταση της Google, αποφύγετε το observeForever() σε Activity/Fragment — χρησιμοποιήστε observe() με LifecycleOwner.

Τι είναι το LiveData έκδοσης 1.0 (πάντα επίκαιρο);

Χαρακτηριστικό συμπεριφοράς: όταν το LiveData λαμβάνει έναν νέο ενεργό Observer, λαμβάνει αμέσως την τελευταία τιμή (εάν έχει οριστεί). Οι παλιές εκδόσεις του LiveData (πριν από lifecycle 2.5.0) παρέδιδαν την τιμή ακόμη και σε ανενεργούς συνδρομητές κατά τη μετάβαση σε ενεργή κατάσταση — αυτό διορθώθηκε. Στην τρέχουσα έκδοση, το LiveData λαμβάνει την τελευταία τιμή κατά τη μετάβαση ΑΠΟ ανενεργή ΣΕ ενεργή κατάσταση, απλοποιώντας την αρχικοποίηση οθονών.

Σύνοψη

  • LiveData — παρατηρήσιμο δοχείο δεδομένων με αυτόματη σύνδεση στο Lifecycle, εξαλείφοντας διαρροές μνήμης και crashes από παρωχημένες αναφορές.
  • MutableLiveData με setValue() (κύριο νήμα) και postValue() (νήμα παρασκηνίου) — το κύριο API για αλλαγή δεδομένων.
  • Μετασχηματισμοί map(), switchMap() και MediatorLiveData — λειτουργικές αλυσίδες χωρίς κώδικα boilerplate.
  • liveData builder liveData { } — προσέγγιση coroutine για δημιουργία ασύγχρονων LiveData με αυτόματη ακύρωση coroutine.
  • Room + LiveData — έτοιμος συνδυασμός για τοπική προσωρινή αποθήκευση χωρίς επιπλέον κώδικα σε DAO.
  • Το LiveData χρησιμοποιείται στο 74% των έργων Jetpack και παραμένει το πρότυπο για κώδικα Java και αρχιτεκτονικές legacy.
  • Για νέα έργα Kotlin, η Google συνιστά StateFlow, αλλά το LiveData παραμένει συμβατή λύση για υβριδικές στοίβες.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης