ViewModel

홍승범·2023년 1월 23일
0

Android

목록 보기
5/9

ViewModel

비즈니스 로직 또는 화면 수준 상태를 유지하기 위한 홀더역할로, UI의 상태를 노출하고, 관련 비즈니스 로직을 캡슐화 할 수 있도록 하는 클래스

화면을 가로 세로 전환하는 경우, 안드로이드는 액티비티를 Destroy 하고 재 생성하게 되는데, 이때 기존 유지하던 데이터가 사라진다.

안드로이드는 onCreate콜백의 savedInstanceState 번들 파미터를 제공하여 개발자가 중요한 데이터는 유지할 수있도록 하고 있긴 하지만, 다음과 같은 문제점이 있다

  1. 데이터의 크기가 적다 : 50K 미만의 데이터를 권장
  2. 데이터의 형태가 제한적이다.
  3. onCreate에서 처리하므로 화면을 띄우는 시간에 영향이 있다.

이런 문제점을 해결하기 위해 데이터를 따로 홀딩하고 있도록 고안된 클래스이다.

생명주기

ViewModel은 viewModelStoreOwner가 사라질 때 까지 메모리에 남아있다.

ViewModelStoreOwner는 인터페이스로서 Activity와 Fragment가 ViewModelStoreOwner 인터페이스를 구현하고 있다.

따라서 ViewModel은 Activity가 종료되거나, 프래그먼트가 액티비티에서 분리되기 전까지 인스턴스가 유지된다고 할 수 있다.

아래 그림은 ViewModel의 생명주기를 액티비티 생명주기에 빗대어 나타낸 그림이다.

액티비티가 최초 onCreate 호출시 생성되어, 회전시에도 유지가 되고, 액티비티가 더이상 백스택에 남아있을 필요가 없는 finish시에 함께 사라진다.

사용의 이점

  1. 생명주기에 영향을 받지 않고 데이터를 유지할 수 있다
  2. UI 컨트롤러와 데이터가 분리된다.
  3. 프래그먼트간 데이터 공유가 쉬워진다.

ViewModel의 생성 및 사용

아래 이미지는 ViewModel을 얻어오는 과정을 나타낸 그림이다.

  1. ViewModelProvider를 통해 ViewModel인스턴스를 요청
  2. ViewModelProvider 내부에서는 요청한 viewModelStoreOwner를 참조하여 ViewModelStore를 가져온다.
  3. ViewModelStore에 ViewModel 인스턴스를 요청한다.
  4. ViewModelStore가 적합한 인스턴스를 가지고있지 않다면, Factory를 이용해 인스턴스를 생성
  5. 생성된 인스턴스를 ViewModelStore에 저장하고 저장된 인스턴스를 반환

일반적으로 액티비티에서 뷰모델 클래스를 가져오는 코드는 아래와 같다.

  class MainActivity : AppCompatActivity() {
      private lateinit var viewModel:MainViewModel
      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          //ViewModel 인스턴스 생성
          viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MainViewModel::class.java)
      }
  }
  • ViewModelProvider의 파라미터로 ViewModelStoreOwner와 팩토리를 넘겨주게 되는데, ViewModelStoreOwner는 Activity/Fragment이다. 실제로 사용할 Owner를 넘겨주면 된다
  • Factory는 ViewModelProvider에 이미 선언된 기존 팩토리 클래스를 이용해도 되고, ViewModel 생성시 생성자로 넘겨야 할 데이터가 있다면, 팩토리 클래스를 직접 구현하여 사용하면 된다.
  • 위의 ViewModel 사용시 이점중에 Fragment간 데이터 공유가 쉬워진다는 내용이 있었다. 관련해서 이야기 해 보자
    • ViewModel은 ViewModelStoreOwner마다 관리가 된다. 그래서 ViewModelProvider에 ViewModelStoreOwner를 파라미터로 전달해 주는 것이다
    • 프래그먼트에서 호스트 액티비티를 ViewModelStoreOwner로 넘겨준다면?
    • 그렇게 생성된 ViewModel은 ViewModelStoreOwner가 호스트 액티비티 이므로, 프래그먼트가 아니라, 액티비티의 라이프사이클을 따르게 된다.
    • 프래그먼트의 생명주기는 액티비티 생명주기의 서브셋이므로, 프래그먼트의 생명주기동안 자유롭게 공유가 가능하다..
    • 관련한 코드는 아래와 같다.
      override fun onAttach(context: Context) {
              viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().application))[YoutubeViewModel::class.java]
              super.onAttach(context)
          }
    • 이를통해 다음과 같은 장점을 누릴 수 있다.
      1. 액티비티는 프래그먼트간 커뮤니케이션에 개입하지 않아도 된다
      2. 프래그먼트는 뷰모델외에 다른부분에 대해서 의존하지 않아도 된다. 프래그먼트중 하나가 사라져도 나머지는 평소처럼 동작한다
      3. 각 프래그먼트는 다른 프래그먼트의 생명주기에 영향을 주거나 받지 않는다

ViewModel 사용시 주의사항

  1. 뷰 모델은 기본적으로 UI가 타내는 정보를 유지하기 위한 구성요소이므로 UI 구성요소 자체의 상태를 나타내서는 안됨
  2. ViewModelStoreOwner보다 오래 지속될 수 있으므로, Context나 Resource등의 참조를 보유해서는 안됨
  3. 뷰모델을 다른 클래스, 하수, 기타 UI 구성요소에 전달하면 안됨

AndroidViewModel

뷰 모델은 Context를 소유하지 않는것이 권장된다. 하지만, 그래도 context가 필요한 경우가 있으므로, 이를 위해 애플리케이션 생명주기동안 유효한 applicationContext를 제공해주는 뷰 모델 클래스가 AndroidViewModel 클래스이다. 아래와 같이 사용한다.

viewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application))
.get(MainViewModel::class.java)

AndroidViewModelFactory를 사용해 생성하는것이 다른점이다.

특히 주의할것은, AndroidViewModel 애플리케이션의 컨텍스트를 제공해 줄 뿐, ViewModelStoreOwner는 동일하게 액티비티/프래그먼트이므로, ViewModelStoreOwner의 라이프사이클에 맞춰 동작한다.

절대로 애플리케이션 전체의 뷰 모델역할을 하는것이 아니다..... 부끄럽다.....


https://todaycode.tistory.com/33
https://kotlinworld.com/79?category=971011
https://charlezz.medium.com/viewmodel%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-viewmodel-%EC%B4%88%EB%B3%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C-e1be5dc1ac18

profile
그냥 사람

0개의 댓글