Уверен, с вами такое тоже случалось: приходится работать со старой библиотекой, использующей типичную систему обратных вызовов, и кажется, что код превращается в полный беспорядок. Такая структура, где одна функция вызывает другую, а та, в свою очередь, вызывает третью, в итоге приводит к печально известной проблеме. "ад повторных звонков"Это превращает поддержку проекта в настоящий кошмар, а чтение потока данных — в попытку расшифровать иероглиф.
К счастью, Котлин Это предоставляет нам сопрограммы, которые по сути похожи на потоки, но гораздо легче и эффективнее. Благодаря им мы можем преобразовать старые асинхронные вызовы в стиль последовательного программированияЭто делает код намного чище, читаемее и, самое главное, упрощает отладку без блокировки основного потока приложения.
Проблема обратных вызовов и появления сопрограмм
При использовании API, основанных на коллбэках, логика становится фрагментированной. Представьте, что вам нужно проверить пользователя, затем запросить у него друзей и, наконец, предложить новых; если каждый шаг зависит от коллбэка, в итоге вы получите... бесконечный отступ вправоКроме того, управление ошибками на каждом уровне этой пирамиды — изнурительный и чреватый неудачами процесс.
Вот тут-то и вступают в действие функции приостановки. Они позволяют сопрограмме приостановить выполнение в определённой точке и вернуть управление системе до тех пор, пока результат не будет готов. Самое приятное то, что для того, кто читает код, операция выглядит синхронной, хотя поток не блокируется, и приложение продолжает бесперебойно отвечать.
Dominating suspendCancellableCoroutine
Для преодоления разрыва между миром коллбэков и миром сопрограмм у нас есть фундаментальный инструмент, называемый suspendCancellableCoroutineЭта функция выступает в качестве связующего звена, предоставляя нам объект. CancellableContinuation, что позволяет нам возобновить выполнение процедуры вручную, как только старый API вернет ожидаемые данные.
В отличие от упрощенной версии, suspendCoroutine, вариант сократимый Это предпочтительный вариант в профессиональном развитии. Почему? Потому что он позволяет нам управлять отменой заданий. Если пользователь закрывает экран, пока запрос еще выполняется, мы можем предотвратить ненужное возобновление сопрограммы, тем самым предотвращая Утечки памяти и потребление ресурсов.
Для реализации этого мы просто оборачиваем асинхронный вызов и внутри функции обратного вызова при успешном выполнении вызываем соответствующую функцию. continuation.resume(resultado)Если операция не удалась, мы используем continuation.resumeWithException(e) чтобы исключение возникло в точке вызова и могло быть перехвачено блоком обработки. обычный ловля, что устраняет необходимость обработки ошибок в нескольких функциях обратного вызова.
Предварительное оформление отмены и обжалования
Один из важнейших моментов — это обеспечение того, чтобы ни один процесс не оставался незавершенным. Метод invokeOnCancellation Это крайне важно, поскольку позволяет нам установить драйвер, который будет работать. при условии отмены мероприятияЭто крайне важно, если устаревший API требует от нас вручную отменить регистрацию слушателя или закрыть файл, чтобы избежать оставления ненужных файлов в системе.
Важно понимать, что отмена действий в Kotlin, как правило, происходит асинхронно. гарантия немедленной отмены Это гарантирует, что если задание было отменено во время приостановки функции, сопрограмма не сможет успешно возобновить работу, даже если метод возобновления уже был вызван. Это защищает целостность приложения, предотвращая выполнение кода во время приостановки функции. Компоненты пользовательского интерфейса уже уничтожены.
Когда одного результата недостаточно: callbackFlow
Бывают случаи, когда нам нужен не один показатель, а непрерывный поток данных, например, обновления местоположения по GPS. Для этого... suspendCancellableCoroutine Этого недостаточно, и нам приходится прибегать к... callbackFlowЭтот конструктор позволяет нам выдать несколько значений посредством асинхронного потока данных по каналам.
В пределах callbackFlowмы используем эту функцию offer (o trySend (В последних версиях) отправлять данные в поток по мере их поступления. Обязательной деталью является вызов функции awaitClose в конце квартала; именно здесь мы должны Очистка ресурсов и отмена регистрации обратных вызовов. чтобы поток не оставался открытым бесконечно, потребляя заряд батареи и ресурсы процессора.
Для оптимизации этих потоков мы можем использовать такие операторы, как conflate()что идеально, когда нас интересует только... последнее обновление И мы хотим игнорировать промежуточные значения, если сборщик работает медленнее, чем отправитель. Кроме того, интеграция этого с asLiveData() Это позволяет автоматически активировать и деактивировать подписку в соответствии с жизненным циклом действия.
Контексты, диспетчеры и оптимизация
Для того чтобы всё это работало без зависания экрана, нам необходимо управлять контекстом сопрограммы. Dispatchers.Main Она отвечает за пользовательский интерфейс, но для вызовов к устаревшим библиотекам, выполняющим операции ввода-вывода, нам необходимо использовать Dispatchers.IO, функция withContext является идеальным инструментом для изменить поток выполнения своевременно, не нарушая последовательность действий.
Если нам нужно выполнять несколько задач параллельно для экономии времени, то строительная программа... async В этом и суть. Вместо того чтобы ждать завершения одного процесса перед запуском другого, мы запускаем оба и используем их. await() для синхронизировать результаты Именно тогда, когда они нам нужны. Это может значительно сократить время загрузки экрана, перейдя от линейного к параллельному выполнению.
Благодаря интеграции этих методов мы заставляем устаревшие библиотеки вести себя как современный код Kotlin, используя преимущества... точный контроль над жизненным циклом посредством таких областей применения, как lifecycleScope o viewModelScopeЭто гарантирует, что ни одна задача не останется запущенной в вакууме после того, как пользователь покинет приложение.
