반응형
1. 새 코루틴을 만들거나 withContext를 호출할때 Dispatchers를 하드코딩하지 마세요. -> 코드의 유연성, 테스트 용이성을 위함. 직접 하드코딩하게 되면 테스트나 리팩토링이 어려워진다.
하드코딩된 Dispatchers
import kotlinx.coroutines.*
fun fetchData() {
GlobalScope.launch(Dispatchers.IO) {
// 일부 오래 동작하는 작업
val data = performNetworkRequest()
withContext(Dispatchers.Main) {
// UI 업데이트
updateUI(data)
}
}
}
하드코딩을 피하는 방법
1) 의존성 주입을 사용하여 Dispatchers를 주입하는 방법
디스패처를 주입할 수 있는 클래스 만들기
class CoroutineDispatchers(
val io: CoroutineDispatcher = Dispatchers.IO,
val main: CoroutineDispatcher = Dispatchers.Main
)
이 디스패처 클래스를 의존성으로 주입
class MyRepository(private val dispatchers: CoroutineDispatchers) {
suspend fun fetchData() {
withContext(dispatchers.io) {
val data = performNetworkRequest()
withContext(dispatchers.main) {
updateUI(data)
}
}
}
}
val testDispatchers = CoroutineDispatchers(
io = Dispatchers.Unconfined, // 테스트용 디스패처
main = Dispatchers.Unconfined // 테스트용 디스패처
)
val repository = MyRepository(testDispatchers)
2) 함수 인자로 Dispatchers를 전달하는 방법
suspend fun fetchData(dispatcherIO: CoroutineDispatcher = Dispatchers.IO, dispatcherMain: CoroutineDispatcher = Dispatchers.Main) {
withContext(dispatcherIO) {
val data = performNetworkRequest()
withContext(dispatcherMain) {
updateUI(data)
}
}
}
runBlocking {
fetchData(Dispatchers.Unconfined, Dispatchers.Unconfined)
}
2. 정지 함수는 기본 스레드에서 호출하기에 안전해야 함
네트워크 요청을 'Dispatchers.IO'에서 처리하고 UI업데이트를 'Dispatchers.Main' 에서 처리하는 예제
import kotlinx.coroutines.*
class NetworkService {
suspend fun fetchUserData(userId: String): UserData = withContext(Dispatchers.IO) {
// 네트워크 요청을 통해 사용자 데이터 가져오기 (이 예제에서는 가정)
simulateNetworkRequest(userId)
}
}
class UserProfileViewModel(private val networkService: NetworkService) {
fun fetchAndDisplayUserData(userId: String) {
// ViewModel에서 launch는 기본적으로 Dispatchers.Main에서 실행됨
viewModelScope.launch {
try {
val userData = networkService.fetchUserData(userId)
displayUserData(userData)
} catch (e: Exception) {
handleError(e)
}
}
}
private fun displayUserData(userData: UserData) {
// UI 업데이트
}
private fun handleError(e: Exception) {
// 에러 처리
}
}
data class UserData(val id: String, val name: String)
suspend fun simulateNetworkRequest(userId: String): UserData {
delay(1000) // 네트워크 지연 시뮬레이션
return UserData(userId, "John Doe")
}
`fetchUserData` 정지 함수는 `Dispatchers.IO`를 통해 네트워크 요청을 수행하고, `fetchAndDisplayUserData` 함수는 기본 스레드(UI 스레드)에서 호출되어도 안전하게 작동합니다. 데이터가 불러와지면 `Dispatchers.Main`에서 UI 업데이트를 수행합니다.
3. ViewModel은 코루틴을 만들어야 함 - 정지 함수를 노출하는 대신 코루틴을 만들게 되는데 데이터 스트림을 사용해 상태를 노출하는 하나의 값만 방출하는 경우 ViewModel의 정지 함수가 유용하다.
// DO create coroutines in the ViewModel
class LatestNewsViewModel(
private val getLatestNewsWithAuthors: GetLatestNewsWithAuthorsUseCase
) : ViewModel() {
private val _uiState = MutableStateFlow<LatestNewsUiState>(LatestNewsUiState.Loading)
val uiState: StateFlow<LatestNewsUiState> = _uiState
fun loadNews() {
viewModelScope.launch {
val latestNewsWithAuthors = getLatestNewsWithAuthors()
_uiState.value = LatestNewsUiState.Success(latestNewsWithAuthors)
}
}
}
반응형
'Android' 카테고리의 다른 글
Android Compose TextField 천 단위 콤마로 표현하기 (0) | 2024.10.24 |
---|---|
Android ANR / Canvas / 메모리 초과 해결방법 (0) | 2024.08.28 |
Compose Box Basic Style (0) | 2024.06.03 |
RoomDatabase Hilt Error -> AppDatabase. AppDatabase_Impl does not exist at androidx.room.Room.getGeneratedImplementation(Room.kt:58) .. (0) | 2024.04.14 |
RecyclerView 하단 divider 깜빡임 | RecyclerView bottom divider blinking (0) | 2024.03.26 |
댓글