android/Jetpack
3. LiveData
android by kotlin
2022. 10. 3. 11:12
*개인적으로 분석한 내용이므로 틀린 내용이 있을 수 있습니다. 틀린 내용을 댓글로 남겨주시면 수정하도록 하겠습니다!
[1]. LiveData 의존성 선언
dependencies {
...
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.0-alpha02"
...
}
[2]. LiveData 사용방법
class MainViewModel : ViewModel() {
private var _flag = MutableLiveData<Boolean>()
/*
ViewModel 클래스 외부에 노출될 LiveData 프로퍼티는
변경이 불가능한 LiveData 타입을 사용한다.
*/
val flag: LiveData<Boolean> get() = _flag
/*
LiveData 를 관찰하고 있는 옵저버가
데이터가 변경되었음을 통지받을 수 있도록
LiveData 의 setValue 메서드를 사용하여
데이터를 변경시킨다.
*/
fun change() {
_flag.value = _flag.value != true
}
}
class MainActivity : AppCompatActivity() {
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val viewModel by lazy {
ViewModelProvider(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initViews()
initObserver()
}
private fun initViews() {
/*
버튼을 클릭한 경우
ViewModel 클래스의 change 메서드를 호출한다.
*/
binding.switchButton.setOnClickListener {
viewModel.change()
}
}
private fun initObserver() {
/*
ViewModel 클래스의 _flag LiveData 가 변경되었을 경우
변경 사항을 통지받기 위해 옵저버를 등록시켜놓는다.
첫번째 매개변수는 생명주기 소유자 객체로
생명주기가 활성화 상태일 경우에만 통지를 받기위해 전달하는 매개변수이다.
*/
viewModel.flag.observe(this) { flag ->
if(flag) {
binding.switchButton.text = "true"
} else {
binding.switchButton.text = "false"
}
}
}
}
[3]. LiveData 옵저버 등록 분석
1. LiveData 의 observe 메서드의 동작 과정을 간략히 요약하면 LiveData 의 옵저버 Map 에 등록 → 생명주기 소유자의 생명주기를 관찰하기 위해 Lifecycle 옵저버 등록 → 초기값을 옵저버에 통지 하는 과정을 거치게 된다.
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
/*
옵저버를 등록한 생명주기 소유자의 상태가 DESTROYED 상태라면
통지를 받지 않도록 한다.
*/
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
/*
해당 옵저버를 Map 자료구조인 mObservers 에 등록한다.
*/
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;
}
/*
생명주기 소유자의 생명주기 변화에 따라 옵저버의 callback 메서드를 호출하기 위해서
생명주기에 옵저버를 바인딩한다.
*/
owner.getLifecycle().addObserver(wrapper);
}
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
enforceMainThreadIfNeeded("addObserver");
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
if (previous != null) {
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
//DESTROYED == 0, INITIALIZED == 1, CREATED == 2, STARTED == 3, RESUMED == 4
/*
현재 옵저버의 생명주기가 CREATED 이상인 경우 그리고 해당 옵저버가 등록되어 있는 경우에만
while 구문을 실행하여 옵저버에 이벤트를 디스패치한다.
*/
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
final Event event = Event.upFrom(statefulObserver.mState);
if (event == null) {
throw new IllegalStateException("no event up from " + statefulObserver.mState);
}
statefulObserver.dispatchEvent(lifecycleOwner, event);
popParentState();
// mState / subling may have been changed recalculate
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = event.getTargetState();
mState = min(mState, newState);
/*
옵저버의 onStateChanged 메서드를 호출한다.
*/
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
void activeStateChanged(boolean newActive) {
//새로운 상태와 기존 상태가 동일하다면 종료한다.
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
//옵저버의 상태를 업데이트한다.
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
/*
활성 상태로 업데이트된 경우
옵저버에게 한번 통지하기 위해 dispatchingValue 메서드를 호출한다.
*/
if (mActive) {
dispatchingValue(this);
}
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
/*
옵저버 등록 후 최초의 dispatchingValue 호출 시에는
initiator 객체가 null 이 아니므로 if 조건을 수행한다
*/
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;
}
@SuppressWarnings("unchecked")
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
/*
옵저버 등록 시 전달한 객체의 callback 메서드를 호출하여
Activity 또는 Fragment 에서 초기값을 이용하여 View 를 업데이트할 수 있도록 한다
*/
observer.mObserver.onChanged((T) mData);
}
[4]. LiveData 의 값 변화 시 분석 내용
1. LiveData 의 setValue 메서드를 호출하여 값을 변경하게 되면 해당 LiveData 를 관찰하고 있는 모든 옵저버들에게 값이 변경되었음을 통지하게 된다.
class MainViewModel : ViewModel() {
private var _flag = MutableLiveData<Boolean>()
val flag: LiveData<Boolean> get() = _flag
/*
LiveData 의 setValue 를 호출한다.
*/
fun change() {
_flag.value = _flag.value != true
}
}
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
/*
setValue 를 호출하는 경우에는 initiator 객체가 null 이므로
mObservers 를 통해 해당 LiveData 를 관찰하는 옵저버들을 하나씩 꺼내서
값이 변화되었다는 것을 옵저버에게 통지해주기 위해 onChanged callback 메서드를 호출해준다.
*/
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
[5]. LiveData 옵저버 제거 분석 내용
1. 생명주기 소유자의 생명주기가 DESTROYED 상태가 되거나 명시적으로 removeObserver 를 호출하는 경우 removeObserver 메서드를 호출하여 옵저버를 제거한다.
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
/*
생명주기 소유자의 생명주기가 DESTROYED 상태인 경우 LiveData 에 등록된 옵저버를 제거한다.
*/
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
/*
해당 LiveData 를 관찰중인 옵저버를 제거한다.
*/
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();
removed.activeStateChanged(false);
}