๋น์ฆ๋์ค ๋ก์ง ํน์ ํ๋ฉด ๋ ๋ฒจ์ ์ํ ๋ณด์ ์. ์ํ๋ฅผ UI์ ๋ ธ์ถํ๊ณ ๊ด๋ จ ๋น์ฆ๋์ค ๋ก์ง์ ์บก์ํํจ.
์ํ๋ฅผ ์บ์ฑํ๊ณ ๊ตฌ์ฑ ๋ณ๊ฒฝ์ ํตํด ์ํ ์ ์ง -> ํ๋ฉด์ ํ์ ํ๋ ๋ฑ์ ๊ตฌ์ฑ ๋ณ๊ฒฝ ์์๋ UI๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ค์ง ์์๋ ๋จ.
ViewModel์ด ๋ณด์ ํ๋ ์ํ์ ViewModel์ด ํธ๋ฆฌ๊ฑฐํ๋ ์์
์ ํตํด ์ง์์ฑ ์ ๊ณต.
์ด๋ฌํ ์บ์ฑ์ ํตํด ํ๋ฉด ํ์ ๊ณผ ๊ฐ์ ๊ตฌ์ฑ ์์ ๋ณ๊ฒฝ์ด ์ผ์ด๋ ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ฌ ํ์๊ฐ ์๊ฒ ํจ.
ViewModel์ ์์ฑํ ๋ ViewModelStoreOwner ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฐ์ฒด๋ฅผ ์ ๋ฌ. (Navigation ๋ชฉ์ ์ง, Navigation ๊ทธ๋ํ, ์กํฐ๋นํฐ, ํ๋๊ทธ๋จผํธ ๋๋ ํด๋น ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๋ค๋ฅธ ์ ํ์ผ ์ ์์.)
ViewModel์ ViewModelStoreOwner์ ์๋ช
์ฃผ๊ธฐ์ ๋ํ ๋ฒ์๋ก ์ค์ ๋จ.
ViewModelStoreOwner๊ฐ ์๊ตฌ์ ์ผ๋ก ์ฌ๋ผ์ง ๋๊น์ง ๋ฉ๋ชจ๋ฆฌ์ ์ ์ง๋จ.
ViewModel์ด ๋ฒ์๋ก ์ค์ ๋ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ๊ฐ ํ๊ดด๋ ๋, ViewModel์ด ํด๋น ๋ฒ์๋ก ์ค์ ๋์ด ์๋ ์ํ์์ ๋น๋๊ธฐ ์์ ์ด ๊ณ์๋จ.
SavedStateHandle์ ์ฌ์ฉํ๋ฉด ๊ตฌ์ฑ ๋ณ๊ฒฝ ๋ฟ๋ง์ด ์๋ ํ๋ก์ธ์ค ์ฌ์์ฑ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ง์์ํฌ ์ ์์.
์ฌ์ฉ์๊ฐ ์ฑ์ ๋ซ๊ณ ๋์ค์ ๋ค์ ์ด์ด๋ UI ์ํ๋ฅผ ๊ทธ๋๋ก ์ ์งํ ์ ์๊ฒ ํด์ค.
UI ๋ ์ด์ด์์ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ์ฌ๋ฐ๋ฅธ ์์น๋ ViewModel.
ViewModel์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ๋น์ฆ๋์ค ๋ก์ง์ด ์์ฉ ํ๋ก๊ทธ๋จ ๋ฐ์ดํฐ๋ฅผ ์์ ํด์ผ ํ ๋ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ค๋ฅธ ๋ ์ด์ด๋ก ์ด๋ฒคํธ๋ฅผ ์์ํ๋ ์ญํ ์ ํจ.
๋ทฐ๋ชจ๋ธ ์ฝ๋
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
์กํฐ๋นํฐ์์ ๋ทฐ๋ชจ๋ธ์ ์ ๊ทผ
import androidx.activity.viewModels
class DiceRollActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same DiceRollViewModel instance created by the first activity.
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val viewModel: DiceRollViewModel by viewModels()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}
//Jetpack Compose
import androidx.lifecycle.viewmodel.compose.viewModel
// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
viewModel: DiceRollViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// Update UI elements
}
ViewModel์ ํด๋น ViewModelStoreOwner๊ฐ ์ฌ๋ผ์ง ๋๊น์ง ๋ฉ๋ชจ๋ฆฌ์ ๋จ์์์.
์กํฐ๋นํฐ๊ฐ ์ข
๋ฃ๋ ๋
ํ๋๊ทธ๋จผํธ๊ฐ ๋ถ๋ฆฌ๋ ๋
**Navigation ํญ๋ชฉ์ด ๋ฐฑ ์คํ์์ ์ ๊ฑฐ๋ ๋
-> ๋ฐ๋ผ์ ViewModel์ ๊ตฌ์ฑ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ ์ ์งํด์ผ ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋๋ฐ ์ ํฉ.

