AAC - viewModel
AAC(Andorid Architecture Component) - viewModel
viewModel알아보기!
안드로이드 아키텍처 컴포넌트 viewModel은 UI관련 데이터를 저장하고 관리하기 위하여 설계되었습니다.
스크린 회전 같은 상태 변화에도 데이터가 보존될 수 있도록 합니다.
안드로이드기기를 회전한다면 안드로이드 생명주기는 위 그림의 왼쪽처럼
화면이 Destroy되고 다시 Create되는 과정을 거치게됩니다.
위같은 회전에대하여 간단한 데이터같은 경우 onSaveInstanceState()메소드를 이용하여 저장하고
다시 onCreate()에서 다시 데이터를 받을 수 있지만,
serialize가 가능한 간단한데이터만 가능합니다. bitmap같은 많은양에 데이터는 부적합합니다.
또한 화면회전의 상태변화로 인해서 객체를 재생성해야하는경우, 이미 만들어진객체를 재생성해야하므로 리소스낭비가 심합니다.
또 다른 대안으로는 Retained Fragment 를 사용하는 방법도 있습니다.
UI가 없는 Fragment를 사용하여 UI에 필요한 데이터를 관리하고, 이 프래그먼트를
setRetainInstance(true)를 사용하여 설정함으로서 , 액티비티 파괴시에도
프레그먼트를 메모리에 유지시키는 방법입니다.
이방법을 쓰면 onDetach, onAttach등을 반복하지 않습니다.
하지만 일정한 범위를 넘어섰을때 또다른 문제를 다뤄야했습니다.
이곳이 데이터를 보관하기에 적합한 곳인가? 혹은 오버-엔지니어링이 아닐까?라는 논쟁의 여지가 존재했습니다.
아키텍처 컴포넌트의 ViewModel은 개발자에게 전가된 이러한 고민들을 근본적으로 해결합니다.
viewModel은 화면회전에도 스코프가 일관되게 유지되는것을 볼 수 있습니다(위 그림의 오른쪽)
viewModel의 장점
- viewModel은 액티비티 스코프의 싱글톤객체처럼 사용할수 있습니다.
- 프래그먼트 사이에서 viewModel을 이용하여 데이터를 쉽게 공유할 수 있습니다.
- 더이상 프래그먼트 중개자로 액티비티를 사용하지않아도됩니다.(액티비티의 역할수행을 덜을수있음)
- 위에서 말한 화면회전 같은 부분문제를 해결합니다.
viewModel 더 자세히 알아보기
- viewModel 라이브러리는 내부적으로 프래그먼트를 사용합니다
- 최초 viewModel을 생성할 때 , ViewModelProvider는 HolderFragment라 명명된프래그먼트를 생성하고
- 이 프래그먼트에는 setRetainInstance(true)가 설정됩니다.
- viewModel은 유보된 프래그먼트(RetainFragment)의 연장선이라 할 수 있습니다.
- 최초 viewModel을 생성할 때 , ViewModelProvider는 HolderFragment라 명명된프래그먼트를 생성하고
- viewModel은 추상클래스(abstract)로 상속하는것만으로 viewModel을 만들 수 있습니다 .
- 추상클래스이므로 객체를 그냥 생성할수 없습니다.
- ViewModelProvider를 통해 객체를 생성해주어야합니다.
class DisplayViewModel(val mainVm: MainViewModel) : ViewModel()
- 생명주기 함수 onCleared()가 존재합니다.
- 커스텀 생성자를 가지려면, ViewModel은 ViewModelProvider.Factory 인터페이스를 사용해야합니다.
//위코드의 DisplayViewModel을 생성하기위한 팩토리클래스
class DisplayViewModelFactory(val mainViewModel: MainViewModel) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return modelClass.getConstructor(MainViewModel::class.java).newInstance(mainViewModel)
}
}
val factory = DisplayViewModelFactory(mainVm)
val displayVm = ViewModelProviders.of(this, factory).get(DisplayViewModel::class.java)
viewModel 사용시에 주의사항
- viewModel내부에 액티비티, 프레그먼트, 뷰에 대한 컨텍스트를 저장해서는 안됩니다.
- viewModel의 수명주기는 외부에 존재하기때문에 , 메모리 릭의 원인이 될 수 있습니다.
- ApplicationContext 는 상관없습니다. AndroidViewModel클래스도 제공하고 있습니다.
- viewModel은 기기의 구성이 변경될때만 유지됩니다.
- 백버튼이나 , 최근목록에서 앱을 종료했을때는 어떠한 처리도 기대할 수 없습니다.
- A라는 액티비티에서 사용하는 viewModel을 B 액티비티에서 viewModel에 저장된 값을 사용하고 싶다면 ?
- ViewModelProvider.Factory를 SingleTon으로 구성하면 됩니다.
- 하지만 다른 생명주기에서 ViewModel객체를 유지하는것은 안티패턴이므로
- 생명주기에 따라 데이터를 보관/관리 해주는 LiveData등의 장점을 버리는 것이 되버릴수 있습니다.
- 따라서 ViewModel 인스턴스를 유지시키는 것이 아닌 Datasoure나 Repository를 싱글톤으로 유지하는것이 더 추천되는 방식입니다.
- https://stackoverflow.com/questions/49364550/android-livedata-how-to-reuse-the-same-viewmodel-on-different-activities/49365126#49365126
- 단일 액티비티에서 2개이상의 프래그먼트 사이 데이터를 공유할때
- viewModel을 생성할때 프래그먼트의 scope를 사용하는것이아닌 액티비티의 scope를 전달하는것이 좋습니다.