Android - Single Live Event

Park Suyong·2022년 2월 23일
2

Android Dev

목록 보기
3/7

Single Live Event

정의

MVVM 패턴으로 프로젝트를 설계하고 개발하다 보면, 특정 이벤트 발생 시 ViewModel에서 값을 변경할 필요가 없는 경우가 발생한다. Android에서는 화면이 가로 혹은 세로로 전환될 때마다 View가 재활성화 되게 된다. 이 때도 값의 변경이라고 observing이 되면서 발생하는 문제인데, 불필요한 Observer Event를 막기 위한 것이 Single Live Event 이다.

이런 상황이 발생했을 때 boolean flag를 사용해서 observing 되지 않도록 코드를 작성할 수 있을 것이다. 하지만, 이는 분명한 Anti Pattern이다. 이런 방법으로 해결책을 찾는 것은 코드를 읽는 것을 어렵게 만들 뿐더러 유지보수 측면에서 어려울 수 있다.

왜 등장했나?

정의에서 설명했듯, 불필요한 Observer Event를 막기 위해 등장한 것이 Single Live Event 이다. Anti Pattern으로 인한 피해를 방지하기 위함인 것인지, 구글에서 아래의 샘플 코드를 제공하고 있다.

코드

/*
 *  Copyright 2017 Google Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

import android.util.Log
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean

/**
 * A lifecycle-aware observable that sends only new updates after subscription, used for events like
 * navigation and Snackbar messages.
 *
 *
 * This avoids a common problem with events: on configuration change (like rotation) an update
 * can be emitted if the observer is active. This LiveData only calls the observable if there's an
 * explicit call to setValue() or call().
 *
 *
 * Note that only one observer is going to be notified of changes.
 */
class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val pending = AtomicBoolean(false)

  	@MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
        }
        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }
    companion object {
        private val TAG = "SingleLiveEvent"
    }
}

코드 분석

코드를 보면 알 수 있듯, Single Live Event는 LiveData를 상속 받는 클래스이다.

AtomicBoolean은 멀티스레딩 환경에서 동시성을 보장한다. false로 초기화 되어 있다.

private val pending = AtomicBoolean(false)

View(Activity, Fragment, LifeCycleOwner 등)가 활성화 상태가 되거나, setValue를 통해 값이 변경되었을 때 호출되게 되는 observe 메서드이다.

아래 조건문의 pending.compareAndSet(true, false)는 pending 값이 true일 때 조건문을 처리하고 처리 후 false로 값이 변경된다는 의미이다.

밑의 setValue 메서드를 통해서만 pending 값의 변경이 일어나게 되기 때문에, 화면 가로 세로 전환 등으로 인한 무의미한 값의 상태 변경이 발생해도 pending 값은 false 이므로, observe 메서드가 데이터를 전달하지 않게 되는 원리이다.

@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
    if (hasActiveObservers()) {
        Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
    }
    // Observe the internal MutableLiveData
    super.observe(owner, Observer { t ->
        if (pending.compareAndSet(true, false)) {
            observer.onChanged(t)
        }
    })
}

LiveData로써 갖고 있는 데이터의 값을 변경하는 함수이다. 이 메서드에서는 pending 값을 true로 변경한다. 이를 통해 observe 메서드에서 값의 변경이 일어날 수 있게 된다.

@MainThread
override fun setValue(t: T?) {
  pending.set(true)
  super.setValue(t)
}

데이터의 속성을 따로 지정해 주지 않아도 call 메서드 만으로 setValue를 호출할 수 있다.

/**
 * Used for cases where T is Void, to make calls cleaner.
 */
@MainThread
fun call() {
  value = null
}

결론

위 코드에서 확인했듯이, 반드시 setValue를 통해서만 Observer를 통해 값을 변경할 수 있다. 이를 통해 불필요한 상태 체크가 일어나도 이벤트가 다시 발생하는 것을 방지할 수 있게 해준다.

참고

Anti Pattern은 실제로 많이 사용되는 패턴이지만 비효율적이거나 비생산적인 패턴을 의미한다. 여기에 안티 패턴으로 인해 발생하는 안좋은 영향까지 포함되는 개념이다.

References

SingleLiveEvent와 Event 정리

[Android] SingleLiveEvent의 원리

SingleLiveEvent to help you work with LiveData and events

profile
Android Developer

0개의 댓글