Irbisa · Шпаргалка
Корутины Kotlin
Suspend-функции, scope, dispatcher-ы, структурная конкурентность, отмена, обработка исключений
Что такое корутины, чем они отличаются от потоков и почему их называют легковесными?
fun main() = runBlocking { repeat(100_000) { // 100k threads would run out of memory launch { delay(1_000) // suspends: the thread serves other coroutines print(".") }…Что делают
runBlockingиGlobalScopeи почему им не место в коде Android-приложения?Чем
launchотличается отasyncи как по-разному они обрабатывают исключения?class ProfileVM(private val api: Api) : ViewModel() { fun save(p: Profile) { viewModelScope.launch { api.save(p) } // Job, no result } fun load() = viewModelScope.launch {…Что такое structured concurrency и почему это важно на Android?
Чем
CoroutineScopeотличается отCoroutineContextи какие элементы хранит контекст?Какой диспетчер выбрать для CPU-работы, блокирующего I/O и обновления UI и чем
Dispatchers.Defaultотличается отDispatchers.IO?class ReportRepo( private val dao: ReportDao, private val io: CoroutineDispatcher = Dispatchers.IO, // injectable for tests) { private val writer = io.limitedParallelism(1) // one writer at a time…Что на самом деле делает ключевое слово
suspendи во что компилятор превращает suspend-функцию?// What you writesuspend fun showProfile(id: Long) { val user = api.user(id) // suspension point 1 val posts = api.posts(user.id) // suspension point 2 render(user, posts)}…Когда использовать
withContext, а когдаasync/await, и как запустить три независимых сетевых вызова параллельно?suspend fun loadDashboard(): Dashboard = coroutineScope { val user = async { api.user() } val feed = async { api.feed() } val badges = async { api.badges() } Dashboard(user.await(), feed.await(), badges.await()) // all three overlap}…Поймает ли
try/catchв этом коде исключение и как правильно обрабатывать исключения в корутинах?fun load() { try { viewModelScope.launch { val feed = api.feed() // throws IOException _state.value = FeedState.Ready(feed) } } catch (e: IOException) { _state.value = FeedState.Error(e) }}Как работает отмена корутин, почему она кооперативная и почему нельзя глотать
CancellationException?suspend fun hashAll(files: List<File>): List<String> = withContext(Dispatchers.Default) { files.map { file -> ensureActive() // CPU loop: stop here if cancelled sha256(file) }}…Чем отличаются
viewModelScope,lifecycleScopeиrepeatOnLifecycleи почемуlaunchWhenStartedобъявили устаревшим?class ChatFragment : Fragment(R.layout.fragment_chat) { private val vm: ChatViewModel by viewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {…Как обернуть API на колбэках в suspend-функцию или
Flow?suspend fun Call.await(): Response = suspendCancellableCoroutine { cont -> enqueue(object : Callback { override fun onResponse(call: Call, response: Response) = cont.resume(response) override fun onFailure(call: Call, e: IOException) = cont.resumeWithException(e) }) cont.invokeOnCancellation { cancel() } // cancel the HTTP call too…Тысяча корутин по тысяче раз увеличивают один и тот же счётчик, и итог получается меньше миллиона. Почему и как это исправить, не блокируя поток?
suspend fun broken(): Int { var counter = 0 withContext(Dispatchers.Default) { repeat(1_000) { launch { repeat(1_000) { counter++ } } } // races: usually less than 1_000_000 } return counter…Корутины и Flow против RxJava: в чём компромиссы и чем отличаются их пулы потоков?
Что меняют
SupervisorJobиsupervisorScopeи почемуscope.launch(SupervisorJob()) { }ломает отмену?val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) // wrong: the passed Job becomes the parent, this is not a child of scopeval orphan = scope.launch(SupervisorJob()) { longRunningSync() }// scope.cancel() leaves it running; only orphan.cancel() stops it…api.promo()бросает исключение, аpromo.await()нигде не вызывается. Упадёт ли этот код и что будет с лентой?fun refresh() { viewModelScope.launch { val promo = async { api.promo() } // throws IOException val feed = api.feed() _state.value = FeedState.Ready(feed) // promo.await() is never called }}Найдите баг: первый же вызов API, которому нужен токен, зависает навсегда, а за ним и все остальные.
class TokenProvider(private val auth: AuthApi) { private val mutex = Mutex() private var token: Token? = null suspend fun validToken(): Token = mutex.withLock { val current = token if (current != null && !current.isExpired()) current else refresh() } suspend fun refresh(): Token = mutex.withLock { auth.refresh().also { token = it } }}