๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Android

[Android] ViewModel - ๋ทฐ ๋ชจ๋ธ์— ์ธ์ž(Argument) ์ „๋‹ฌ (ViewModelFactory)

interfacer_han 2024. 1. 15. 16:24

#1 ViewModelProvider ํด๋ž˜์Šค ๋ถ„์„

#1-1 ์ˆ˜์ •ํ•  ์ƒ˜ํ”Œ ์•ฑ

 

[Android] View Model - ๊ธฐ์ดˆ

#1 View Model์˜ ํ•„์š”์„ฑ#1-1 ์˜ˆ์ œ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด TextView์˜ text๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ์˜ˆ์‹œ ์•ฑ์ด๋‹ค. MainActivity.kt ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. // package com.example.viewmodelbasics import androidx.appcompat.app.AppCompatActivity import andro

kenel.tistory.com

์œ„ ๊ฒŒ์‹œ๊ธ€์˜ '์™„์„ฑ๋œ ์•ฑ'์„ ์ˆ˜์ •ํ•ด์„œ, ViewModel ํด๋ž˜์Šค๊ฐ€ ์ธ์ž(Argument)๋ฅผ ๋ฐ›๊ฒŒ ๋งŒ๋“ค์–ด๋ณธ๋‹ค.

 

๊ทธ๋ƒฅ class sampleViewModel(args: Int) : ViewModel() { ... }์™€ ๊ฐ™์ด ์ธ์ž๋ฅผ ๋ฐ›๋Š” ViewModel ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ์“ฐ๋ฉด ๊ทธ๋งŒ ์•„๋‹ˆ๋ƒ๋Š” ์ƒ๊ฐ๋„ ๋“ ๋‹ค. ํ•˜์ง€๋งŒ ์ด์ „ ๊ธ€์— ๋”ฐ๋ฅด๋ฉด View Model์˜ ์ธ์Šคํ„ด์Šค๋Š” View Model์˜ ์ž์ฒด ์ƒ์„ฑ์ž๊ฐ€ ์•„๋‹ˆ๋ผ, ViewModelProvier์˜ ์ƒ์„ฑ์ž๋ฅผ ์ด์šฉํ•œ ๊ฐ„์ ‘์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ๋จผ์ €, ๊ทธ ๋ฐฉ๋ฒ•์ด๋ผ๋Š” ๊ฒƒ์„ ์ž์„ธํžˆ ์‚ดํŽด๋ณธ๋‹ค.

 

#1-2 ViewModelProvider์˜ ์ƒ์„ฑ์ž ์‚ดํŽด๋ณด๊ธฐ

...

public open class ViewModelProvider

/**
 * Creates a ViewModelProvider
 * Params:
 *     store - `ViewModelStore` where ViewModels will be stored.
 *     factory - factory a `Factory` which will be used to instantiate new `ViewModels`
 *     defaultCreationExtras - extras to pass to a factory
 */
constructor(
    private val store: ViewModelStore,
    private val factory: Factory,
    private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
) {
    ...

    /**
    * Creates ViewModelProvider. This will create ViewModels and retain them in a store of the given ViewModelStoreOwner.
    * This method will use the default factory if the owner implements HasDefaultViewModelProviderFactory. Otherwise, a NewInstanceFactory will be used.
    */
    public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

    /**
    * Creates ViewModelProvider, which will create ViewModels via the given Factory and retain them in a store of the given ViewModelStoreOwner. 
    * Params: 
    *     owner - a ViewModelStoreOwner whose ViewModelStore will be used to retain ViewModels
    *     factory - a Factory which will be used to instantiate new ViewModels
    */
    public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
        owner.viewModelStore,
        factory,
        defaultCreationExtras(owner)
    )

    ...

    public interface Factory {
        ...
    }

    public companion object {
        internal fun defaultFactory(owner: ViewModelStoreOwner): Factory = ...
        
        ...
    }
}

internal fun defaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras {
    ...
}

...

์ด 3๊ฐœ์˜ ์ƒ์„ฑ์ž๊ฐ€ ๋ณด์ธ๋‹ค. ๋งจ ์œ„์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ๋‹ค. ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์ธ์ž๋Š” 3๊ฐ€์ง€๋กœ ์ฒซ์งธ๋Š” ViewModelStore, ๋‘˜์งธ๋Š” ViewModelProvider ํด๋ž˜์Šค ๋‚ด๋ถ€์— ์ •์˜๋œ ์ธํ„ฐํŽ˜์ด์Šค์ธ ViewModelProvider.Factory, ์…‹์งธ๋Š” CreationExtras์ด๋‹ค.

 

