LiveData — egy megfigyelhető adattároló az Android Jetpackből, amely figyelembe veszi az Activity, Fragment vagy Service életciklusát. Megvizsgáljuk, hogyan kezeli a LiveData automatikusan a feliratkozásokat: az aktív feliratkozók frissítéseket kapnak, a nem aktívak pedig nem, ami megszünteti a memóriaszivárgásokat és az elavult referenciák okozta összeomlásokat. A Google adatai szerint (Android Developers, 2025) a LiveData a Java és Kotlin projektek 74%-ában használatos a ViewModel-től az UI-ig tartó reaktív adatátvitel fő módjaként.
Főbb pontok
LiveData — egy osztály az Android Jetpack könyvtárból, amely az Observer mintát valósítja meg az életciklus figyelembevételével. A szabványos Observable vagy Flow-tól eltérően a LiveData automatikusan kezeli a feliratkozásokat: az Observer csak akkor kap értesítéseket, amikor a LifecycleOwner aktív állapotban van (STARTED vagy RESUMED). Ha az életciklus tulajdonosa inaktív állapotba kerül (STOPPED vagy DESTROYED), a feliratkozás felfüggesztésre vagy törlésre kerül.
A LiveData-t az Android Architecture Components (AAC) részeként 2017-ben mutatták be a Google I/O-n a ViewModel és a Room mellett. A fő motiváció — a memóriaszivárgás problémájának megszüntetése aszinkron adatokkal való munka során: a fejlesztők gyakran elfelejtettek leiratkozni a callback-ekről, ami a megsemmisített Activity-kre való referenciák megtartásához vezetett. A LiveData automatizálja a leiratkozást — a LifecycleOwner-hez kapcsolt Observer nem kap frissítéseket a tulajdonos megsemmisítése után.
Az Android Developers (2025) felmérése szerint a LiveData bevezetése előtt minden második összeomlás egy megsemmisített UI-vezérlőn lévő metódusok meghívásával volt kapcsolatos. A LiveData teljesen megszünteti ezt a hibakategóriát. Az IT Sectr-nél 2018 óta vezettük be a LiveData-t minden projektbe — 7 év alatt egyetlen összeomlás sem történt elavult Activity-referencia miatt.
A LiveData legfőbb különbsége más megfigyelhető tárolókhoz képest — a Lifecycle-hez való kapcsolódás. A megfigyelő létrehozásakor a LiveData ellenőrzi a LifecycleOwner állapotát: ha az állapot STARTED vagy RESUMED, az Observer aktívnak minősül és azonnal frissítéseket kap. Ha az állapot PAUSED, STOPPED vagy DESTROYED, a frissítések nem kerülnek kézbesítésre az aktív állapotba való visszatérésig.
A mechanizmus a LifecycleBoundObserver osztályon keresztül valósul meg, amely addObserver() segítségével regisztrál a Lifecycle-ben. Amikor a LifecycleOwner állapotot vált, a onStateChanged() callback aktiválódik, és a LiveData frissíti az Observer aktivitási állapotát. Az adatok setValue()-n keresztüli beállításakor a LiveData végiglépked a megfigyelők listáján, és csak az aktívaknak kézbesíti az értéket. Amikor a megfigyelő DESTROYED állapotba kerül, az Observer automatikusan eltávolításra kerül a feliratkozók listájáról.
Az Android Jetpack dokumentációja szerint (2025) a LifecycleBoundObserver mechanizmus kevesebb mint 0,5 µs-t fogyaszt az állapotellenőrzésre — a többletterhelés elhanyagolható egy tipikus UI-frissítési művelethez képest. Ez alkalmassá teszi a LiveData-t nagy frekvenciájú frissítésekhez (időzítők, számlálók) a teljesítménycsökkenés kockázata nélkül.
MutableLiveData — a LiveData leszármazottja nyilvános setValue() és postValue() metódusokkal a tárolt érték módosításához. A LiveData-tól eltérően a MutableLiveData írásra is elérhető, de a ViewModel-ben szokás csak a LiveData-t (a nem módosítható verziót) publikálni, a MutableLiveData-t pedig a private módosító alatt elrejteni.
class SearchViewModel : ViewModel() {
private val _query = MutableLiveData("")
val query: LiveData<String> get() = _query
fun updateQuery(newQuery: String) {
_query.value = newQuery // setValue() — a fő szálon
}
fun updateFromNetwork(result: String) {
_query.postValue(result) // postValue() — bármely szálról
}
}
setValue() csak a fő szálból (main thread) hívható — azonnal értesíti a megfigyelőket. postValue() biztonságosan hívható háttérszálból: az értéket a fő szál sorába helyezi, és aszinkron módon értesíti a megfigyelőket. Fontos: ha a postValue() kétszer egymás után kerül meghívásra az első feldolgozása előtt, a köztes érték elveszhet — a megfigyelők csak az utolsót kapják meg. Az összes köztes állapot (pl. betöltési folyamat) átviteléhez használja a setValue()-t a fő szálon.
Transformations.map() — egy LiveData értékének funkcionális átalakítása másik típusba Observer írása nélkül. Például LiveData<User>-ből LiveData<String> előállítása a felhasználó nevével. A transzformációk lusták: az átalakítás csak akkor hajtódik végre, ha van aktív Observer a cél LiveData-n.
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 — két forrás egyesítése
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() — a flatMap megfelelője a reaktív folyamok világából: a bemeneti LiveData változásakor átvált a kimeneti LiveData új példányára. MediatorLiveData — fejlett eszköz több LiveData forrás egyesítésére a frissítési prioritás kezelésének lehetőségével. A Developer Survey (2024) szerint a MediatorLiveData a projektek 35%-ában használatos, ahol különböző forrásokból történő adataggregáció szükséges — például UI űrlapadatok és szerverválasz egyesítése.
liveData { } — coroutine builder (a lifecycle-livedata-ktx 2.2.0-ban jelent meg), amely lehetővé teszi a LiveData értékének aszinkron kiszámítását egy korutinon belül. A liveData { } blokkon belül elérhető a suspend kontextus, valamint az emit() függvény az értékek publikálásához. A builder-en belül elindított összes korutin automatikusan törlődik, amikor az összes megfigyelő inaktívvá válik.
val userLiveData: LiveData<User> = liveData {
// Alapértelmezetten Dispatchers.IO-n fut
val user = userRepository.fetchUser(userId)
// Kibocsátjuk az eredményt — automatikusan a fő szálon
emit(user)
}
val progressLiveData: LiveData<Int> = liveData {
for (i in 0..100) {
emit(i)
delay(50)
}
}
A liveData builder támogatja az emitSource()-t — egy másik LiveData forrásként történő kibocsátását (a switchMap megfelelője korutinon belül). Időtúllépés: ha egyetlen Observer sem aktív 5 másodpercig (alapértelmezett), a korutin törlődik. Újraaktiváláskor a liveData { } újra végrehajtódik. A Google szerint (Android Dev Summit 2024) a liveData builder 40%-kal csökkenti a sablonkódot a kézi ViewModel + LiveData kezeléshez képest.
Klasszikus bejelentkezési képernyő e-mail és jelszó mezőkkel, validációval és betöltési állapottal. A ViewModel három LiveData-t kezel: email, password és 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("Töltse ki az összes mezőt"))
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)
}
}
}
}
A Room támogatja a LiveData-t a DAO lekérdezés visszatérési típusaként: minden táblamódosításkor a LiveData automatikusan értesíti a megfigyelőket, ami ideális a reaktív UI-hoz.
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE completed = 0")
fun getActiveTasks(): LiveData<List<Task>>
@Insert
suspend fun insertTask(task: Task)
}
// A ViewModel-ben:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
private val dao = AppDatabase.getDatabase(application).taskDao()
val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}
A Room olyan kódot generál, amely figyeli a tasks tábla változásait és automatikusan frissíti a LiveData-t minden INSERT, UPDATE vagy DELETE esetén. Ez további kód nélkül működik — csak a @Query annotáció szükséges LiveData visszatérési típussal. Az IT Sectr-nél 2019 óta használjuk a Room + LiveData-t szabványos stack-ként a helyi adatgyorsítótárazáshoz Android projektekben.
Gyakran ismételt kérdések
LiveData — megfigyelhető tároló beépített Lifecycle-támogatással: az Observer automatikusan aktiválódik/deaktiválódik. StateFlow — reaktív folyam a Kotlin Coroutines-ből (Kotlinx Coroutines 1.3.7+), nem kapcsolódik a Lifecycle-hez, de ezt a stateIn(WhileSubscribed) segítségével támogatja. A StateFlow explicit életciklus-kezelést igényel a View-ban, de hozzáférést biztosít a korutinokhoz, Flow operátorokhoz és többplatformos fejlesztéshez. A Google a StateFlow-t ajánlja új Kotlin projektekhez, a LiveData-t Java kódhoz vagy ha kompatibilitás szükséges régi könyvtárakkal.
Használja a liveData.asFlow() kiterjesztő függvényt a lifecycle-livedata-ktx könyvtárból. Ez létrehoz egy Flow-t, amely minden változáskor kibocsátja a LiveData aktuális értékét. Ezután alakítsa át StateFlow-vá a .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue) segítségével. Fordított átalakítás — stateFlow.asLiveData(). A kölcsönös átalakítás lehetővé teszi mindkét könyvtár előnyeinek kihasználását egyetlen projektben.
A postValue() AtomicReference-t használ a késleltetett érték tárolásához. Ha a postValue() kétszer kerül meghívásra a fő szál általi feldolgozás előtt, az első érték felülíródik a másodikkal — az Observable csak az utolsót kapja meg. Ez azért van, mert a LiveData-nak nincs belső sora: csak egy késleltetett értéket tárol. Minden köztes pont (1%, 2%, … 100%) átviteléhez használja a setValue()-t a fő szálon vagy a ConflatedFlow-t a kotlinx-coroutines-ből.
Igen, a LiveData megfigyelhető a observeForever() segítségével, Observer átadásával LifecycleOwner nélkül. Ebben az esetben azonban a leiratkozásnak explicitnek kell lennie a removeObserver()-n keresztül — az automatikus leiratkozás nem működik. Az observeForever() olyan szolgáltatásokban, ContentProvider-ben vagy ViewModel-ben alkalmazható, ahol a LifecycleOwner nem elérhető. A Google ajánlása szerint kerülje az observeForever() használatát Activity/Fragment-ben — használja az observe()-t LifecycleOwner-rel.
Viselkedési jellemző: amikor a LiveData új aktív Observer-t kap, az azonnal megkapja az utolsó értéket (ha be van állítva). A LiveData régi verziói (a lifecycle 2.5.0 előtt) az értéket még az inaktív feliratkozóknak is kézbesítették az aktív állapotba való átmenetkor — ezt kijavították. A jelenlegi verzióban a LiveData az utolsó értéket az inaktívból aktív állapotba való ÁTMENETkor kapja, ami leegyszerűsíti a képernyők inicializálását.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is