본문 바로가기
개발/Android

[Android] 여러 개의 LiveData를 한번에 핸들링하는 MediatorLiveData

by tempus 2022. 10. 13.
반응형

MediatorLiveData란?

LiveData의 하위 클래스로 다른 LiveData 객체를 Observe하고 해당 객체의 OnChanged 이벤트를 받습니다.

 

즉, 하나의 MediatorLiveData에 여러 개의 LiveData를 등록하고 한 번에 핸들링할 수 있습니다.

 

주요 함수로는

  1. addSource() : observe할 LiveData와 수행할 로직을 추가
  2. removeSource() : 추가된 LiveData 삭제

 

MediatorLiveData.java 파일을 살펴보면 

public class MediatorLiveData<T> extends MutableLiveData<T> {
    private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();

MediatorLiveData는 LiveData와 Source를 저장하는 Map을 가지고 있고 MutableLiveData를 상속받기 때문에 값이 변경이 가능합니다.

private static class Source<V> implements Observer<V> {
    final LiveData<V> mLiveData;
    final Observer<? super V> mObserver;
    int mVersion =START_VERSION;

    Source(LiveData<V> liveData, final Observer<? super V> observer) {
        mLiveData = liveData;
        mObserver = observer;
    }

    void plug() {
        mLiveData.observeForever(this);
    }

    void unplug() {
        mLiveData.removeObserver(this);
    }

    @Override
    public void onChanged(@Nullable V v) {
        if (mVersion != mLiveData.getVersion()) {
            mVersion = mLiveData.getVersion();
            mObserver.onChanged(v);
        }
    }
}

또한  MediatorLiveData.java에 있는 Source 클래스는 LiveData와 onChanged 추상 메서드가 포함된 Observer 인터페이스를 가지고 있습니다.

여기서 Source는 LiveData를 옵져빙하며 변화를 감지했을 때 전달받은 Observer의 onChanged를 호출한다는 것을 알 수 있습니다.

 

예시 - 로그인 기능 구현

MediatorLiveData를 사용하는 예시로 로그인 시에 아이디와 비밀번호의 입력에 따라 로그인 버튼을 활성화 여부를 MediatorLiveData로 만들어서 실시간으로 확인할 수 있습니다.

class UserViewModel : BaseViewModel() {

    private val _userId = MutableLiveData<String>()
    val userId: LiveData<String> = _userId

    private val _userPassword = MutableLiveData<String>()
    val userPassword: LiveData<String> = _userPassword

    private val _isPossibleLogin = MediatorLiveData<Boolean>()
    val isPossibleLogin: LiveData<Boolean> = _isPossibleLogin

    init {
        _isPossibleLogin.apply {
            addSource(_userId) {
                value = checkLogin()
            }
            addSource(_userPassword) {
                value = checkLogin()
            }
        }
    }

    private fun checkLogin(): Boolean {
        return !(userId.value.isNullOrBlank()) || !(userPassword.value.isNullOrBlank())
    }

}

추가적으로 확장 함수를 이용하면 추가되는 조건들도 쉽게 구현이 가능합니다. (addSourceList)

class UserViewModel : BaseViewModel() {

    private val _userId = MutableLiveData<String>()
    val userId: LiveData<String> = _userId

    private val _userPassword = MutableLiveData<String>()
    val userPassword: LiveData<String> = _userPassword

    private val _isPossibleLogin = MediatorLiveData<Boolean>()
    val isPossibleLogin: LiveData<Boolean> = _isPossibleLogin

    init {
        _isPossibleLogin.apply {
            addSourceList(_userPassword, _userId) {
                checkLogin()
            }
        }
    }

    private fun checkLogin(): Boolean {
        return !(userId.value.isNullOrBlank()) || !(userPassword.value.isNullOrBlank())
    }
    
    //확장 함수
    private fun <T> MediatorLiveData<T>.addSourceList(
        vararg liveDataArgument: MutableLiveData<*>,
        onChanged: () -> T
    ) {
        liveDataArgument.forEach {
            this.addSource(it) { value = onChanged() }
        }
    }

}

 

반응형

댓글


loading