공식문서 최상단에 다음과 같이 적혀있습니다.
LiveData는 관찰 가능한 데이터 홀더 클래스입니다. 관찰 가능한 일반 클래스와 달리 LiveData는 수명 주기를 인식합니다
이게 사실 라이브데이터의 전부입니다.
1. 홀더 클래스이다 -> 다른 객체를 감쌉니다.
2. 관찰가능하다 -> 감싼 객체가 변경되면 관찰자에게 통보합니다.
3. 생명주기를 인식한다 -> 액티비티(프래그먼트)가 사라지면 같이 사라지고, 생명주기 이벤트에 따라 처리를 해줍니다.
class LiveData<T>
위에서 이야기했듯 livedata
는 객체를 감싸서 가집니다.
이때 객체를 저장하고, 해당 객체를 또 옵저버들에게 전파해야하기에 타입이 제네릭으로 되어있습니다.
<T>
처음 생성시 아래와 같이 타입을 지정해줍니다.
val state = LiveData<String>()
라이브데이터를 생성하는 방법은 두가지 인데 하나는 위와 같이 그냥 생성하는 방법이고 하나는 초기값을 지정해주는 방식입니다.
val state = LiveData<String>("hello")
//또는 타입추론이 가능한 경우 생략
val state = LiveData("hello")
//hello가 String임이 분명하기에 생략가능.
이렇게 생성자로 전달한 값은 내부에서 mData
라는 곳에 저장됩니다.
//LiveData의 생성자.
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
이때 보시면 mVersion
이라는 것이 있습니다.
이 mVersion
은 mData
를 변경할때마다 1씩 증가하는데
외부에서 사용할 수 없고 내부적으로 사용하는 것 같습니다.
LiveData의 값변경에는 두가지 방법이 있습니다.
1. postValue
2. setValue
이 두가지의 사용법은 다음과 같습니다.
val state = MutableLiveData("hello")
state.value = "안녕" // setValue
state.postValue("안녕") // postValue
둘 다 값이 변경되고 옵저버들에게 전달되는것은 같지만 큰 차이가 있습니다.
setValue
는 그냥 말그대로 value를 세팅합니다.
메인쓰레드에서 돌아가고, 메인쓰레드가 아니라면 오류가 생깁니다.
전달도 동기적으로 바로 iterator로 돌면서 전달합니다.
- setValue 호출
- 데이터 변경
- 버전변경
- 옵저버들을 탐색
- 만약 옵저버가 활성상태이고, 마지막으로 확인한 라이브데이터 버전이 현재 버전보다 이전이라면
- 현재 데이터를 전파.
postValue
는 백그라운드에서 value를 세팅합니다.
내부에서 최종적으로 setValue
를 호출하지만 만약 setValue
가 호출되기 이전에 postValue
가 여러번 호출된다면 마지막으로 호출한 postValue
의 값이 저장됩니다.
- postValue 호출
- Lock걸고 mPendingData 변경
- 메인쓰레드로 변경 해서 setValue 호출 준비
- 만약 postValue에서 Lock이 걸려있다면 기다림
- 이 도중에 postValue가 또 호출되면 mPendingData 변경
- 만약 postValue가 호출되지 않아 Lock이 풀리면 setValue호출됨
즉 자신이 메인쓰레드가 아닌 곳에서 값을 바꿔야 한다면 postValue
를 쓰면됩니다.
단, postValue
는 위와 같이 조금 딜레이가 있게 세팅되기에 값을 보내자 마자 value로 읽으면 다른 값이 보일 수 있습니다.
그런데 보시면 위에서 setValue
예시를 보일때 LiveData
가 아닌 MutableLiveData
를 씁니다.
즉 LiveData
자체는 내부의 값을 변경할 수 없습니다.
정확히는 setValue
와 postValue
가 LiveData
내부에 이미 있지만 접근제한자가 protected라 접근 할 수 없습니다.
그래서 또 다른 클래스인 MutableLiveData
를 만들었습니다.
해당 클래스는 LiveData
를 상속받습니다.
public class MutableLiveData<T> extends LiveData<T> {
이 MutableLiveData
는 LiveData
와 같은 클래스에 있기에 LiveData
의 setValue
, postValue
에 접근 할 수 있습니다.
그렇기에 우린 mutableLiveData
를 만들어야만 setValue
, postValue
를 쓸 수 있습니다.
하지만 이런 생각이 들 수 있습니다.
변경을 전파하는게 핵심인
LiveData
에서 value변경을 막아두면LiveData
를 어디에 써?
물론 이 자체로는 뭘 할 수 없지만 MutableLiveData
와 연계해서 캡술화를 할 수 있습니다.
private val _state = MutableLiveData<String>()
val state : LiveData<String>()
get() = _state
이렇게 하면 이 state가 선언된 곳에서는 _state에 접근해 값을 바꿀 수 있고,
외부에선 state에 접근해 옵저버를 등록할 순 있지만 값을 못바꾸게 할 수 있습니다.
즉 LiveData
는 캡슐화를 위해 존재하게 됩니다.
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
위는 observe
라는 함수의 선언부입니다.
여기에 매개변수로 들어가는 Observer
란 클래스는 onChanged
란 함수 하나로 이루어진 클래스이기에 익명함수로 빠지고, 축약되어 아래와 같이 적을 수 있습니다.
라이브데이터는 오로지 활성상태(STARTED
,RESUMED
)인 옵저버에게만 변경을 전파합니다.
그래서 라이프 사이클을 알아야하기에 처음에 LifeCycleOwner를 등록합니다.
만약 해당 옵저버를 등록할때 넣은 owner
의 라이프사이클이 destroyed
라면 라이브데이터가 이를 가지는 것은 위험해집니다.(메모리릭)
그렇기에 destroyed
되면 옵저버 목록에서도 제거해줍니다.
또한 비슷하게 화면이 활성화되면 무조건 최신값을 한번 받아오도록 되어있어 화면회전, 백스택에있을때 바뀐 값등을 받아오는데 유용합니다.
아무튼 이렇게 등록한 Observer
는 내부적으로 ObserverWrapper
(마지막으로 받은 라이브데이터의 버전 등 필요한 기능이 들어있음)으로 한번 감싸져 LiveData
에 저장됩니다.
마지막으로 라이브데이터는 값이 변경되었을때만 옵저버들에게 전파하기에 그 값 내부의 무언가가 변경되는 것은 감지하지 못합니다.
대표적으로 LiveData
안에 ArrayList
가 들어가는 경우, ArrayList
에 Add
를 통해 원소를 넣어도 옵저버들에게 ArrayList
가 전달되지는 않습니다.
이런 경우에는 ArrayList
자체를 따로 관리해서 Add
할 때마다 다시 리스트 자체를 setValue
하거나 하는 방식으로 값 자체를 바꿔야합니다.
참조 자료
https://developer.android.com/topic/libraries/architecture/livedata?hl=ko
wow 잘 읽었습니다!!