๊ธฐ๋ณธ ์ƒ์„ฑ์ž ๋’ค์—, ๋ณด์กฐ ์ƒ์„ฑ์ž์ธ 2๋ฒˆ์งธ ์ƒ์„ฑ์ž, 3๋ฒˆ์งธ ์ƒ์„ฑ์ž๋„ ์žˆ๋‹ค. '2๋ฒˆ์งธ ์ƒ์„ฑ์ž'๊ฐ€ ์ด์ „ ๊ธ€์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ์ƒ์„ฑ์ž๋‹ค. '2๋ฒˆ์งธ ์ƒ์„ฑ์ž'๋Š” ViewModelStore๋งŒ ์ธ์ž๋กœ ๋ฐ›๊ณ  ๋‚˜๋จธ์ง€๋Š” ๊ธฐ๋ณธ๊ฐ’(๊ฐ๊ฐ ํ•จ์ˆ˜ defaultFactory(), defaultCreationExtras())์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. '3๋ฒˆ์งธ ์ƒ์„ฑ์ž'๋Š” ViewModelStore์™€ ViewModelProvider.Factory๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ  CreationExtras๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

์–ด๋Š ์ƒ์„ฑ์ž๋“  ๊ฐ„์— ViewModelProvider.Factory ํด๋ž˜์Šค๊ฐ€ ๋‚˜์˜จ๋‹ค. ViewModelProvider.Factory๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ ํŒจํ„ด(Factory Method Pattern)์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค๋‹ค. ์ฆ‰, ViewModelProvider๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์œผ๋กœ ViewModel ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์žˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, defaultFactory() ๋Œ€์‹  ๋‚ด๊ฐ€ ์ง์ ‘ ViewModelProvider.Factory์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ด์ „ ๊ธ€์˜ ์ฝ”๋“œ๋ฅผ '3๋ฒˆ์งธ ์ƒ์„ฑ์ž'๋ฅผ ์ด์šฉํ•ด ๋‹ค์‹œ ์งค ์ˆ˜ ์žˆ๋‹ค.

 

#2 '2๋ฒˆ์งธ ์ƒ์„ฑ์ž' ๋Œ€์‹  '3๋ฒˆ์งธ ์ƒ์„ฑ์ž' ์‚ฌ์šฉํ•˜๊ธฐ

#2-1 MainActivityViewModelFactory.kt ๋งŒ๋“ค๊ธฐ

// package com.example.argumenttoviewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class MainActivityViewModelFactory : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainActivityViewModel::class.java)) {
            return MainActivityViewModel() as T
        }
        throw IllegalArgumentException("Unknown View Model Class")
    }
}

defaultFactory๋ฅผ ๋Œ€์‹ ํ•  custom(์‚ฌ์šฉ์ž ์ •์˜) ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ์ธํ„ฐํŽ˜์ด์Šค ViewModelProvider.Factory์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค๋‹ค. create()๋ฅผ overrideํ•˜๊ณ  ์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ง ๋‹ค. ์ด๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ViewModelProvider.Factory์˜ ๊ตฌํ˜„ ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ‘œ์ค€๊ณผ๋„ ๊ฐ™์€ ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ผ๊ณ  ํ•œ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ '2๋ฒˆ์งธ ์ƒ์„ฑ์ž'๋ฅผ ์“ฐ๋Š” ๊ฒฝ์šฐ, ViewModelProvider๋Š” ์ด ๊ตฌํ˜„ ํด๋ž˜์Šค์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” defaultFactory()๋ฅผ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด ์ œ๊ณตํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค.

 

#2-2 MainActivity.kt ์ˆ˜์ •

...

class MainActivity : AppCompatActivity() {

    ...
    
    private lateinit var viewModelFactory: MainActivityViewModelFactory
    
    ...
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        ...
        
        viewModelFactory = MainActivityViewModelFactory()
        viewModel = ViewModelProvider(this, viewModelFactory).get(MainActivityViewModel::class.java) // '3๋ฒˆ์งธ ์ƒ์„ฑ์ž' ์‚ฌ์šฉ
        
