LiveData 는 관찰가능한 데이터를 가지고 있는 클래스입니다. 쉽게 말해서 viewMode에서 멤버 변수의 타입을 LiveData 로 선언하고, activity에서 이 변수를 참조해서 옵저버 또한 등록해주면, 해당 변수의 값이 변할 때마다 activity 에게 알려주게 됩니다.
그렇다면 왜 LiveData 는 jetpack 의 lifecycle library 에 속할까요?
일반적인 observable 과 달리, LiveData 는 컴포넌트들의 lifecycle을 고려합니다. 라이프 사이클을 인지해서, start나 resume 과 같은 활성화 상태에 있는 컴포넌트 옵저버에게만 업데이트를 해줍니다. 또한 destroy 가 되면, 옵저버는 지워집니다. 이는 activity 나 fragment 사용시 굉장히 유용한데요, 안전하게 옵저버를 등록하고 destroy 가 되면 자동으로 구독을 취소하기 때문에 메모리 누수를 걱정하지 않아도 됩니다.
LiveData 를 사용하려면, 데이터의 변화를 관찰하고 있는 옵저버(observer)를 등록해야 합니다.
class NameActivity : AppCompatActivity() {
// viewModel 을 초기화해줍니다.
private val model: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// UI를 업데이트 해주는 옵저버를 등록합니다.
val nameObserver = Observer<String> { newName ->
nameTextView.text = newName
}
// 뷰모델의 데이터에 activity 를 LifecyclerOwner 로, 옵저버와 함께 넣어줍니다.
model.currentName.observe(this, nameObserver)
}
}
class NameViewModel : ViewModel() {
// String 타입으로 LiveData 를 등록합니다.
private val _currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
val currentName : LiveData<String> get() = _currentName
}
옵저버를 등록할 때, LifecycleOwner 이 필요합니다. 보통 activity 에서는 LifecycleOwner 를 가지고 있기 때문에 this 를 넣어줍니다. 그러면 liveData 는 이를 통해 lifecycle 을 고려할 수 있게 됩니다. 위에서 말한 것 처럼 LiveData 는 옵저버가 활성화 상태(start, resume 등)에 있을 때만 변화된 값을 보냅니다.
뷰모델에서 LiveData를 등록할 때는 뷰모델 내에서 가공할 데이터는 MutableLiveData (mutable : 변할 수 있는)로, 밖에서(activity 나 fragment에서) 접근할 데이터는 LiveData 로 정의해서 캡슐화(encapsulate)합니다. 네이밍 컨밴션은 MutableLiveData 에는 앞에 _ 를 넣어주면 됩니다.
MutableLiveData는 제너릭 클래스이기 때문에, 위의 예시처럼 데이터의 타입을 지정해주어야 합니다.
Fragment's view 는 사용자가 다른 뷰로 이동하면 destroy 되지만, fragment 그 자체는 destroy 되지 않습니다. 이는 곧 fragment 에 두 개의 lifecycle 을 만들게 되는데, 위 그림처럼 fragment 의 lifecycle 과 fragment's view 의 lifecycle 입니다. (fragment 는 activity 와 달리 view 가 여러번 죽고 재생성될 수 있습니다.)
따라서 옵저버 등록시 fragment 의 lifecycleowner 를 등록하게 되면 문제가 생깁니다. 반드시 fragment's view 의 lifecycleowner 인 viewLifecycleOwner 를 등록해주어야 합니다.
fragment 의 lifecyclerowner 를 등록해주면, view가 destory 되어도 옵저버가 사라지지 않기 때문에 재생성되었을 때 fragment 에 옵저버가 2개가 존재하게 되어서 문제가 생길 수 있기 때문입니다.
그리고 옵저버 자체를 등록할 때도 onCreate 에서 등록하게 되면 문제가 생길 수 있습니다. fragment's view 가 재생성 되었을 때, onCreate 는 불리지 않기 때문에 옵저버가 view 가 재생성 된지 모르고 업데이트를 해주지 않는 문제가 생깁니다. 따라서 onCreate 이후에 불리는 onCreateView 나 onViewCreated 에서 등록해주어야 합니다.
LiveData 객체를 binding 객체와 함께 사용하면 liveData 의 값이 변했을 때, UI Controller 에서 옵저버를 등록하고, ui 를 업데이트 해주는 로직 필요없이 바로 xml 에서 업데이트 해줄 수 있습니다.
그러려면, lifecycle owner 를 지정해서 LiveData 의 scope 를 명시해주어야 합니다.
옵저버를 binding 객체에 등록한다고 보면 편할 것 같습니다.
이때도, fragment 의 경우에는 viewLifecycleOwner 로 지정해줘야겠지요?
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// data binding
val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)
// activity 의 경우 this를, fragment 의 경우 viewLifecycleOwner 를 등록해줍니다.
binding.setLifecycleOwner(this)
}
}
이상으로 jetpack 의 lifecycle library 중 하나인 liveData 를 살펴보았습니다.
liveData 는 등록된 옵저버 객체의 lifecycle 을 고려해서 active 상태에 있는 경우에만 변화된 값을 업데이트 해주고, destroy 가 되면 자동으로 지워지는 똑똑한 친구입니다.
그리고 data binding 과 함께 사용했을 때, activity 나 fragment 에서 옵저버를 등록하고 setText 등과 같은 ui 를 업데이트 해주는 코드없이 xml 에서 바로 바로 업데이트 해줄 수 있답니다!
주의사항으로는 lifecycleOwner 를 지정할 때, fragment 의 경우 view 가 여러번 재생성 될 수 있기 때문에 viewLifecycleOwner 로 등록해주어야 한다는 것이었습니다!
왜 이제서야 공부하게 되었을까 싶을 정도로 정말 코딩과 개발을 쉽게 해주는 좋은 라이브러리인 것 같습니다! 혹시 글에서 잘못된 부분이 있으면 알려주시고, 앞으로 liveData 를 사용하면서 알게 되는, 나누면 좋은 것들을 발견하게 되면 업데이트! 하도록 하겠습니다~ 제 블로그를 꾸준히 옵저빙해주세요~! ㅋㅋㅋ
공식문서 : https://developer.android.com/topic/libraries/data-binding/architecture
https://developer.android.com/topic/libraries/architecture/livedata
codelabs : https://developer.android.com/codelabs/kotlin-android-training-live-data#6
fragment lifecycle : https://www.youtube.com/watch?v=fEmS9vEUqTE
fragment & liveData 주의사항 : https://www.youtube.com/watch?v=pErTyQpA390&feature=youtu.be&t=349