LiveData
- LiveData는 구독 시 Android Lifecycle을 따르도록 구현되어 있다.
- LiveData는 항상 최신 데이터만을 보증한다.
이유는 위에서 보았던 setValue를 보면 알 수 있다.
setValue는 어떠한 처리도 없는 단순 변수이다. 변수는 항상 마지막 데이터만을 들고 있을 수 있다.
- LiveData는 항상 MainThread에서 사용할 값을 보증한다. (setValue를 하든 postValue를 하든)
0. 설정
def lifecycle_version = "2.3.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
Q. ViewModel 함께 선언된 이유?
ㄴ> Re : ViewModel은 보통 LiveData와 같이 사용하기 때문에 추가 기재함
- LiveData 버전 정보는 공식문서에서 확인
1. 선언 (ViewModel에)
ㄴ> 위치 : 일반적으로 ViewModel 클래스 내에서 이루어집니다.
class LoadDetailViewModel : ViewModel() {
private val _liveData = MutableLiveData<String>()
val liveData: LiveData<String> get() = _liveData
fun loadData() = viewModelScope.launch {
_liveData.value = "value"
_liveData.postValue("value")
}
}
ㄴ> 보통 MutableLiveData 변수명 앞에는 _를 붙이고, LiveData는 붙이지 않는다.
ㄴ> Q. MutableLiveData와 LiveData를 구분해서 사용하는 이유?
ㅡ> Re: ViewModel과 View의 역할을 분리하기 위함
ViewModel은 언제나 새로운 값의 변경이 일어나고, 다시 읽을 수 있는 형태로 사용하는 것이고,
View는 값의 입력이 아닌 읽기만을 허용하는 것이다.
그렇다고 해서 항상 View에서는 LiveData만 봐야 하는 것은 아니다. 필요에 따라 View에서도 값의 변경이 일어나야 하는 케이스라면 허용할 수 있는데, checkBox의 값을 변경하는 케이스라면 가능하다.
상황에 잘 맞게 써야 한다는 말이다.
ㄴ> LiveData값 할당하기
set(위에서 사용)과 postValue 2 가지 방식으로 값을 주입한다.
- set (=setValue) : MainThread(UI)가 보장될 경우에는 set을 활용한다.
- postValue : MainThread가 아닌 IO 스케쥴러를 활용하는 경우 postValue를 활용한다.
2. 사용할 observe 등록
ㄴ> 위치 : 일반적으로 Activity나 프래그먼트와 같은 UI 컨트롤러에Observer객체를 연결합니다.
// kotlin 1.4.x 버전부터는 SAM 지원으로 아래와 같다.
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
loadDetailViewModel.liveData.observe(this) {
// View 처리
}
ㄴ> observe 안에다가 데이터의 변경이 이루어질 때마다 하고 싶은 작업을 적어주면 된다.
ㄴ> 변경이 이루어질때마다 반복적으로 저 안에 코드가 실행될 것이다.
ㄴ> observe의 파라미터로 넘겨주는 LifecycleOwner
- Observer 클래스로 표현되는 관찰자의 수명 주기가 STARTED 또는 RESUMED 상태이면 LiveData는 관찰자를 활성 상태이며, 활성 관찰자에게만 업데이트 정보를 알립니다.
- 액티비티 같은 경우는 this 키워드를 이용해 현재 액티비티를 지정해주면 되는데 프래그먼트 같은 경우는 this를 사용하면 안 된다.
그 이유
3줄 요약해보자면
1. 기존의 프래그먼트 생명주기를 사용하면 복수의 Observer가 호출될 가능성이 있다.
2. 구글이 실수한 부분이고, 이를 개선하기 위해 새로운 프래그먼트 생명주기가 도입되었다.
3. this 대신에 viewLifecycleOwner를 사용하면 된다.
- LifecycleOwner을 어떤 것을 넘겨주는지에 따라 다른데,
Activity는 하나뿐이지만, Fragment에서는 2개의 LifecycleOwner을 가지고 있다.
- lifeCycleOwner와 상관없이 항상 알림을 받을 수 있는 방법
- 바로 observerForever 메서드이다.
이 메서드를 사용하면 lifeCycleOwner가 없어도 관찰자를 생성하고 LiveData와 연결할 수 있으며
UI 컨트롤러의 생명주기와 상관없이 항상 알림을 받을 수 있는 상태가 된다.
- ex)
// 일반적인 Observer 생성
model.getAll().observe(this, Observer{ notice ->
})
// observerForever를 통한 생성
model.getAll().observeForever(Observer{ notice ->
})
[ LiveData를 사용하면 좋지 않은 케이스 ]
: UI를 업데이트하지 않는데 LiveData를 활용하는 경우 (∵ LiveData는 항상 UI를 처리하는 MainThread로 값을 처리하므로)
- ViewModel보다 더 안쪽인 Repository(UseCase)와 같이 내부에서 LiveData로 값을 가져오는 경우
- View 업데이트가 없는 코드에서 LiveData를 활용하는 경우
- 데이터의 처리가 IO에서 발생해야 하는 경우라면 LiveData 활용은 맞지 않는 것이다. 이 경우라면 Thread를 활용하거나, RxJava, Coroutines을 활용해 처리해야 한다.
- LiveData로 어떠한 값을 가공해야 한다면 맞지 않다. 단순히 상태 값을 저장하더라도 항상 MainThread 임을 기억해야 한다.
이는 문제일 수 있는데, Clean Architecture로 작성한다면 LiveData를 쓰기보단 RxJava나 Flow로 데이터를 처리하고, AAC-ViewModel에서 LiveData로 값을 다시 전달하는 게 좋다.
+) LiveData를 DataBinding으로 사용 시 매우 흔한 오류를 경험할 수 있다.
(LiveData는 상태를 보관하고, 이를 Android Lifecycle에 따라 데이터를 받을 수 있도록 만들어져있다.)
1. AActivity에서 버튼 onClickEvent를 받아, LiveData를 이용 BActivity로 화면 전환을 시도한다.
2. BActivity에서 작업을 마친 후 다시 AActivity로 Back 한다.
3. AActivity의 LiveData는 마지막 이벤트인 버튼의 onClickEvent 정보를 보관하고 이를 onResume/onStart 상태에 흘려보내기 때문에 다시 BActivity로 자동 이동시킨다.
4. 자동으로 무한 반복 시키는 것이다.
=> 그래서 돌아다니는 솔루션으로 SingleLiveEvent라는 처리를 사용하도록 한다.
Lifecycle에 따라 동작하도록 만들어진 LiveData의 observe의 처리를 강제로 null의 상태를 추가해 데이터를 흐르지 않도록 만들거나, 별도의 Boolean 값을 두고 흐르지 않도록 하는 것이다.
값을 입력하는 것보단 언제 써지며, 어떠한 타이밍에 따라 값을 불러오고, 가져오는 케이스를 아는 것이 더 중요하다.
[출처]
- https://todaycode.tistory.com/49
- https://thdev.tech/android/2021/02/01/LiveData-Intro/
- https://thdev.tech/android/2021/10/12/Android-LiveData-Observable/