본문 바로가기

Android/Jetpack, Clean Architecture

[안드로이드/Android] ViewModel 사용하기

안드로이드 os 설정이 바뀌면 activity를 다시 만듭니다.

액티비티 생명주기로는 onDestory 이후 onCreate가 실행됩니다.

viewmodel은 이런 경우를 대비해 기존에 UI가 가진 데이터를 가지고 있고 onCreate 할 때 다시 보여줄 수 있습니다..

설정이 바뀌는 경우는

  1. 화면 전환
  2. 언어 변경
  3. 멀티 윈도우 환경으로 변경
  4. 키보드 변경

등등이 있습니다.

 

ViewModel을 사용하지 않았을 경우 겪는 불편함

안드로이드 앱 개발자로서 os 설정 바뀌여서 앱이 다시 시작되는 상황을 고려하고 앱을 만들어야 합니다.

예를 들어 앱이 규모가 큰 앱인 경우 구성이 변경될 때마다 2000천 개의 리스트를 불러오는 비동기 호출을(API 호출) 다시 해야 합니다.

시스템 자원이 불필요하게 사용되고, 사용자는 다시 데이터를 받을 때까지 기다려야 하는 불편함을 겪습니다.

 

viewmodel

뷰에 대한 모델입니다. UI 관련 데이터를 저장하고 관리하기 위해 디자인되었습니다.

보통 하나의 activity 또는 fragment에 대해 하나의 뷰 모델을 사용합니다

때로는 2개 이상의 activity 또는 fragment가 하나의 뷰모델을 공유하기도 합니다.

뷰 모델은 액티비티가 생성될 때 메모리에 생기고, activity가 메모리에서 제거될 때까지 살아있습니다.

그래서 뷰 모델이 activity에 속한 값을 가지고 있을 수 있습니다.

viewmodel lifecycle

 

사용법

화면을 회전해도 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

 

ViewModel 개요  |  Android 개발자  |  Android Developers

ViewModel을 사용하면 수명 주기를 인식하는 방식으로 UI 데이터를 관리할 수 있습니다.

developer.android.com

 

전체 소스는 깃헙에서 확인할 수 있습니다.

github.com/keepseung/Android-Blog-Source

 

keepseung/Android-Blog-Source

https://develop-writing.tistory.com/ 에서 제공하는 예제. Contribute to keepseung/Android-Blog-Source development by creating an account on GitHub.

github.com