이 글은 코루틴 베스트 프랙티스 구글 문서를 살짝 요약한 글입니다.

 

코루틴 Best Practice

1.Inject Dispatchers

// DO inject Dispatchers
class NewsRepository(
    private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
    suspend fun loadNews() = withContext(defaultDispatcher) { /* ... */ }
}

// DO NOT hardcode Dispatchers
class NewsRepository {
    // DO NOT use Dispatchers.Default directly, inject it instead
    suspend fun loadNews() = withContext(Dispatchers.Default) { /* ... */ }
}

Dispatcher 선언시 직접 dispatcher를 하드코딩하지말고 생성자로 만들어서 프로그래머가 원하는 dispatcher를 넣을 수 있도록 하자.

 

2. 코루틴은 background thread에서 실행하라

Making a network request on the main thread causes it to wait, or block, until it receives a response. Since the thread is blocked, the OS isn't able to call onDraw(), which causes your app to freeze and potentially leads to an Application Not Responding (ANR) dialog. For a better user experience, let's run this operation on a background thread.

 

3.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)
        }
    }
}

// Prefer observable state rather than suspend functions from the ViewModel
class LatestNewsViewModel(
    private val getLatestNewsWithAuthors: GetLatestNewsWithAuthorsUseCase
) : ViewModel() {
    // DO NOT do this. News would probably need to be refreshed as well.
    // Instead of exposing a single value with a suspend function, news should
    // be exposed using a stream of data as in the code snippet above.
    suspend fun loadNews() = getLatestNewsWithAuthors()
}

단순히 suspend fun을 노출시켜 사용하기 보다는 viewmodel에서 coroutine의 값을 받아와 노출시키는것이 훨씬 좋다

 

4. Mutable 변수를 노출시키지 말자

// DO expose immutable types
class LatestNewsViewModel : ViewModel() {

    private val _uiState = MutableStateFlow(LatestNewsUiState.Loading)
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    /* ... */
}

class LatestNewsViewModel : ViewModel() {

    // DO NOT expose mutable types
    val uiState = MutableStateFlow(LatestNewsUiState.Loading)

    /* ... */
}

 

5.데이터 및 비지니스 레이어는 suspend 함수 및 flows를 노출시키자

// Classes in the data and business layer expose
// either suspend functions or Flows
class ExampleRepository {
    suspend fun makeNetworkRequest() { /* ... */ }

    fun getExamples(): Flow<Example> { /* ... */ }
}

 

5.1 데이터 및 비지니스 레이어에서는 coroutinescope 혹은 supervisorscope를 사용하는것이 좋다.

 

6. 코루틴을 cancellable하게 만들자

코루틴은 job이 캔슬 되더라도 suspend되거나 cancellation이 check되기 전까지는 coroutine이 cancel되지 않는다. 그래서 coroutine을 cancellable하게 만드는것이 중요하다. 

someScope.launch {
    for(file in files) {
        ensureActive() // Check for cancellation
        readFile(file)
    }
}

 

7. 코루틴 exception을 조심하자

코루틴이 unexpected exception을 만날 경우에는 이를 핸들링 할 수 있는 코드가 필요하다.

class LoginViewModel(
    private val loginRepository: LoginRepository
) : ViewModel() {

    fun login(username: String, token: String) {
        viewModelScope.launch {
            try {
                loginRepository.login(username, token)
                // Notify view user logged in successfully
            } catch (exception: IOException) {
                // Notify view login attempt failed
            }
        }
    }
}
728x90
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기