반응형
Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

안드로이드 개발자 노트

[Android] MVVM AAC LiveData 잘 알아보기 본문

Android

[Android] MVVM AAC LiveData 잘 알아보기

어리둥절범고래 2023. 1. 8. 18:02
반응형

 

안녕하세요. 이번 포스팅에서는 LiveData에 대해서 알아보도록 하겠습니다.

 

 

 

LiveData의 개념

 

1. LiveData는 기본적으로 Observer Pattern을 활용하며, 말 그대로 실시간 데이터를 나타냅니다.

2. LiveData는 Observable한 데이터 홀더 클래스입니다.

3. 일반적인 Observable 클래스와 달리 LiveData는 수명 주기를 인식합니다.

즉, 액티비티, 프래그먼트, 서비스 등 다른 앱 구성요소의 수명 주기를 고려합니다.

4. 수명 주기 인식을 통해 LiveData는 활동 수명 주기 상태에 있는 앱 구성요소 Observer(관찰자)만 업데이트합니다.

5. Observer의 수명 주기가 STARTED 또는 RESUMED 상태이면 LiveData는 Observer를 활성 상태로 간주합니다.

6. LiveData는 활성 Observer에게만 업데이트 정보를 알립니다.

7. LiveData 객체를 보기 위해 등록된 비활성 Observer는 변경사항에 관한 알림을 받지 않습니다.

 

 

 

수명 주기 인식은 어떻게?

 

LifecycleOwner 인터페이스를 implement하는 객체와 페어링된 Observer를 등록할 수 있습니다.

여기서 LifecycleOwner는 클래스에 Lifecycle이 있음을 나타내는 getLifecycle() 메서드를 가지고 있는 단일 메서드 인터페이스입니다. 이 인터페이스는 Fragment 및 AppCompatActivity와 같은 개별 클래스에서 Lifecycle의 소유권을 추출할 수 있으며, 모든 커스텀 애플리케이션 클래스는 이 LifecycleOwner 인터페이스를 구현(implement)하고 있습니다.

 

이 관계를 사용하면 Observer에 대응되는 Lifecycle 객체의 상태가 DESTROYED로 변경될 때 Observer를 삭제할 수 있습니다. 이는 특히 액티비티 및 프래그먼트에 유용합니다. 액티비티와 프래그먼트는 LiveData 객체를 안전하게 관찰할 수 있고, 수명 주기가 끝나는 즉시 수신 거부되어 누수를 걱정하지 않아도 되기 때문입니다.

 

 

LiveData 사용의 이점


1. UI와 데이터 상태의 일치 보장

  • LiveData는 기본 데이터가 변경될 때 Observer 객체에 알립니다. 앱 데이터가 변경될 때마다 Observer가 대신 UI를 업데이트하므로 개발자가 업데이트할 필요가 없습니다.

 

2. 메모리 누수 없음

  • Observer는 Lifecycle 객체에 결합되어 있으며 연결된 수명 주기가 끝나면 자동으로 삭제됩니다.


3. 중지된 활동으로 인한 비정상 종료 없음

  • 활동이 백 스택에 있을 때를 비롯하여 Observer의 수명 주기가 비활성 상태에 있으면 Observer는 어떤 LiveData 이벤트도 받지 않습니다.

 

4. 수명 주기를 더 이상 수동으로 처리하지 않음

  • UI 구성요소는 관련 데이터를 관찰하기만 할 뿐 관찰을 중지하거나 다시 시작하지 않습니다. LiveData는 관찰하는 동안 관련 수명 주기 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리합니다.

 

5. 최신 데이터 유지

  • 수명 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신합니다. 예를 들어 백그라운드에 있었던 활동은 포그라운드로 돌아온 직후 최신 데이터를 받습니다.


6. 적절한 구성 변경

  • 기기 회전과 같은 구성 변경으로 인해 활동 또는 프래그먼트가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받게 됩니다.


7. 리소스 공유

  • 앱에서 시스템 서비스를 공유할 수 있도록 싱글톤 패턴을 사용하는 LiveData 객체를 확장하여 시스템 서비스를 래핑할 수 있습니다.

 

 

LiveData 사용법

 

LiveData의 기본적인걸 알았으니 이제 사용법을 살펴보겠습니다.

 

LiveData 객체 생성 단계

 

1. 특정 유형의 데이터를 보유할 LiveData의 인스턴스를 생성합니다. 이 작업은 일반적으로 ViewModel 클래스 내에서 이루어지며, 이유는 다음과 같습니다.

  • 활동과 프래그먼트가 지나치게 커지지 않게 하기 위해서입니다. 이러한 UI 컨트롤러는 데이터 표시만을 담당하며, 데이터 상태를 보유하지는 않습니다.
  • LiveData 인스턴스를 특정 활동이나 프래그먼트 인스턴스에서 분리하고 구성 변경에도 LiveData 객체가 유지되도록 하기 위해서입니다.

 

2. onChanged() 메서드를 정의하는 Observer 객체를 만듭니다.

이 메서드는 LiveData 객체가 보유한 데이터 변경 시 발생하는 작업을 제어합니다.

일반적으로 활동이나 프래그먼트 같은 UI 컨트롤러에 Observer 객체를 만듭니다.

 

3. observe() 메서드를 사용하여 LiveData 객체에 Observer 객체를 연결합니다. observe() 메서드는 LifecycleOwner 객체를 사용합니다. 이렇게 하면 Observer 객체가 LiveData 객체를 구독하여 변경사항에 관한 알림을 받습니다. 일반적으로 활동이나 프래그먼트와 같은 UI 컨트롤러에 Observer 객체를 연결합니다.

 

