모든 안드로이드 버전과 기기에서 일관되게 작동하는 코드 작성을 도와주는 라이브러리 모음
기능에 따라 4가지로 구분된다.
데이터를 Activity가 아닌 ViewModel에서 관리하자!!
View에서 사용되는 데이터를 쉽게 관리하도록 도와주는 역할이다.
안드로이드의 Activity 생명주기에 분리를 시켜, Activity가 재실행되어도 데이터가 소멸되지 않도록 한다. Activity가 소멸되면 ViewModel의 자원도 자연히 소멸된다.
참고로 Jetpack ViewModel은 MVVM 패턴에서의 ViewModel과 차이점이 있다. MVVM 패턴의 경우에는 jetpack viewmodel 및 livedata를 활용하여 뷰를 갱신하는 작업을 해야한다.
뷰 모델을 ViewModel을 상속받는 클래스를 만들어 생성한다.
count변수를 가지고 있는 뷰 모델 하나를 만들어보자.
class MainViewModel : ViewModel() {
//getter는 public하게, set만 private 하게
var count = 0
private set
fun increaseCount(){
count++
}
}
액티비티에서 뷰 모델 가져오기
고전적인 방식
private val viewModel : MainViewModel by lazy{ ViewModelProvider(this)[MainViewModel::class.java] }
ViewModelProvider
를 통해 현재 위와 같은 방식으로 MainViewModel을 가져올 수 있다.
이제 뷰모델을 통해 데이터에 접근해서 그 데이터를 참조하여 뷰를 변경하면
binding.increaseButton.setOnClickListener {
viewModel.increaseCount()
binding.countText.text = viewModel.count.toString()
}
화면 회전 등의 이벤트가 발생하여 oncreate()가 새로 호출되도 count가 초기화되지 않고 유지된다.
위 처럼 설정하면 백그라운드로 앱을 전환하면 자동으로 kill 해준다.
이 경우에 앱을 백그라운드로 보내면 뷰모델도 같이 사라져 데이터가 초기화된다.
그렇다면 앱이 kill되어도 데이터를 보존하려면 어떻게 해야 할까?
SavedState ViewModel
로 가능하다.
SavedState ViewModel 은 디스크에 데이터를 저장하는 방식이다.
SavedState 뷰모델은 SavedState를 지원하는 ViewModel Factory를 인자로 하여 생성한다.
이 ViewModel Factory를 따라서 만들어줘야 한다.
ViewModel
// SavedStateHandle은 프로세스가 시스템에 의해 종료되더라도 유지된다.
// SavedStateHandle 객체는 Key-Value 형태의 Map 구조
class MainViewModel(private val handle:SavedStateHandle):ViewModel() {
//사용자의 클릭 수를 세는 변수
var count = handle.get<Int>("count") ?: 0
set(value){
handle.set("count", value)
field = value
}
//사용자가 클릭 했을 때 클릭수 를 증가시키는 메소드
fun increaseCount(){
count++
}
}
viewModel 가져와 사용
viewModel = ViewModelProvider(this,
SavedStateViewModelFactory(application, this)
)[MainViewModel::class.java]
고전적으로 뷰모델을 선언하지 않고
Fragment KTX를 활용하여 뷰모델을 선언할 수 있다.
KTX 통한 뷰 모델 초기화
private val viewModel: MainViewModel by viewModels() private val viewModel by viewModels<MainViewModel>()
위의 어느 방식을 사용해도 무방하다.
액티비티 안의 여러 프래그먼트가 있을 때 그 여러 액티비티가 공유하는 데이터는 activity의 viewmodel에 저장하고, 프래그먼트도 각자의 viewmodel을 가지고 있으면서
공유되는 데이터를 activityViewModels()
로 가져와 처리할 수 있다.
private val activityViewModel: NewActivityViewModel by activityViewModels()
1번 방법
2번 방법
뷰모델은 context를 인자로 받아와서 쓰면 안된다!(activity, fragment, view 객체 등도 포함)
activity가 종료되어 자원이 소멸될 때, 뷰모델의 자원도 자연스럽게 소멸되어야 하는데, 만약 내부에서 context를 참조하고 있다면 데이터 정리가 안됨.