안드로이드 os 설정이 바뀌면 activity를 다시 만듭니다.
액티비티 생명주기로는 onDestory 이후 onCreate가 실행됩니다.
viewmodel은 이런 경우를 대비해 기존에 UI가 가진 데이터를 가지고 있고 onCreate 할 때 다시 보여줄 수 있습니다..
설정이 바뀌는 경우는
- 화면 전환
- 언어 변경
- 멀티 윈도우 환경으로 변경
- 키보드 변경
등등이 있습니다.
ViewModel을 사용하지 않았을 경우 겪는 불편함
안드로이드 앱 개발자로서 os 설정 바뀌여서 앱이 다시 시작되는 상황을 고려하고 앱을 만들어야 합니다.
예를 들어 앱이 규모가 큰 앱인 경우 구성이 변경될 때마다 2000천 개의 리스트를 불러오는 비동기 호출을(API 호출) 다시 해야 합니다.
시스템 자원이 불필요하게 사용되고, 사용자는 다시 데이터를 받을 때까지 기다려야 하는 불편함을 겪습니다.
viewmodel
뷰에 대한 모델입니다. UI 관련 데이터를 저장하고 관리하기 위해 디자인되었습니다.
보통 하나의 activity 또는 fragment에 대해 하나의 뷰 모델을 사용합니다
때로는 2개 이상의 activity 또는 fragment가 하나의 뷰모델을 공유하기도 합니다.
뷰 모델은 액티비티가 생성될 때 메모리에 생기고, activity가 메모리에서 제거될 때까지 살아있습니다.
그래서 뷰 모델이 activity에 속한 값을 가지고 있을 수 있습니다.
사용법
화면을 회전해도 UI 관련 데이터를 가지고 있게 앱을 만들어 보겠습니다.
build.gradle
def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
MainActivityViewModel.kt
ViewModel은 ViewModel을 상속받아 만듭니다.
ViewModel 대신에 AndroidViewModel를 상속받아 뷰 모델을 만들 경우 Application instance를 생성자의 파라미터로 가져야 합니다.
class MainActivityViewModel() :ViewModel() {
private var count = 0
fun getCurrentCount():Int{
return count
}
fun getUpdatedCount(plusCount: Int):Int{
count+= plusCount
return count
}
}
MainActivity.kt
ViewModelProvider를 통해 ViewModel 객체를 생성합니다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
binding.countText.text = viewModel.getCurrentCount().toString()
binding.button.setOnClickListener {
binding.countText.text = viewModel.getUpdatedCount(Integer.parseInt(binding.countEt.text.toString())).toString()
}
}
}
viewmodel을 사용한 간단한 더하기 예제를 만든 결과입니다.
언제 뷰 모델의 onCleared() 함수가 호출될까?
앱이 백그라운드 상태로 들어갔거나 앱 프로세스가 시스템의 메모리 확보를 위해 죽은 경우에 호출됩니다.
커스텀 뷰 모델을 만드는 방법
커스텀 뷰 모델이 필요한 상황 → 특정 값을 뷰 모델로 전달해야 하는 경우
→ ViewModelFactoryClass를 사용하면 됩니다.
우리 앱에서는 초기 값이 0인데 초기 값을 지정해서 전달해보겠습니다.
MainActivityViewModel.kt
다음과 같이 뷰 모델의 기본 생성자을 통해 값을 전달합니다.
class MainActivityViewModel(startingTotal: Int) :ViewModel() {
private var count = 0
...
init {
count = startingTotal
}
...
}
MainActivityViewModelFactory.kt
ViewModelProvider.Factory를 상속받는 Factory 클래스를 통해 뷰 모델을 만들도록 합니다.
만약 뷰 모델에 파라미터를 전달해서 생성한다면 Factory 클래스의 생성자에도 같은 파라미터를 넣어줘야 합니다.
class MainActivityViewModelFactory(private val startingTotal: Int) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainActivityViewModel::class.java)){
return MainActivityViewModel(startingTotal) as T
}
throw IllegalArgumentException("Un")
}
}
MainActivity.kt
뷰 모델에 전달할 값을 viewModelFactory의 파라미터로 넣어 생성합니다.
viewmodel을 생성할 때는 ViewModelProvider에 viewModelFactory 객체를 인자로 넣습니다.
private lateinit var viewModel: MainActivityViewModel
private lateinit var viewModelFactory: MainActivityViewModelFactory
...
viewModelFactory = MainActivityViewModelFactory(1234)
viewModel = ViewModelProvider(this, viewModelFactory).get(MainActivityViewModel::class.java)
초기 값을 가지고 있고,
기기를 회전해도 값을 유지하는 것을 볼 수 있습니다.
참고 https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko
전체 소스는 깃헙에서 확인할 수 있습니다.
github.com/keepseung/Android-Blog-Source
'Android > Jetpack, Clean Architecture' 카테고리의 다른 글
DataBinding에 ViewModel, LiveData와 함께 사용하기 (0) | 2021.02.16 |
---|---|
[안드로이드/Android] LiveData 사용하기 (0) | 2021.01.25 |
[안드로이드/Android] Databinding 사용하기 (0) | 2021.01.20 |
모던 안드로이드 앱 만들기 (3) - Retrofit, RxJava를 이용한 네트워크 통신 (0) | 2021.01.18 |
모던 안드로이드 앱 만들기 (2) - MVVM 구조를 사용한 리스트 구현 (0) | 2021.01.17 |