ANDROID/Android 개발 이슈 & 해결

[Android] LiveData를 유연하게 사용하는 방법 (feat.Transformations)

주 녕 2022. 3. 7. 04:48
728x90

이번 프로젝트를 하면서 LiveData와 Room을 연결하는 작업을 하던 도중,

사용자가 버튼으로 날짜를 바꾸면 바뀐 날짜에 대한 데이터 리스트를 Room에서 받아오고, 그 결과값을 LiveData에 넣어야 했다.

지금까지 알기로는 LiveData의 값은 직접 변경할 수 없고 MutableLiveData를 통해 변경한 값을 가져올 수 있었는데,

이 경우에는 LiveData의 값을 직접 바꿔야 한다는 문제에 봉착했다.

 

 

[Android/Jetpack] AAC - LiveData

LiveData 식별 가능한(Observable) 데이터 홀더 클래스 LiveData는 Activity, Fragment, Service 등 다른 앱 구성요소의 수명 주기를 고려(Lifecycle-aware)한다. 수명 주기 인식을 통해 LiveData는 활성(active)..

junyoung-developer.tistory.com

*LiveData에 대해 공부했던 포스팅

 

Transformations 사용하기

Transformations는 LiveData를 위한 클래스로, 단어의 뜻 그대로 LiveData를 변형하는 것이다.

이를 위해서, MediatorLiveData가 Transformations 클래스의 아래 메서드와 함께 사용된다.

  • Transformations.map()
  • Transformations.switchMap()

 

MediatorLiveData

일대다 종속성

    val liveData1: LiveData = ...
    val liveData2: LiveData = ...

    val result = MediatorLiveData()

    result.addSource(liveData1) { value ->
        result.setValue(value)
    }
    result.addSource(liveData2) { value ->
        result.setValue(value)
    }

1개 이상의 data source를 하나의 LiveData observer에 추가할 수 있음

  • 추가된 source 중 일부라도 변경되면 결과를 업데이트 함
  • 변화에 대한 알림을 처리하지만, source로 들어간 데이터가 자동으로 결합되지는 않음
  • 예시 설명 : liveData1이나 liveData2에 변경이 있을 시, result 업데이트

관련 메서드

  • addSource(source1: LiveData<>) : 관찰할 Livedata 추가

 

Transformations.map()

LiveData<Y> map (LiveData<X> source, Function<X, Y> func)
  • LiveData<X> 타입 데이터를 LiveData<Y> 타입 데이터로 변환함
  • func에서는 Y 타입의 객체를 리턴

→ 데이터 변환을 처리하는 함수에서 Y 타입을 리턴

→ map 내부에서 LiveData<Y> 타입의 객체를 리턴

 

일대일 정적 변환

데이터의 변화가 있을 때 즉각적으로 변환만 하면 되는 경우, map()을 사용할 수 있음

    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }
  • source : 결과값를 변형시킬 LiveData 인자
  • mapFunction : source로 넘겨준 LiveData의 value 타입과 같음
  • source를 observing해서 값이 바뀔 때마다 result의 값이 변화함
  • 변화된 result를 반환 (source로 넘겨준 LiveData의 value 타입과 같음)

 

Transformations.switchMap()

LiveData<Y> switchMap (LiveData<X> trigger, Function<X, LiveData<Y>> func)
  • LiveData<X> 타입 데이터를 LiveData<Y> 타입 데이터로 변환
  • func에서는 LiveData<Y> 타입 객체를 리턴

→ 데이터 변환을 처리하는 함수에서 LiveData<Y>를 리턴

→ switchMap에서 LiveData<Y> 타입의 객체를 리턴

 

일대일 동적 변환

즉각적인 데이터 변환이 어려워 초기화 구간에서는 사용할 수 없지만, 그 후에 사용해도 된다면 switchMap()

    @MainThread
    @NonNull
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
  • source를 observing해서 값이 바뀔때마다 result 값이 변화함
  • 변화된 result를 반환 

 


 

내가 적용한 코드

calDate라는 캘린더의 날짜를 getAllDoneInDate라는 Room에서 해당 날짜에 해당하는 데이터 리스트를 받아오는 메서드의 매개변수로 넣어야 했다. 또한 해당 메서드의 결과 값은 변화된 날짜에 따라 실시간으로 바뀌는 데이터를 보여주어야 하기 때문에 LiveData로 받아와야 했다. 

    var calDate: MutableLiveData<String> = MutableLiveData("")  // YYYY-MM-DD
    
    var doneList: LiveData<List<Done>> = Transformations.switchMap(calDate) {
        dbRepo.getAllDoneInDate(it).asLiveData()
    }

→ 데이터 변환을 처리하는 함수에서 LiveData를 리턴하고 있기 때문에 switchMap()을 사용함

 


reference >

 

 

 

 

728x90