        ...
    }
}

MainActivity.kt๋ฅผ ์ˆ˜์ •ํ•ด, MainActivityViewModelFactory์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  '2๋ฒˆ์งธ ์ƒ์„ฑ์ž' ๋Œ€์‹  '3๋ฒˆ์ž ์ƒ์„ฑ์ž'๋ฅผ ์‚ฌ์šฉํ•ด ViewModel์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค. ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ์œ ์ง€ํ•œ๋‹ค.

 

#2-3 ๊ตฌ๋™ ํ…Œ์ŠคํŠธ

์ด์ „๊ณผ ๋˜‘๊ฐ™์ด ์ž˜ ์ž‘๋™ํ•œ๋‹ค.

 

#3 ViewModel ๊ฐ์ฒด์— ์ธ์ž ์ „๋‹ฌํ•˜๊ธฐ

์‚ฌ์‹ค ์ด๋ฏธ #2์—์„œ ๋‹ต์ด ๋‚˜์™”๋‹ค. MainActivityViewModelFactory ํด๋ž˜์Šค๊ฐ€ ์ธ์ˆ˜๋ฅผ ๋ฐ›๋„๋ก ์ˆ˜์ •ํ•˜๋ฉด ๋์ด๋‹ค. ์ผ๊ฐ์—์„  ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๋•Œ๋งŒ ์ปค์Šคํ…€ ํŒฉํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ ๋‹ค๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ์ด๋Š” ์—„๋ฐ€ํ•˜๊ฒŒ๋Š” ๋…ผ๋ฆฌ์ ์œผ๋กœ ํ‹€๋ฆฐ ๋ง์ด๋‹ค. #2์—์„œ ์ธ์ˆ˜๊ฐ€ ์—†์–ด๋„ ์ปค์Šคํ…€ ํŒฉํ† ๋ฆฌ๋กœ ๋งŒ๋“ค์—ˆ์Œ์„ ๋ณด์ด์ง€ ์•Š์•˜๋Š”๊ฐ€. ๋”ฐ๋ผ์„œ, ๋ง์„ ๊น๊นํ•˜๊ฒŒ ๊ณ ์น˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ViewModel์— ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š์„๊ฑฐ๋ผ๋ฉด '๊ตณ์ด' ์ปค์Šคํ…€ ํŒฉํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค 'ํ•„์š”'๊ฐ€ ์—†๋‹ค.

ViewModel์— ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

#3-1 MainActivityViewModel.kt ์ˆ˜์ •

...

class MainActivityViewModel(startingCount : Int) : ViewModel() {
    private var count = 0

    init {
        count = startingCount
    }

    ...
}

init { ... } ๋ธ”๋ก์„ ์“ฐ์ง€ ์•Š๊ณ , private var count = startingCount ์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์งœ๋„ ๋œ๋‹ค. ํ˜น์€, init { ... } ๋ธ”๋ก๊ณผ private var count = 0 ๋‘˜๋‹ค ์—†์• ๋ฒ„๋ฆฌ๊ณ , class ์„ ์–ธ๋ถ€์— class MainActivityViewModel(private var count : Int) : ViewModel()๊ณผ ๊ฐ™์ด ์ ์–ด๋„ ๋œ๋‹ค.

 

#3-2 MainActivityViewModelFactory.kt ์ˆ˜์ •

...

class MainActivityViewModelFactory(private val startingCount: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainActivityViewModel::class.java)) {
            return MainActivityViewModel(startingCount) as T
        }
        throw IllegalArgumentException("Unknown View Model Class")
    }
}

 

#3-3 MainActivity.kt ์ˆ˜์ •

...

class MainActivity : AppCompatActivity() {

    ...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        
        viewModelFactory = MainActivityViewModelFactory(777)
        
        ...
    }
}

MainActivityViewModel.count์— 777์„ ์ „๋‹ฌํ•œ๋‹ค.

 

#3-4 ๊ตฌ๋™ ํ…Œ์ŠคํŠธ

 

#4 ์š”์•ฝ

ViewModel์˜ ์ƒ์„ฑ์ž๊ฐ€ ํŠน์ดํ•œ ํ˜•ํƒœ์ธ ์ด์œ ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.

 

#5 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/view-model/ArgumentToViewModel at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com