๐ก ์๋๋ก์ด๋์ ๋ฐฑ์๋์์ ์ฝ๋ฃจํด ์ค์ฝํ๋ฅผ ์ด๋ป๊ฒ ๋ง๋ค๊ณ ํ์ฉํ๋์ง ํ์ตํ์์ต๋๋ค.
CoroutineScope ํฉํ ๋ฆฌ ํจ์
- CoroutineScope๋ coroutineContext๋ฅผ ์ ์ผํ ํ๋กํผํฐ๋ก ๊ฐ์ง๊ณ ์๋ ์ธํฐํ์ด์ค์ ๋๋ค.
interface CoroutineScope {
val coroutineContext: CoroutineContext
}
CoroutineScope ๊ฐ์ฒด ์์ฑ
- CoroutineScope ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๋ด๋ถ์์ ์ฝ๋ฃจํด ๋น๋๋ฅผ ์ง์ ํธ์ถํ ์ ์์ง๋ง, ์ด ๋ฐฉ๋ฒ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
- cancel์ด๋ ensureActivce ๊ฐ์ ๋ฉ์๋๋ฅผ ์ง์ ํธ์ถํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํจ
- ๊ฐ์๊ธฐ ์ ์ฒด ์ค์ฝํ๋ฅผ ์ทจ์ํ๋ฉด ์ฝ๋ฃจํด์ด ๋ ์ด์ ์์๋ ์ ์์
class SomeClass : CoroutineScope
- ๋์ ์ฝ๋ฃจํด ์ค์ฝํ ์ธํฐํ์ด์ค๋ฅผ ํ๋กํผํฐ๋ก ๊ฐ์ง๊ณ ์๋ค๊ฐ ์ฝ๋ฃจํด ๋น๋๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์ ํธ๋ฉ๋๋ค.
class SomeClass {
val scope: CoroutineScope = ...
}
- ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ CoroutineScope ํฉํ ๋ฆฌ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
- ์ด ํจ์๋ ์ปจํ ์คํธ๋ฅผ ๋๊ฒจ ๋ฐ์ ์ค์ฝํ๋ฅผ ์์ฑํฉ๋๋ค.
- ์ก์ด ์ปจํ ์คํธ์ ์์ผ๋ฉด ๊ตฌ์กฐํ๋ ๋์์ฑ์ ์ํด Job์ ์ถ๊ฐํ ์๋ ์์ต๋๋ค.
public fun CoroutineScope(
context: CoroutineContext
): CoroutineScope =
ContextScope(
if (context[Job] != null) context
else context + Job()
)
internal class ContextScope(
context: CoroutineContext
): CoroutineScope
- ์์ฑ์์ฒ๋ผ ๋ณด์ด๋ ํจ์๋ ๊ฐ์ง ์์ฑ์๋ก ๊ตฌํ๋์ด ์์ต๋๋ค.
- ์์ฑ์ ๋์ ํฉํ ๋ฆฌ ํจ์๋ฅผ ์ฌ์ฉํ๋ผ
์๋๋ก์ด๋์์ ์ค์ฝํ ๋ง๋ค๊ธฐ
- ๋๋ถ๋ถ์ ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ MVC ๋ชจ๋ธ์ ๊ธฐ๋ฐ์ผ๋ก ํ MVVM, MVP ์ํคํ ์ฒ๊ฐ ์ฌ์ฉ๋๊ณ ์์ต๋๋ค.
- ์ด๋ฌํ ์ํคํ
์ฒ์์๋ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ ์ฃผ๋ ๋ถ๋ถ์ ViewModels๋ Presenters์ ๊ฐ์ ๊ฐ์ฒด๋ก ์ถ์ถํฉ๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก ์ฝ๋ฃจํด์ด ๊ฐ์ฅ ๋จผ์ ์์๋๋ ๊ฐ์ฒด์ด๋ฉฐ, ์ ์ฆ ์ผ์ด์ค๋ ์ ์ฅ์์ ๊ฐ์ ๊ณ์ธต์์๋ ์ค๋จ ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ํ๋๊ทธ๋จผํธ๋ ์กํฐ๋นํฐ์์๋ ์์ํ ์ ์์ต๋๋ค.
- ์๋๋ก์ด๋์์ ์ด๋ค ๋ถ๋ถ์์ ์ฝ๋ฃจํด์ ์์ํ๋ ์ง ๊ฐ์ ์ฝ๋ฃจํด์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ ๋น์ทํฉ๋๋ค.
abstract class BaseViewModel : ViewModel() {
protected val scope = CoroutineScope(TODO())
}
class MainViewModel() : BaseViewModel {
fun onCrate() {
scope.launch {
// in CoroutineScope
}
}
}
์ค์ฝํ์์ ์ปจํ ์คํธ ์ ์ํ๊ธฐ
- ์๋๋ก์ด๋์์๋ ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋ง์ ์์ ํจ์๋ฅผ ํธ์ถํด์ผ ํ๋ฏ๋ก ๊ธฐ๋ณธ ๋์คํจ์ฒ๋ฅผ Dispatchers.Main์ผ๋ก ์ง์ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
abstract class BaseViewModel: ViewModel() {
protected val scope =
CoroutineScope(Dispatchers.Main + Job())
override fun onCleared() {
scope.cancel()
}
}
- ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ์๊ฐ ์คํฌ๋ฆฐ์ ๋๊ฐ๋ฉด, ์งํ ์ค์ธ ๋ชจ๋ ์์ ์ ์ทจ์ํด์ผ ํฉ๋๋ค.
- ๋ฐ๋ผ์ ์์ฒ๋ผ onCleared()๋ฅผ ํ์ฉํด ์ค์ฝํ๋ฅผ ์ทจ์ํ ์ ์์ต๋๋ค.
- ๋ํ ์์ ์ฝ๋ฃจํด๋ง์ ์ทจ์ํ๋ ๊ฒ์ด ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ฉฐ, ์์ ์ฝ๋ฃจํด๋ง ์ทจ์ํ๋ฉด ๋ทฐ ๋ชจ๋ธ์ด ์กํฐ๋ธํ ์ํ๋ก ์ ์ง๋๋ ํ, ๊ฐ์ ์ค์ฝํ์์ ์๋ก์ด ์ฝ๋ฃจํด์ ์์ํ ์ ์์ต๋๋ค.
override fun onCleared() {
scope.coroutineContext.cancelChildren()
}
- ๋ํ ์ฝ๋ฃจํด์ ๋
๋ฆฝ์ ์ผ๋ก ์๋์ํค๊ธฐ ์ํด์ SupervisorJob์ ํ์ฉํ ์ ์์ต๋๋ค.
- ์ฝ๋ฃจํด ํ๋๊ฐ ์ทจ์๋ ๊ฒฝ์ฐ ๋ถ๋ชจ์ ๋ค๋ฅธ ์์ ์ฝ๋ฃจํด์ด ํจ๊ป ์ทจ์๋๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
protected val scope =
CoroutineScope(Dispatchers.Main + SupervisorJob())
์กํ์ง ์์ ์์ธ ์ฒ๋ฆฌ
- ์๋๋ก์ด๋์์ ์๋์ ๊ฐ์ ์๋ฌ๋ฅผ ๋์ธ ์ ์์ต๋๋ค.
- HTTP ํธ์ถ๋ก 401 ์๋ฌ๋ฅผ ๋ฐ์ผ๋ฉด ๋ก๊ทธ์ธ ์ฐฝ์ ๋์์ผ ํฉ๋๋ค.
- 503 Service Unavailable์ ๊ฒฝ์ฐ ์๋ฒ์ ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค๋ ๋ฉ์ธ์ง๋ฅผ ๋ณด์ฌ์ผ ํฉ๋๋ค.
- BaseActivity์ ์์ธ ์ฒ๋ฆฌ ํธ๋ค๋ฌ๋ฅผ ํ ๋ฒ๋ง ์ ์ํด๋๊ณ ๋ทฐ ๋ชจ๋ธ์ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํฉ๋๋ค.
- ์กํ์ง ์์ ์์ธ๊ฐ ์๋ ๊ฒฝ์ฐ CoroutineExceptionHandler๋ฅผ ์ฌ์ฉํด ํด๋น ํจ์๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
abstract class BaseViewModel(
private val onError: (Throwable) -> Unit
): ViewModel() {
private val exceptionHandler =
CorouitneEcceptionHandler { _, throwable ->
onError(throwable)
}
private val context =
Dispathcers.Main + SupervisorJob() + exceptinoHandler
protected val scope = CoroutineScope(context)
}
viewModelScope์ lifecycleScope
- ์ต๊ทผ์๋ ์๋๋ก์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ค์ฝํ๋ฅผ ๋ฐ๋ก ์ ์ํ๋ ๋์ ์ viewModelScope์ lifecycleScope๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- Dispatchers.Main๊ณผ SupervisorJob์ ์ฌ์ฉํ๊ณ , ๋ทฐ ๋ชจ๋ธ์ด๋ ๋ผ์ดํ์ฌ์ดํด์ด ์ข ๋ฃ๋์์ ๋ ์ก์ ์ทจ์์ํจ๋ค๋ ์ ์์ ์ ์ฝ๋์ ๋งค์ฐ ๋์ผํ๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
viewModelScope.launch {
// on coroutine
}
๋ฐฑ์๋์์ ์ฝ๋ฃจํด ๋ง๋ค๊ธฐ
- ๋ฐฑ์๋ ํ๋ ์์ํฌ์์ ์ค๋จ ํจ์๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํฉ๋๋ค.
- Ktor์์ ๋ชจ๋ ํธ๋ค๋ฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ค๋จ ํจ์๋ฅผ ํ์ฉํ๋ฉฐ, ๋ฐ๋ก ์ค์ฝํ๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค.
suspend fun delay(timeMillis: Long) {...}
suspend fun someNetworkCallReturningValue(): SomeType {
...
}
์ถ๊ฐ์ ์ธ ํธ์ถ์ ์ํ ์ค์ฝํ ์์ฑ
- ์ถ๊ฐ์ ์ธ ์ฐ์ฐ์ ์์ํ๊ธฐ ์ํ ์ค์ฝํ๋ฅผ ์ข ์ข ๋ง๋ค๊ณค ํฉ๋๋ค.
- ์ด๋ฐ ์ค์ฝํ๋ ํจ์๋ ์์ฑ์์ ์ธ์๋ฅผ ํตํด ์ฃผ์ ๋๋ฉฐ, ์ค์ฝํ๋ฅผ ํธ์ถ ์ค๋จํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก๋ง ์ฌ์ฉํ๋ ค๋ ๊ฒฝ์ฐ SupervisorScope๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ง์ผ๋ก ์ถฉ๋ถํฉ๋๋ค.
val analyticsScope = CoroutineScope(SupervisorJob())
- CoroutineExceptionHandler๋ฅผ ์ฌ์ฉํด์ ์์ธ๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
private val exceptionHandler =
CoroutineExceptionHandler { _, throwable ->
FirbaseCrashlytics.getInstance()
.recordException(throwable)
}
val analyticsScope = CoroutineScope(
SupervisorJob() + exceptionHandler
)
- ๋ํ ๋ค๋ฅธ ๋์คํจ์ฒ๋ฅผ ์ค์ ํจ์ผ๋ก์จ ์ปค์คํ ํ ์ ์์ต๋๋ค.
- ์ค์ฝํ์์ ๋ธ๋กํน์ ํธ์ถํ๋ค๋ฉด Dispathcers.IO๋ฅผ ์ฌ์ฉํ๊ณ , ์๋๋ก์ด๋ ๋ฉ์ธ ๋ทฐ๋ฅผ ๋ค๋ค์ผ ํ๋ค๋ฉด Dispathcers.Main์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
val analyticsScope = CoroutineScope(
SupervisorJob() + Dispathcers.IO
)