[Android] LiveData 자세히 봐보기

K_Gs·2023년 5월 21일
9
post-thumbnail

LivaData

공식문서 최상단에 다음과 같이 적혀있습니다.

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이라는 것이 있습니다.
mVersionmData를 변경할때마다 1씩 증가하는데
외부에서 사용할 수 없고 내부적으로 사용하는 것 같습니다.

값 변경

LiveData의 값변경에는 두가지 방법이 있습니다.
1. postValue
2. setValue

이 두가지의 사용법은 다음과 같습니다.

val state = MutableLiveData("hello")


state.value = "안녕" // setValue
state.postValue("안녕") // postValue

둘 다 값이 변경되고 옵저버들에게 전달되는것은 같지만 큰 차이가 있습니다.

setValue는 그냥 말그대로 value를 세팅합니다.
메인쓰레드에서 돌아가고, 메인쓰레드가 아니라면 오류가 생깁니다.

전달도 동기적으로 바로 iterator로 돌면서 전달합니다.

  1. setValue 호출
  2. 데이터 변경
  3. 버전변경
  4. 옵저버들을 탐색
  5. 만약 옵저버가 활성상태이고, 마지막으로 확인한 라이브데이터 버전이 현재 버전보다 이전이라면
  6. 현재 데이터를 전파.

postValue백그라운드에서 value를 세팅합니다.

내부에서 최종적으로 setValue를 호출하지만 만약 setValue가 호출되기 이전에 postValue가 여러번 호출된다면 마지막으로 호출한 postValue의 값이 저장됩니다.

  1. postValue 호출
  2. Lock걸고 mPendingData 변경
  3. 메인쓰레드로 변경 해서 setValue 호출 준비
  4. 만약 postValue에서 Lock이 걸려있다면 기다림
  5. 이 도중에 postValue가 또 호출되면 mPendingData 변경
  6. 만약 postValue가 호출되지 않아 Lock이 풀리면 setValue호출

즉 자신이 메인쓰레드가 아닌 곳에서 값을 바꿔야 한다면 postValue를 쓰면됩니다.
단, postValue는 위와 같이 조금 딜레이가 있게 세팅되기에 값을 보내자 마자 value로 읽으면 다른 값이 보일 수 있습니다.

MutableLiveData

그런데 보시면 위에서 setValue 예시를 보일때 LiveData가 아닌 MutableLiveData를 씁니다.

LiveData 자체는 내부의 값을 변경할 수 없습니다.
정확히는 setValuepostValueLiveData 내부에 이미 있지만 접근제한자가 protected라 접근 할 수 없습니다.

그래서 또 다른 클래스인 MutableLiveData를 만들었습니다.
해당 클래스는 LiveData를 상속받습니다.

public class MutableLiveData<T> extends LiveData<T> {

MutableLiveDataLiveData와 같은 클래스에 있기에 LiveDatasetValue, 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가 들어가는 경우, ArrayListAdd를 통해 원소를 넣어도 옵저버들에게 ArrayList가 전달되지는 않습니다.

이런 경우에는 ArrayList자체를 따로 관리해서 Add할 때마다 다시 리스트 자체를 setValue하거나 하는 방식으로 값 자체를 바꿔야합니다.


참조 자료
https://developer.android.com/topic/libraries/architecture/livedata?hl=ko

profile
~(~o~)~

8개의 댓글

comment-user-thumbnail
2023년 5월 22일

wow 잘 읽었습니다!!

1개의 답글
comment-user-thumbnail
2023년 5월 22일

오늘 스터디 기대할게요~!

1개의 답글
comment-user-thumbnail
2023년 5월 22일

정말 기대되네요 근성쌤!

1개의 답글
comment-user-thumbnail
2023년 5월 22일

잘 읽었어요~~~!!😊

1개의 답글