ViewModel

최희창·2022년 5월 25일
0

Android AAC

목록 보기
2/13

ViewModel

  • ViewModel은 앱의 Lifecycle을 고려하여 UI 관련 데이터를 저장하고 관리하는 컴포넌트입니다.

ViewModel은 왜 필요할까?

  • ViewModel은 View로부터 독립적이며, View가 필요로 하는 데이터만을 소유한다.
  • UI Controller로부터 UI 관련 데이터 저장 및 관리를 분리하여 ViewModel이 담당하도록 하면 다음과 같은 문제를 해결할 수 있습니다.
    • 안드로이드 프레임워크는 특정 작업이나 완전히 통제할 수 없는 기기 이벤트에 대한 응답으로 UI Controller를 제거하거나 다시 생성할 수 있는데, 이 경우 UI Controller에 저장된 모든 일시적인 관련 UI 데이터가 삭제됩니다.
    • UI Controller에서 데이터를 위한 비동기 호출을 한다면 메모리 누수 가능성을 방지하기 위한 많은 유지 관리가 필요합니다.
    • UI Controller에서 DB나 네트워크로부터 데이터를 로드하도록 하면 단일 클래스가 많은 작업을 처리하게 될 수도 있습니다. 이러면 테스트가 훨씬 어려워지게 됩니다.

ViewModel의 특징

  • ViewModel은 Activity에서는 Activity가 완전히 종료될 때 까지, 그리고 Fragment에서는 Fragment가 분리될 때 까지 메모리에 남아있도록 설계되어 있다.
  • ViewModel 객체는 뷰 또는 LifecycleOwners보다 오래 지속되도록 설계되었으며, ViewModelProvider에 전달되는 LifecycleOnwers에 따라 Lifecycle이 달라집니다.
  • 만약 액티비티가 전달되었다면 액티비티가 종료될 때까지, 프래그먼트가 전달되었다면 프래그먼트가 분리될 때까지 ViewModel은 메모리에 남아 있습니다.

ViewModel 요청 프로세스

  1. ViewModelProvider를 통해 ViewModel 인스턴스를 요청한다.
  2. ViewModelProvider 내부에서는 ViewModelStoreOwner를 참조하여 ViewModelStore를 가져온다.
  3. ViewModelStore에게 이미 생성된(저장된) ViewModel 인스턴스를 요청한다.
  4. 만약 ViewModelStore가 적합한 ViewModel 인스턴스를 가지고 있지 않다면, Factory를 통해 ViewModel 인스턴스를 생성한다.
  5. 생성한 ViewModel 인스턴스를 ViewModelStore에 저장하고 만들어진 ViewModel 인스턴스를 클라이언트에 반환한다.
  6. 똑같은 ViewModel 인스턴스 요청이 들어온다면, 1~3번의 과정을 반복하게 된다.

ViewModel의 구현

1. kotlin의 by 키워드 이용

(해당 ViewModel이 초기화 되는 액티비티,혹은 프래그먼트의 생명주기에 완전히 종속)

private val viewModel: MyViewModel by viewModels()
  • 보통은 1번의 방법을 많이 사용하지만, 프래그먼트들 사이에서 데이터 공유를 해야하는 상황이 나올때 2번을 사용한다.

2. ViewModelProvider를 이용하여 초기화

(지정한 컴포넌트의 생명주기에 종속)

val viewModel = ViewModelProvider(
      this,
      ViewModelProvider.NewInstanceFactory())
      .get(MyViewModel::class.java)
  • 기본적으로 ViewModel는 추상클래스로, new 키워드로 객체 생성이 불가하다. 그렇기때문에 ViewModelProvider을 통해 객체 생성을 해야 한다. 일반적으로 ViewModelProvider의 매개변수로 ViewModelStoreOwnwer와 ViewModelProvider.Factory가 필요하다.

  • ComponentActivity와 ComponentActivity의 서브클래스인 AppCompatActivity는 별도로 ViewModelStoreOwnwer 인터페이스를 구현하고 있다. Fragment 또한 마찬가지이다.

  • 생성자에 매개변수가 있는 ViewModel을 인스턴스화 하는 경우에는 ViewModelProvider.Factory를 구현해야 한다.

(참고 : ViewModel은 액티비티 혹은 프래그먼트 내에서 1개만 생성이 가능하다. 즉, 액티비티 내의 싱글톤 객체로 아무리 여러번 생성해도 하나의 객체만 계속 사용된다.)

AndroidViewModel

  • 기본적으로 ViewModel은 액티비티나 프래그먼트에 의존적인데, 개발을 하다보면 이를 넘어 Context를 사용해야 할 때가 있다. 이럴 때 액티비티나 프래그먼트를 참조하면 메모리 릭이 발생한다.
  • 기본적으로 Activity와 Fragment보다 긴 생명주기를 가지고 있는데, 여기서 이에 대한 참조를 가지고 있다면, 아무리 사라져도 ViewModel은 사라지지 않고 메모리를 차지하고 있을 것이다.
  • 그렇기 위해서 AndroidviewModel을 사용하는데, 이 클래스는 Application에 의존적이다. 즉, 특정 액티비티나 프래그먼트가 사라지더라도 인스턴스가 유지되고, Application이 종료되는 시점에 onCleared()가 호출되어 데이터가 사라지게 된다.

ViewModel을 이용한 Fragment간의 Data 공유

  • 액티비티내에서 프래그먼트 간의 데이터 또는 이벤트를 공유하는 것은 일반적이다. ViewModel을 사용하면 이를 좀 더 쉽게 해결할 수 있다.

  • SharedViewModel에 두 프래그먼트가 동시에 접근하고 있다. SharedViewModel 인스턴스를 얻을 때 ViewModelStoreOwnwer를 부모 Activity로 지정하자. 그러면 SharedViewModel의 생명주기는 Activity를 따르게 되고, Fragment의 생명주기는 Activity의 서브셋이므로 Fragment의 생명주기 동안에 자유롭게 데이터를 공유할 수 있게 된다.
  • 다음과 같은 장점을 가질 수 있다.
    • Activity는 Fragment 커뮤니케이션에 개입하지 않아도 된다.
    • Fragment는 SharedViewModel 외에 다른 부분에 대해서 의존하지 않아도 된다.
    • 각 프래그먼트는 자체 생명주기를 가지고 있고, 각자 영향을 주거나 받지 않는다.
      -> 결국 Activity와 Fragment들이 강하게 결합되어 있던 부분을 느슨하게 만드는 효과를 주면서 유지보수나 확장성 측면에서 많은 이득을 볼수 있게 해준다.

ViewModel 코드

class MyViewModel : ViewModel() {

    private val users: MutableLiveData<List<User>> by lazy {
        loadUsers()
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers(): MutableLiveData<List<User>> {
        return MutableLiveData(listOf(User("name", "number")))
    }
}
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val vm: MyViewModel by viewModels()
        vm.getUsers().observe(this, Observer { users ->
            //update UI
        })
    }
}

ViewModel을 생성하는 법

profile
heec.choi

0개의 댓글