ํ๋๊ทธ๋จผํธ์ ์๋ช ์ฃผ๊ธฐ์๋ ๋์ผํ๊ฒ ์ ์ฉ๋จ.
์ผ๋ฐ์ ์ผ๋ก ์ฒซ ๋ฒ์งธ๋ก ์์คํ
์ด ์กํฐ๋นํฐ ๊ฐ์ฒด์ onCreate() ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ViewModel์ ์์ฒญ. ์์คํ
์ ์กํฐ๋นํฐ ์กด์ฌ ๊ธฐ๊ฐ ๋์ ๊ธฐ๊ธฐ ํ๋ฉด์ด ํ์ ํ๋ ๊ฒฝ์ฐ ๋ฑ์ ์ด์ ๋ก onCreate()๋ฅผ ์ฌ๋ฌ ๋ฒ ํธ์ถ ๊ฐ๋ฅ.
ViewModel์ ๋งจ ์ฒ์์ผ๋ก ์์ฒญํ ๋๋ถํฐ ์กํฐ๋นํฐ๊ฐ ์๋ฃ๋๊ณ ํ๊ดด๋ ๋๊น์ง ์กด์ฌํจ.
ViewModel์ ViewModelStoreOwner๊ฐ ํ๊ดด๋ ๋ onCleared()๋ฉ์๋๋ฅผ ํธ์ถํจ.
์ด ๋ ViewModel์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๋ฐ๋ผ์ ๋ฐ์ํ๋ ๋ชจ๋ ์์
๊ณผ ์ข
์์ฑ์ ์ ๋ฆฌํ ์ ์์.
class MyViewModel(
private val coroutineScope: CoroutineScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {
// Other ViewModel logic ...
override fun onCleared() {
coroutineScope.cancel()
}
}
viewModelScope๋ ViewModel ์๋ช
์ฃผ๊ธฐ๋ฅผ ์๋์ผ๋ก ๋ฐ๋ฅด๋ ๋ด์ฅ๋ CoroutineScope.
ViewModelStoreOwner๊ฐ ์๋ช
์ฃผ๊ธฐ์ ๋์์ ViewModel์ ์ง์ฐ๋ฉด ViewModel์ CoroutineScope๋ฅผ ์ทจ์ํจ.
๋ผ์ดํ์ฌ์ดํด ๋ฒ์ 2.5 ์ด์์์๋ ViewModel์ ์์ฑ์์ ํ๋ ์ด์์ Closeable ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ์ฌ ViewModel ์ธ์คํด์ค๊ฐ ์ง์์ง ๋ ์๋์ผ๋ก ๋ซํ๋๋ก ํ ์ ์์.
class CloseableCoroutineScope(
context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
class MyViewModel(
private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
// Other ViewModel logic ...
}
Jetpack Compose๋ฅผ ์ฌ์ฉํ ๋ ViewModel์ ํ๋ฉด UI ์ํ๋ฅผ Composable์ ๋ ธ์ถํ๋ ์ฃผ์ ๋ฐฉ๋ฒ.
ViewModel์ Composable์ ๋ฒ์ ์ค์ ์ ํ ์ ์๋ค๋ ๊ฒ์ด ๊ฐ์ฅ ์ค์.
๐ Composable์ด ViewModelStoreOwner๊ฐ ์๋๊ธฐ ๋๋ฌธ.