본문 바로가기

[Android/Kotlin] AAC #3 - LiveData

AAC - LiveData

 

 

AAC 연관 글

 

 

 

LiveData

  • LiveData는 식별가능한 데이터 홀더클래스로 식별가능한 일반클래스와 달리 수명주기를 인식합니다

식별가능한 데이터 홀더클래스 : LiveData, MutableLiveData, MediatorLiveData 등

식별가능한 일반클래스 : ObservableField, ObservableBoolean, ObservableObject 등

식별가능한이란 의미는 데이터(값)이 변경되면 구독자(Observer)에게 변경을 알림으로 UI를 갱신의 의미로

둘의 이런 기능은 비슷하지만 차이점은 데이터를 홀더(Holder) 여부에 달라집니다

LiveData는 owner의 생명주기를 알고 Observing 여부를 지정할 수 있습니다

LifeCycle이 STARTED RESUME 상태를 Active로 간주하고 Observing 합니다

  • LiveData 구성

1) LifecycleOwner + Observer

Owner가 active(STARTED or RESUMED)일때 wrapping된 data가 변경되면 noti를 수신

 

 

LiveData 장점

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

: LiveData는 Observer패턴으로 구성

  • 메모리 릭 방지

: Observer는 LifecycleOwner와 결합되어 Destroyed 되면 자동으로 소멸

  • 중지된 활동으로 인한 비정상 종료 방지

: 활동이 백스택에 있을 때를 비롯해서 Lifecycle이 InActive 상태라면 Observer는 어떤 LiveData이벤트 수신X 

  • Lifecycle에 따른 처리 로직 X

: UI 구성요소는 관련 데이터를 관찰하기만 하며 Observing을 중지하거나 재개하지 않습니다

LiveData는 Observing 동안 관련 Lifecycle의 변경을 인식하고 자동으로 관리

  • 항상 최신의 데이터 유지

: Lifecycle이 비활성화 상태 -> 활성 상태로 변경 시 비활성화 상태에서의 가장 최신 데이터를 수신

ex) 백그라운드에 있던 활동은 포그라운드로 돌아온 직후 최신 데이터를 수신 

  • 리소스 공유

: 싱글톤 패턴을 사용하는 LiveData 인스턴스를 확장하여 시스템 서비스를 앱에서 공유하도록 래핑 가능

 

 

LiveData Method

 

Public Constructors

// 주어진 값(value)로 초기화 된 LiveData 생성
public LiveData(value :T)

// 값이 할당되지 않은 LiveData 생성
public LiveData()

 

Public Methods

Return Type Method Description
T

getValue()

 : 현재값을 반환, Background Thread에서 호출하면 마지막 set된값을 받는다는 보장 X

boolean

hasObservers()

 : LiveData에 연결된 Observer가 한개라도 있다면 true를 반환

boolean

hasActivieObservers()

 : LiveData에 Active(활성) 상태의 Observer가 있으면 true, 없으면 false 반환

void

observe(owner: LifecycleOwner, observer: Observer<? super T>)

 : owner의 Lifecycle에 따라 동작하는 Observer를 등록하는 메서드

Main Thread에서 호출하며, 이미 등록된 observer일 경우 observer의 내용이 실행

Active(STARTED, RESUMEND) 상태에서만 observer가 이벤트를 수신하며

inActive 상태에서는 데이터 수신 불가 -> Active 상태로 진입 시 자동 마지막 데이터 수신

DESTROYED 상태 전까지는 LiveData - owner - observer 강한참조를 갖고있으나, 

DESTROYED 상태가 되면 참조가 모두 제거됨

 

1. Observer 등록 시 Owner가 DESTROYED 상태일 경우 무시

2. 같은 Owner-Observer 조합 등록요청 시 무시

3. 이미 등록된 Observer가 다른 Owner와 함께 등록요청을 할 경우 IllegalArgumentException 예외 발생

void

observeForever(observer: Observer<? super T>

 : owner 없이 Observer 등록하는 메서드

해당 Observer는 owner가 없으므로 항상 Active 상태로 인식하고 동작

즉, 모든 이벤트를 수신할 수 있으며 자동으로 해제되지 않기 때문에 명시적으로 해제를 위한 removeObserver() 호출의 필요

만약 Observer가 다른 Owner와 함께 이미 등록된 경우 IllegalArgumentException 발생 

void

removeObserver(observer: Observer<? super T>

 : 파라미터로 받은 observer를 List에서 해제

void

removeObservers(owner: LifecycleOwner)

 : 파라미터로 받은 owner에 등록된 모든 Observer 제거

  • observe() 함수는 LifecycleOwner와 Observer 인스턴스가 같이 등록되어,
    lifecycle에 따라 Observer의 동작을 결정합니다

 

Protected Methods

LiveData<T>를 상속하여 class 구현 시 Override 할 수 있는 Method

Return Type Method Description
void

onActive()

 : Active Observer 개수가 0 -> 1 변경 시 호출 (Callback 함수)

 이때 데이터를 실시간으로 업데이트를 시작해야 하는 부분

void

onInActive()

 : Active Observer의 개수가 1 -> 0 변경 시 호출 (Callback 함수)

 Observer가 전부 remove되거나 active상태의 observer가 없을 경우

void

postValue(value: T)

 : Background Thread에서 호출하는 경우 Main Thread에 값을 set하라고 post하는 메서드

 setValue()와 동시에 호출할 경우 postValue()는 setValue()보다 나중에 처리

void

setValue(value: T)

 : Main Thread에서 호출하여 값을 set 시키는 메서드

 등록된 active Observer들에게 바로 전달 

 

  • onActive() 함수는 LiveData가 active Observer를 하나라도 가질때 호출되는 Callback 메서드 
  • onInactive() 함수는 LiveData가 active Observer를 하나도 가지지 않을 때 호출되는 Callback 메서드
  • setValue() 함수가 호출되면 LiveData의 값을 업데이트 + active Observer에게 notify (onChanged() 호출)
  • postValue() 함수는 Worker 스레드(백그라운드)에서 값을 setting 하는 함수로, UI(메인) 스레드에게 값 setting을 요청하는 함수

즉, postValue() 실행 후 setValue()를 바로 실행하면 setValue() 먼저 실행되고, 그 다음 postValue() 실행 

 

 

Transformation

  • 변경된 LiveData를 Observer에게 전달하기 전에 데이터를 가공하고 싶거나,
    새로운 LiveData를 전달하고 싶을때 Transformations
    를 이용합니다
  • 2개의 변환함수 존재
    • Transformation.map()
    • Transformation.switchMap()

 


Transformation.map()

Transformations.map() 메서드

public static LiveData<Y> map (LiveData<X> source,
		Function<X, Y> mapFunction)

 

userLiveData: LiveData<User> = ...;
userNameLiveData: LiveData<String> = Transformations.map(userLiveData) {
    user -> "${user.firstName} ${user.lastName}"
}


1) userLiveData: LiveData<User>
: User클래스를 wrapping한 LiveData입니다

2) userNameLiveData = Transformations.map(userLiveData) { user -> ... }

    : userLiveData의 값이 변경되면 실행될 함수로 변경된 LiveData의 값을 갖고 함수실행 후 결과를 반환

    반환Type은 wrapping된 LiveData의 값을 반환 

    위 코드에선 LiveData<String>의 String값을 반환합니다

 


Transformations.switchMap()

Transformations.switchMap() 메서드

public static LiveData<Y> switchMap (LiveData<X> source,
                Function<X, LiveData<Y>> switchMapFunction)

 

class UserViewModel extends AndroidViewModel {
    var userId: MutableLiveData<String> = ...

    var userLiveData :LiveData<User> = Transformations.switchMap(userId) { userId ->
    	myDataSource.getUserInfo(userId)
        }

    fun setUserId(String userId) {
        this.userId.setValue(userId);
    }
}


1) userId: MutableLiveData<String>
: String을 Wrapping한 변경가능한 LiveData입니다

2) userLiveData :LiveData<User> = Transformations.switchMap(userId) { userId -> ... }

    : userId의 값이 변하면 자동으로 switchMap()이 실행되고 새로운 user의 정보를

      새로운 LiveData로 반환합니다. LiveData 인스턴스가 반환되면 Observer들에게 모두 새로운 noti를 알립니다

    (map은 LiveData의 값을 반환하지만, switchMap은 새로운 LiveData를 반환)