서두

해당 포스트는 Android Jetpack 중 하나인 LiveData 에 대해서 정리한 것입니다.

LiveData

LiveData 는 Android MVVM 디자인 패턴을 구현하기 위해서 사용되는 컴포넌트입니다.

만약 Android MVVM 디자인 패턴을 구현 할 때에는 보통 아래와 같이 set로 묶어서 사용합니다.

그 중 LiveData는 Data 를 저장하고 관라하는 역할을 수행합니다.

LiveData 특징

  1. 관찰가능한 객체로 observer 를 등록하여 최신의 data를 수신을 할수 있습니다.
  2. LiveData 의 값을 단순히 변경만 함으로써 내부적으로 등록된 observer 에게 자동으로 notify 를 합니다.
  3. Data 가 변경되었을 시 모든 observer 에게 전달하는 것이 아니라 현재 활성 상태인 객체에 대해서만 전달이 됩니다. 이것이 가능한 이유는 LiveData 에 observer 를 등록할 때 lifecycleOwner 를 같이 넘겨주기 때문입니다.

LiveData 장점

  1. UI와 데이터 상태의 일치 보장 : LiveData 는 데이터가 변경이 되면 자동으로 observer에 notify 가 전달이 되고 notify를 받을 때 UI를 바로 업데이트가 가능합니다. 따라서 데이터와 UI 상태를 일치 시킬수 있습니다.
  2. 중지된 활동으로 인한 비정상 종료 없음 : 액티비티나 프래그먼트가 백그라운드에 있어 비활성화 상태일 경우 어떤 LiveData 이벤트를 받지 않습니다.
  3. 최신 데이터 유지 : 수명 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신합니다. 예를 들어 백그라운드에 있었던 액티비티는 포그라운드로 돌아온 직후 최신 데이터를 받습니다.
  4. 메모리 누수 없음 : 액티비티나 프래그먼트가 LiveData 를 옵저빙하고 있는 상태에서 생명 주기가 종료가 되면 자동으로 옵저빙을 해제를 합니다.

LiveData 생성

LiveData는 ViewModel class의 맴버 변수로 선언을 하여 View Model이 가지고 있도록 합니다.

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    // Rest of the ViewModel...
}

View Model 에서 LiveData를 저장하는 이유

  • 액티비티나 플래그먼트가 지나치게 커지니 않기 위해서 입니다. UI 컨트롤러가 데이터 표시를 담당하자만 데이터 상태를 보유하지 않습니다.
  • LiveData 인스턴스를 특정 액티비티나 프래그먼트 인스턴스의 생명주기와 분리하여 LiveData 인스턴스를 계속 유지하기 위해서입니다.

LiveData 값 변경

현재 메인 쓰레드(UI thread) 일 경우 setValue() 를 사용하고 Working 쓰레드 일 경우 postValue() 를 사용합니다.

참고로 postValue() 는 내부적으로 메인 쓰레드 핸들러로 메시지를 던지는 구조입니다.

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

LiveData 옵저빙

observe() 메소드를 이용하며 lifecycleOwner, Observer 인터페이스를 인자로 넘깁니다.

val viewModel = ViewModelProvider(this)[MyViewModel::class.java]

val counterTextView = findViewById<TextView>(R.id.counter)

viewModel.getCounter()
    .observe(this, { count ->
        counterTextView.text = "count : $count"
    })

Observer 콜백이 실행되는 쓰레드는 메인 쓰레드이기 때문에 바로 UI 업데이트가 가능합니다.

LiveData 는 액티비티나 프래그먼트의 lifecycleOwner 를 가지고 있으며 LiveData 가 변경된다고 무조건 옵저버에게 전달되는 것이 아니라 아래 코드와 같이 항상 상태를 체크하며 lifecycleOwner 의 상태가 START, RESUME 일 때에만 변경 사실이 notify가 됩니다.

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
 
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

LiveData 변환

Transformations 를 사용하여 LiveData에 저장된 값을 다른 값으로 변환하는 것이 가능합니다.

val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = Transformations.map(userLiveData) {
    user -> "${user.name} ${user.lastName}"
}

LiveData 병합

두 가지 이상의 LiveData를 결합하여 하나의 LiveData로 변경하고 싶은 경우 MediatorLiveData 를 사용합니다.

MediatorLiveData

LiveData 의 서브 클래스로써 한 개 이상의 liveData 를 관찰하고 관찰하고 있는 LiveData가 변경될 경우 등록한 onChange() 콜백이 호출이 되는 형식입니다.

 LiveData liveData1 = ...;
 LiveData liveData2 = ...;

 MediatorLiveData liveDataMerger = new MediatorLiveData<>();
 liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
 liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));

정리

LiveData 를 사용하는 목적은 Android Observer 패턴을 구현함에 있어서 개발 시간을 단축시킬수 있다는 장점이 있습니다.

만약 LiveData 를 사용하지 않는 경우 한 개의 data에 대해서 listener 를 여러 개를 등록/해제를 시킬수 있는 중간 관리자 객체가 필요하게 됩니다.

중간 관리자 객체는 listener callback이 메인 쓰레드에서 호출이 될 수 있도록 쓰레드 스위칭까지 고려해야 합나디.

data를 관찰하는 액티비티나 프래그먼트는 메모리 누수가 발생하지 않도록 하기 위해 생명 주기가 종료되기 전에 반드시 listener를 해제를 해야 합니다.

따라서 LiveData 를 사용함으로써 위에 언급된 동작을 개발자가 따로 고려하지 않아도 된다는 장점을 제공합니다.

Reference

LiveData 개요

댓글남기기