AAC ViewModel이란?
우리는 안드로이드 앱을 설계할 때 MVVM 패턴을 많이 사용합니다. 오늘은 이 MVVM 패턴에서 빠질 수 없는 요소인 ViewModel을 쉽게 구현해주는 AAC의 ViewModel에 대해 알아보려고 합니다. AAC ViewModel을 사용하면 MVVM을 좀 더 쉽게 구현할 수 있습니다. (앞으로 나올 ViewModel은 AAC의 ViewModel을 의미)
ViewModel은 액티비티와 프래그먼트에서 사용되는 UI 관련 데이터를 보관하고, 관리하기 위해 디자인되었습니다.
이전에 액티비티가 재생성될 때 데이터를 유지할 수 없었습니다. (예 : 화면 회전) 하지만 ViewModel은 액티비티가 재생성되는 상황에서도 ViewModel 인스턴스를 유지함으로써 데이터를 안전하게 다룰 수 있습니다.
왜냐하면 ViewModel의 생명주기는 그림과 같기 때문입니다.
ViewModel은 액티비티 스코프의 싱글톤 객체처럼 사용할 수 있습니다. 이때문에 해당 Activity 위에 있는 Fragment들 사이에서 ViewModel을 이용해 데이터를 쉽게 공유할 수 있습니다. ViewModel은 Activity가 완전히 종료되는 시점에 종료됩니다. 이 때 호출되는 함수가 onCleared()입니다.
정리하면 ViewModel을 사용해 다음과 같은 이점을 얻을 수 있습니다.
- Activity 책임을 줄여줄 수 있다.
- Fragment 사이에서 데이터를 쉽게 공유할 수 있다.
ViewModel 사용법
ViewModel은 추상 클래스이며 이 클래스를 상속하는 것만으로 ViewModel을 만들 수 있습니다.
class SimpleViewModel : ViewModel() {
override fun onCleared() {
super.onCleared()
//여기서 ViewModel 종료 시 메모리 해제할 친구들을 해제해주자
}
}
그리고 Activity에서는 다음과 같이 사용하면 됩니다.
class MainActivity : AppCompatActivity() {
private lateinit var simpleViewModel: SimpleViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
simpleViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(SimpleViewModel::class.java)
}
}
ViewModelProvider는 해당 Activity와 ViewModel을 연결시켜주는 역할을 한다. 과정은 아래와 같습니다.
- ViewModelProvider를 통해 ViewModel 인스턴스를 요청
- ViewModelProvider 내부에서는 ViewModelStoreOwner를 참조하여 ViewModelStore를 가져옴
- ViewModelStore에게 이미 생성된 ViewModel 인스턴스를 요청
- 만약 ViewModelStore가 필요한 ViewModel 인스턴스를 가지고 있지 않다면 Factory를 통해 ViewModel 인스턴스 생성
- 생성한 ViewModel 인스턴스를 ViewModelStore에 저장하고 ViewModel 인스턴스를 클라이언트에게 반환
- ViewModel은 ViewModelStore라는 객체에서 관리합니다. (내부적으로 HashMap<String, ViewModel>를 두어 관리)
- ViewModelStore 객체는 ViewModelStoreOwner가 관리합니다. (ViewModelStoreOwner는 interface이고 ComponentActivity와 Fragment클래스가 이를 구현)
즉, ViewModel을 생성하기 위해서는 Fragment나 Activity가 필요하다는 것을 알 수 있습니다.
ViewModelProvider의 내부 구조는 다음과 같습니다.
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
simpleViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(SimpleViewModel::class.java)
이 부분을 다시 보면 this 부분은 ViewModelStoreOwner
가 필요하므로 이를 구현할 Fragment나 Activity가 필요하고 실질적으로 ViewModel을 생성해줄 Factory
가 필요합니다.
NewInstanceFactory() 같은 경우에는 안드로이드가 기본적으로 제공해주는 팩토리 클래스이고 생략이 가능합니다. get부분은 생성하고자 하는 ViewModel 클래스 타입을 넣어주면 됩니다.
하지만 나는 위에 방식보다는 by viewModels()를 통해 생성하는 것을 더 선호합니다. 하기 위해서는 아래의 종속성을 프로젝트에 추가해주어야 합니다.
android {
kotlinOptions {
jvmTarget = "1.8"
}
}
implementation "androidx.fragment:fragment-ktx:1.3.6"
그러면 아래와 같이 좀 더 깔끔하게 사용할 수 있습니다.
class MainActivity : AppCompatActivity() {
private val simpleViewModel by viewModels<SimpleViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Fragment 간에 데이터 공유
Fragment 간 데이터 공유는 by activityViewModels()를 사용하면 됩니다. 이를 사용하면 Fragment가 붙은 Activity에 ViewModel이 종속됩니다. (단, by activityViewModels()는 Fragment에서만 호출이 가능하다.)
아래는 공식 홈페이지의 예제 중 하나입니다.
class ListFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
ViewModel 사용 시 주의할 점
- ViewModel은 View, Fragment, Activity에 대한 Context를 저장해서는 안됩니다. ViewModel의 생명주기는 Activity에 종속되지 않기 때문에 이미 destroy된 component로 인해 메모리 릭이 발생할 수 있습니다. (단, Application Context를 저장하는 것은 문제가 되지 않는다.) ⇒ Application Context를 참조하고 싶다. 그러면 AndroidViewModel을 사용하면 됩니다.
- (권장사항) Activity/Fragment에 하나의 ViewModel만을 사용하는 것을 권장합니다. ⇒ 복잡도가 높아지기 때문에 하나의 ViewModel에 여러 LiveData를 다루는게 더 효율적입니다.
'개발 > Android' 카테고리의 다른 글
[Android] ViewModel Factory에 관하여 (1) | 2022.05.04 |
---|---|
[Android] Intent와 Bundle 무엇이 다를까? (feat. 객체 전달) (0) | 2022.04.29 |
[Android] 라이브러리 없이 ImageView에 URL 이미지 표시하기 (0) | 2022.04.08 |
[Android] Adapter에서 ViewModel 사용하기 (0) | 2022.03.30 |
[Android] 빌드 시에 No cached version available for offline mode 에러 해결방법 (0) | 2022.03.17 |
댓글