๋น์ฆ๋์ค ๋ก์ง ํน์ ํ๋ฉด ๋ ๋ฒจ์ ์ํ ๋ณด์ ์. ์ํ๋ฅผ 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๊ฐ ์๋๊ธฐ ๋๋ฌธ.