ViewModel을 생성할 때는 일반적으로 2가지 경우가 있습니다.
① 파라미터가 있는 경우
② 파라미터가 없는 경우
일반적으로 파라미터가 없는 경우는 아래와 같이 사용해주면 됩니다. (lifecycle-extensions
모듈 필요)
class SimpleViewModel : ViewModel(){
/** Content **/
}
생성하는 것은 2가지 방법으로 생성이 가능합니다.
class MainActivity : AppCompatActivity() {
private lateinit var simpleViewModel: SimpleViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//1번 방법
simpleViewModel = ViewModelProvider(this).get(SimpleViewModel::class.java)
//2번 방법
simpleViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(SimpleViewModel::class.java)
}
}
ViewModelProvider.NewInstanceFactory()
같은 경우는 ViewModelProvider.Factory
인터페이스를 구현하고 있습니다. 1번 또는 2번 방법을 자유롭게 사용하면 됩니다. 아니면 위임을 통해 생성하는 방법이 있습니다.
하지만 지금 관심이 있는 것은 파라미터가 있는 경우의 ViewModel 생성입니다. 지금까지는 Hilt나 Koin을 사용해서 생성을 했지만 없는 경우에는 어떻게 하는지 궁금하게 되었습니다. 그래서 찾아보니 다음과 같은 방법을 사용했습니다.
예제 코드는 아래의 Google에서 제공하는 Android 코드를 가지고 하였습니다.
https://developer.android.com/codelabs/kotlin-android-training-view-model?hl=ko#0
일단 파라미터가 있는 ViewModel을 생성하기 위해 Custom Factory가 필요했습니다. 이 경우 ViewModelProvider.Factory
를 implements 하여 구현을 해야 합니다.
create
함수를 오버 라이딩하고 modelClass가 ScoreViewModel를 구현하였다면 해당 viewModel 클래스를 반환해줍니다. 만약 ScoreViewModel 클래스가 아니라면 exceoption을 던져줍니다.
class ScoreViewModelFactory(private val finalScore : Int) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass.isAssignableFrom(ScoreViewModel::class.java)){
return ScoreViewModel(finalScore) as T
}
throw IllegalArgumentException("unKnown ViewModel class")
}
}
class ScoreViewModel(finalScore : Int) : ViewModel(){
}
그리고 해당 Activity나 Fragment에서 다음과 같이 사용해줍니다.
class ScoreFragment : Fragment() {
private lateinit var viewModel: ScoreViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate view and obtain an instance of the binding class.
val binding: ScoreFragmentBinding = DataBindingUtil.inflate(
inflater,
R.layout.score_fragment,
container,
false
)
viewModel = ViewModelProvider(this, ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(requireArguments()).score)).get(ScoreViewModel::class.java)
return binding.root
}
}
만약 KTX를 통한 위임 방식으로 사용한다면 object
키워드를 통해 Factory 클래스를 선언과 동시에 생성해줍니다.
class ScoreFragment : Fragment() {
private val scoreViewModel by viewModels<ScoreViewModel>(){
object : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return ScoreViewModel(ScoreFragmentArgs.fromBundle(requireArguments()).score) as T
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate view and obtain an instance of the binding class.
val binding: ScoreFragmentBinding = DataBindingUtil.inflate(
inflater,
R.layout.score_fragment,
container,
false
)
Log.i("SCORE", scoreViewModel.getScore())
return binding.root
}
}
결론적으로 파라미터가 있는 ViewModel을 생성할 때는 Factory 클래스를 커스텀할 필요가 있다고 느꼈습니다. 하지만 이를 사용하다 보니 ViewModel마다 해당하는 Factory 클래스를 만들어 주어야 해서 보일러 플레이트 코드가 많아지는 단점이 있었습니다. 그래서 의존성 주입 라이브러리인 Hilt나 Koin을 사용하면 좋겠다는 생각이 들었습니다.
'개발 > Android' 카테고리의 다른 글
[Android] 리스트 업데이트를 위한 ListAdapter (0) | 2022.06.07 |
---|---|
[Android] LiveData에 대해 알아보자 (0) | 2022.05.10 |
[Android] Intent와 Bundle 무엇이 다를까? (feat. 객체 전달) (0) | 2022.04.29 |
[Android] ACC ViewModel이란? - 정의, 사용법, 주의할점 (0) | 2022.04.16 |
[Android] 라이브러리 없이 ImageView에 URL 이미지 표시하기 (0) | 2022.04.08 |
댓글