ViewModel 상태 관리

강성우, 10 June 2020

ViewModel 에서는 ViewModel 을 가져올때 ViewModelProvider를 통해 LifeCycle 을 지정 하여 Activity 나 Fragment 의 생명주기와 동기화된 활동을 보여준다. 아래 그 생명주기를 참조하면 알 수 있다.

vm_lifecycle

Activity 혹은 Fragment 에서 ViewModel 의 인스턴스를 생성 하고 사용 할 때 ViewModel 내부의 LiveData 등은 Finished 상태가 되기 전 까지는 유효한 것 이다.

하지만 앱 화면의 회전과 같은 destroy - re create 의 과정을 밟게 된다면 어떻게 될 것인가? 일반적으로 이 경우에는 onSaveInstanceState() 등의 콜백을 구현 해서 Bundle 객체에 키-값 형태로 다시 화면에 그려져야 할 데이터들을 임시로 저장했다가 onCreate 등 콜백 시점에 다시 세팅하여 화면을 업데이트 하는 방식을 사용 했었다.

하지만 이 방법은 기존 아키텍쳐 에서는 유효했지만 MVVM 등과 같은 아키텍쳐에서는 유효하지 않다. 몰론 Activity 나 Fragment 등 에서 해당 콜백을 ViewModel 의 인스턴스에 직접 알리는 방식이 있겠지만 안드로이드 에서 제공 하는 방법을 사용 해 보도록 하자.

설정 및 사용 방법

ViewModelProvider 에서 ViewModel 을 가져올 때 AbstractSavedStateVMFactory 를 상속, 구현한 인스턴스를 설정 해 주면 해당 팩토리에 의해 ViewModel 에서 SavedStateHandle 을 사용 할 수 있다.

class SavedStateViewModel(
  private val state: SavedStatehandle
): ViewModel() {
  
  fun getText(): LiveData<String> {
    return state.getLiveData("KEY_TEXT")
  }
  
  fun saveText(text: String) {
    state["KEY_TEXT"] = text
  }
}

class SomeFragment: Fragment() {
  private lateinit var vm: SavedStateViewModel
  
  override fun onCreate() {
    // ...
    vm = ViewModelProvider(this, SavedStateVMFactory(this)).get(SavedStateViewModel::class.java)    
  }
}

SavedStateHandle

SavedStateHandle 에는 다음과 같은 함수들을 제공한다.

  • get(key: String)
  • contains(key: String)
  • remove(key: String)
  • set(key: String, value: T)
  • keys()

위 함수들 외 에도 LiveData 로 래핑된 getLiveDate(key: String) 도 사용 할 수 있다. 함수들을 보면 알겠지만 외부에서 이 클래스를 사용 할 때 Map 처럼 사용됨을 알 수 있다.

허용되는 클래스들

SavedStateHandle 에서 저장되는 데이터들은 Bundle 로 저장되므로 기존 Bundle 사용법과 동일 하다. 비트맵 처럼 대용량보다는 작은 데이터들을 임시적으로 사용한다고 생각 하는게 좋을 것 같다.

기타

구글 예제를 보면 실제 테스트 예를 볼 수 있는데 adb kill 명령어를 이용하여 프로세스를 죽이고 다시 앱을 열어 RUNNING 시점 으로 다시 시작해보면 저장한 데이터들이 잘 복구됨을 확인 할 수 있다.