AAC lifecycle컴포넌트의 등장과 함께 MVVM패턴이 다소 변경되었습니다.
기존의 방식은 다음 그림과 같습니다.

Activity와 ViewModel의 의존성을 분리하고 ViewModel은 Navigator를 이용하여 Activity에게 이벤트를 전달합니다. Navigator의 구현체는 보통 Activity가 되곤했었습니다.

BaseObservable을 상속받아 ViewModel을 만들면 데이터바인딩 라이브러리와 궁합도 잘맞습니다.

그런데, AAC ViewModel을 사용하고 부터는 이 패턴을 사용할 수 없게 되었습니다.

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

공식문서에 나와있는 ViewModel 사용에 대한 제약사항을 확인해보면, ViewModel은 View, Lifecycle 또는 액티비티 컨텍스트를 참조하는 어떤 클래스도 참조하지 말라고 경고 하고 있습니다.

액티비티가 재생성되는 경우 AAC ViewModel에 의해 이전 액티비티의 레퍼런스 카운트가 유지 됨으로써 메모리 누수가 발생할 수 있기 때문이죠. 즉, Navigator(Activity)객체가 AAC ViewModel에 의해 참조되면 메모리 누수가 발생하여 이전 패턴을 사용할수가 없습니다.

그래서 구글 샘플의 android-architecture 샘플을 살펴보면 이 문제를 이런방식으로 풀었습니다.

ViewModel에서 어떤 이벤트가 발생할때 LiveData의 value를 변경함으로써 state의 변화를 줘 Activity가 이벤트를 감지 할 수 있게 되는것이죠. 액티비티를 참조하는 Navigator같은 객체도 없으므로 메모리누수도 발생하지 않습니다. 그러면 이벤트를 발생시킬때마다 어떤 value를 주입해야한다는것인데, 어떤 value를 줘야할까요? 또는 파라미터로 넘길 데이터가 필요하지 않다면?

그래서 다음과 같은 코드를 만들게 됩니다.

MutableLiveData<Void> startSomething = new MutableLiveData<>();
startSomething.setValue(null);

이벤트를 발생시킬때마다 null을 넣는것이지요. 그러면 이 LiveData를 observe하고 있던 Activity는 ViewModel의 이벤트를 감지하고 어떤 동작을 수행할 수 있습니다.

하지만 보기는 좋지 않네요.

구글 샘플 코드에서는 이런식으로 해결했습니다.

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

    private static final String TAG = "SingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }

    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}

SingleLiveEvent라는 MutableLiveData를 상속한 클래스를 만듭니다.
이벤트를 발생시키기 위해서 데이터를 변경 시키는 로직을 내부에 두고 외부에서는 call()을 호출하도록 하는것입니다.

만약 함수의 파라미터가 필요하다면 제네릭 T를 정의하고 setValue()를 이용할 수도 있습니다.

SingleLiveEvent를 개선한 내용을 확인하시려면 다음 포스팅을 확인해주세요

카테고리: 미분류

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.