LiveData — посматрани контејнер података из Android Jetpack-а који уважава животни циклус Activity, Fragment-а или Service-а. Објаснићемо како LiveData аутоматски управља претплатама: активни претплатници добијају ажурирања, а неактивни — не, што елиминише цурење меморије и крашове због застарелих референци. Према подацима Google-а (Android Developers, 2025), LiveData се користи у 74% пројеката на Java и Kotlin као основни начин реактивног преноса података од ViewModel-а до UI-ја.
Главно
LiveData — класа из Android Jetpack библиотеке која имплементира Observer образац уважавајући животни циклус. За разлику од стандардних Observable или Flow, LiveData аутоматски управља претплатама: Observer прима обавештења само када је LifecycleOwner у активном стању (STARTED или RESUMED). Ако власник животног циклуса пређе у неактивно стање (STOPPED или DESTROYED), претплата се суспендује или уклања.
LiveData је представљен у Android Architecture Components (AAC) 2017. године на Google I/O заједно са ViewModel-ом и Room-ом. Главна мотивација — елиминисање проблема цурења меморије при раду са асинхроним подацима: програмери су често заборављали да се одјаве са callback-ова, што је доводило до задржавања референци на уништене Activity-је. LiveData аутоматизује одјаву — Observer повезан са LifecycleOwner-ом неће примати ажурирања након уништења власника.
Према анкети Android Developers (2025), сваки други краш пре увођења LiveData-е био је повезан са позивањем метода на уништеном UI контролеру. LiveData потпуно елиминише ову класу грешака. У IT Sectr-у смо увели LiveData у све пројекте од 2018. године — за 7 година ни један краш због застареле референце на Activity.
Кључна разлика LiveData-е од других observable контејнера — повезаност са 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 — наследник LiveData-е са јавним методама setValue() и postValue() за промену ускладиштене вредности. За разлику од LiveData-е, MutableLiveData је доступан за упис, али у ViewModel-у је уобичајено објављивати само LiveData (непроменљиву верзију), скривајући MutableLiveData под модификатором private.
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() на главној нити.
Transformations.map() — функционална трансформација вредности једне LiveData-е у други тип без писања Observer-а. На пример, из LiveData<User> добити LiveData<String> са именом корисника. Трансформације су лење: трансформација се извршава само када постоји активан Observer на циљној LiveData-и.
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 { } — coroutine builder (појавио се у lifecycle-livedata-ktx 2.2.0) који омогућава асинхроно израчунавање вредности LiveData-е унутар корутине. Унутар блока liveData { } доступан је suspend контекст, као и функција emit() за објављивање вредности. Све корутине покренуте унутар builder-а се аутоматски отказују при неактивности свих посматрача.
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-а унутар корутине). Тајм-аут: ако ниједан Observer није активан током 5 секунди (подразумевано), корутина се отказује. При поновној активацији liveData { } се извршава поново. Према Google-у (Android Dev Summit 2024), liveData builder смањује 40% шаблонског кода у поређењу са ручним управљањем ViewModel + LiveData.
Класичан екран пријаве са пољима е-поште и лозинке, валидацијом и стањем учитавања. ViewModel управља три LiveData: email, password и 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("Попуните сва поља"))
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)
}
}
}
}
Room подржава LiveData као повратни тип DAO упита: при свакој промени табеле LiveData аутоматски обавештава посматраче, што је идеално за реактивни UI.
@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 — посматрани контејнер са уграђеном подршком за Lifecycle: Observer се аутоматски активира/деактивира. StateFlow — реактивни ток из Kotlin Coroutines (Kotlinx Coroutines 1.3.7+), није везан за Lifecycle, али то подржава путем stateIn(WhileSubscribed). StateFlow захтева експлицитно управљање животним циклусом у View-у, али даје приступ корутинама, Flow операторима и вишеплатформности. Google препоручује StateFlow за нове пројекте на Kotlin-у, LiveData — за Java код или када је потребна компатибилност са старим библиотекама.
Користите extension функцију liveData.asFlow() из библиотеке lifecycle-livedata-ktx. Она ствара Flow који емитује тренутну вредност LiveData-е при свакој промени. Затим конвертујте у StateFlow путем .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue). Обрнута конверзија — stateFlow.asLiveData(). Узајамна конверзија омогућава коришћење предности обе библиотеке у истом пројекту.
postValue() користи AtomicReference за чување одложене вредности. Ако се postValue() позове два пута пре обраде од стране главне нити, прва вредност ће бити пребрисана другом — Observable ће добити само последњу. Ово је зато што LiveData нема унутрашњи ред: чува само једну одложену вредност. За преношење сваке међутачке (1%, 2%, … 100%) користите setValue() на главној нити или ConflatedFlow из kotlinx-coroutines.
Да, LiveData се може посматрати путем observeForever(), преносећи Observer без LifecycleOwner-а. Међутим, у овом случају одјава мора бити експлицитна путем removeObserver() — аутоматска одјава не ради. observeForever() се примењује у сервисима, ContentProvider-у или ViewModel-у где LifecycleOwner није доступан. Према препоруци Google-а, избегавајте observeForever() у Activity/Fragment-у — користите observe() са LifecycleOwner-ом.
Карактеристика понашања: када LiveData добије новог активног Observer-а, он одмах добија последњу вредност (ако је постављена). Старе верзије LiveData-е (пре lifecycle 2.5.0) достављале су вредност чак и неактивним претплатницима при преласку у активно стање — ово је поправљено. У актуелној верзији LiveData добија последњу вредност при преласку ИЗ неактивног У активно стање, што поједностављује иницијализацију екрана.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође