AAC - LiveData
AAC 연관 글
- AAC #1 - Android Architecture Components
- AAC #2 - LifeCycle / LifecycleOwner
- AAC #3 - LiveData (현재 글)
- AAC #4 - ViewModel
- AAC #5 - Room
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 |
: LiveData에 연결된 Observer가 한개라도 있다면 true를 반환 |
boolean |
: 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 |
: 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()
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()
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를 반환)