추가로, observeForever(Observer) 메서드를 사용하여 연결된 LifecycleOwner 객체가 없는 Observer를 등록할 수 있습니다. 즉 Lifecycle과 상관 없이 계속해서 변경사항을 구독하는 observer 객체를 UI 컨트롤러에 연결하는 것이며, removeObserver(Observer) 메서드를 호출하여 이러한 Observer를 삭제할 수 있습니다.

 

LiveData 객체에 저장된 값을 업데이트하면 연결된 LifecycleOwner가 활성 상태에 있는 한 등록된 모든 Observer가 트리거됩니다.

 

 

 

LiveData 객체 만들기


LiveData는 Collections를 구현(implement)하는 List와 같은 객체를 비롯하여 모든 데이터와 함께 사용할 수 있는 래퍼입니다. LiveData 객체는 일반적으로 ViewModel 객체 내에 저장되며 다음 예에서 보는 것과 같이 getter 메서드를 통해 액세스됩니다.

 

class NameViewModel : ViewModel() {

    // String을 담는 LiveData 인스턴스 생성
    private val _currentName = MutableLiveData<String>()
    val currentName: LiveData<String> get() = _currentName

}

 

 

 

LiveData 객체 관찰

 

대부분의 경우 onCreate() 메서드는 LiveData 객체 관찰을 시작하기 적합한 장소이며 그 이유는 다음과 같습니다.

1. 시스템이 액티비티나 프래그먼트의 onResume() 메서드에서 중복 호출을 하지 않도록 하기 위해서입니다.


2. 액티비티나 프래그먼트에 활성 상태가 되는 즉시 표시할 수 있는 데이터가 포함되도록 하기 위함입니다. 앱 구성요소는 STARTED 상태가 되는 즉시 관찰하고 있던 LiveData 객체에서 최신 값을 수신합니다. 이는 관찰할 LiveData 객체가 설정된 경우에만 발생합니다.


3. 일반적으로 LiveData는 데이터가 변경될 때 활성 Observer에게만 업데이트를 전달합니다. 이 동작의 예외로, Observer가 비활성에서 활성 상태로 변경될 때에도 Observer가 업데이트를 받습니다. 또한 Observer가 비활성에서 활성 상태로 두 번째로 변경되면, 마지막으로 활성 상태가 된 이후 값이 변경된 경우에만 업데이트를 받습니다.

 


다음 샘플 코드는 LiveData 객체 관찰을 시작하는 방법을 보여줍니다.

 

class MainActivity : AppCompatActivity() {

    // 지연 초기화로 뷰모델을 선언한다.
    private val model: NameViewModel by lazy { NameViewModel() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // LiveData를 관찰한다. LifecycleOwner(Activity)와 Observer를 인자로 넣는다.
        // 람다식(Observer)이 마지막 인자에 있다면 소괄호 바깥으로 나온다.
        model.currentName.observe(this) {
            // UI 업데이트
            nameTextView.text = newName
        }
    }

}

 

 

여기서 observe 메서드 내부는 다음과 같습니다.

 

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    owner.getLifecycle().addObserver(wrapper);
}

 

LiveData에는 Observer들을 담아놓는 Map<Observer, ObserverWrapper> 형태의 mObservers라는 변수가 있는데, LiveData는 setValue 메서드가 호출되면 이 mObservers를 통해 변경된 사실을 모든 Observer들에게 알려줄 수 있습니다.

 

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = 
        new SafeIterableMap<>();

 

 

observe 메서드가 호출되면 해당 Observer 객체를 이 mObservers에 put 하는 것을 확인할 수 있습니다.

 

ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

 

 

 

 

LiveData 객체 업데이트

 

observe를 설정한 후에는 아래 예와 같이 사용자가 버튼을 탭할 때 모든 Observer를 트리거하는 LiveData 객체의 값을 업데이트할 수 있습니다.

 

button.setOnClickListener {
    val anotherName = "John Doe"
    model.currentName.setValue(anotherName)
    // 아래와 같이 표현할 수도 있다.
    // model.currentName.value = anotherName
}

 

위 예제에서 currentName은 MutableLiveData 이며, MutableLiveData 내부를 살펴보면 다음과 같습니다.

 

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

    public MutableLiveData(T value) {
        super(value);
    }

    public MutableLiveData() {
        super();
    }

    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}


MutableLiveData 클래스는 LiveData를 상속받고 있으며, 모든 메서드들은 상속받고 있는 LiveData의 메서드를 호출하고 있는 것을 확인할 수 있습니다.

 

 

LiveData 클래스의 setValue 메서드 내부는 아래와 같습니다.

 

protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

 

 

위 예제의 model.current.setValeu() 는 결국 LiveData 클래스의 setValue 메서드를 호출하게 되는데, setValue 메서드는 dispatchingValue 메서드를 호출합니다. 이 메서드는 다음과 같습니다.

 

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

 

mObserver를 순회하며 모든 Observer에 대해 considerNotify 메서드를 수행하게 됩니다.

considerNotify 메서드의 내부는 다음과 같습니다.

 

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);
}

 

해당 Observer의 onChanged(data) 메서드를 호출하고 있습니다. 이로써 setValue()를 호출하면 value를 observe하고 있는 모든 Observer에게 onChanged 메서드를 호출한다는 사실을 알 수 있습니다.

 

 

 

 

 

 

 

 

 

https://developer.android.com/topic/libraries/architecture/livedata

반응형

'Android' 카테고리의 다른 글

[Android] Android KTX 잘 알아보기  (0) 2023.01.22
[Android] MVVM AAC ViewModel 잘 알아보기  (0) 2023.01.08