Implementation kotlinx coroutines android

Kotlin Coroutines on Android

Asynchronous programming is very important and it’s now a common part of modern application. It increases the amount of work that your app can perform in parallel. This allows running heavy tasks away from UI Thread in the background, which ultimately gives a smooth and better experience to the user of the app.

Coroutine in Kotlin

The Kotlin team defines coroutines as “lightweight threads”. They are sort of tasks that the actual threads can execute. Coroutines were added to Kotlin in version 1.3 and are based on established concepts from other languages. Kotlin coroutines introduce a new style of concurrency that can be used on Android to simplify async code.

The official documentation says that coroutines are lightweight threads. By lightweight, it means that creating coroutines doesn’t allocate new threads. Instead, they use predefined thread pools and smart scheduling for the purpose of which task to execute next and which tasks later.

Coroutines are basically of two types:

Kotlin implements stackless coroutines, it means that the coroutines don’t have their own stack, so they don’t map on the native thread.

Why we need coroutines?

As we know android developers today have many async tools in hand. These include RxJava, AsyncTasks, Jobs, Threads. So why there is a need to learn something new?

  • While Using Rx, it requires a lot of effort to get it enough, to use it safely. On the Other hand, AsyncTasks and threads can easily introduce leaks and memory overhead. Even using these tools after so many disadvantages, the code can suffer from callbacks, which can introduce tons of extra code. Not only that, but the code also becomes unreadable as it has many callbacks which ultimately slow down or hang the device leading to poor user experience.
  • Android is a single thread platform, By default, everything runs on the main thread. In Android, almost every application needs to perform some non UI operations like (Network call, I/O operations), so when coroutines concept is not introduced, what is done is that programmer dedicate this task to different threads, each thread executes the task given to it, when the task is completed, they return the result to UI thread to update the changes required. Though In android there is a detailed procedure given, about how to perform this task in an effective way using best practices using threads, this procedure includes lots of callbacks for passing the result among threads, which ultimately introduce tons of code in our application and the waiting time to get the result back to increases.
  • On Android, Every app has a main thread (which handles all the UI operations like drawing views and other user interactions. If there is too much work happening on this main thread, like network calls (eg fetching a web page), the apps will appear to hang or slow down leading to poor user experience.

Kotlin Coroutines Features

Coroutines is the recommended solution for asynchronous programming on Android. Some highlighted features of coroutines are given below.

  • Lightweight: One can run many coroutines on a single thread due to support for suspension, which doesn’t block the thread where the coroutine is running. Suspending frees memory over blocking while supporting multiple concurrent operations.
  • Built-in cancellation support: Cancellation is generated automatically through the running coroutine hierarchy.
  • Fewer memory leaks: It uses structured concurrency to run operations within a scope.
  • Jetpack integration: Many Jetpack libraries include extensions that provide full coroutines support. Some libraries also provide their own coroutine scope that one can use for structured concurrency.

Источник

Как использовать Котлин корутины (Kotlin Coroutines) в андроид приложении. Часть 1

Это первый курок курса о том, как использовать Котлин корутины в андроид приложении.

Читайте также:  Рейтинг андроидов 2021 до 20000

В этом курсе вы узнаете, как использовать Kotlin Coroutines в приложении для Android — новый способ управления фоновыми потоками (background threads), который может упростить код за счет уменьшения потребности в обратных вызовах (callbacks). Корутины, или сопрограммы — это функция Kotlin, которая преобразует асинхронные обратные вызовы для длительных задач, таких как доступ к базе данных или сети, в последовательный (sequential) код.

Чтобы на практике увидеть работу с Kotlin Coroutines и архитектурными компонентами, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»

Вот фрагмент кода, чтобы дать вам представление о том, что вы будете делать:

Код на основе обратного вызова будет преобразован в последовательный код с использованием корутин:

Вы начнете с существующего приложения, созданного с использованием компонентов архитектуры, которое использует стиль обратного вызова для длительных задач.

К концу этого курса у вас будет достаточно опыта для преобразования существующего API для использования корутин, и вы сможете интегрировать корутины в приложение. Вы также познакомитесь с лучшими практиками для корутин и с тем, как написать тест для кода, который использует корутины.

Что вы узнаете

  • Как вызвать код, написанный с корутинами и получить результаты.
  • Как использовать функции приостановки (suspend), чтобы сделать асинхронный код последовательным.
  • Как использовать launch и runBlocking для контроля выполнения кода.
  • Методы преобразования существующих API в корутины с использованием suspendCoroutine.
  • Как использовать корутины с Architecture Components.
  • Лучшие практики для тестирования корутин.

Что нужно знать

  • Знакомство с компонентами архитектуры ViewModel , LiveData , Repository , и Room .
  • Знакомство с синтаксисом Kotlin, включая функции расширения (extension) и лямбды.
  • Основное понимание использования потоков в Android, включая основной поток, фоновые потоки и обратные вызовы.

Для ознакомления с синтаксисом Kotlin см. Kotlin Bootcamp for Programmers.

Для ознакомления с основами многопоточности в Android см. Guide to background processing.

Что вам понадобится

Android Studio 3.3 (можно работать с другими версиями, но некоторые вещи могут отсутствовать или выглядеть иначе).

Приступаем к настройке

Исходный код

Нажмите на кнопку, чтобы загрузить весь код для серии уроков:

… или клонируйте GitHub-репозиторий из командной строки, используя следующую команду:

Репозиторий kotlin-coroutines содержит три разных приложения:

  • kotlin-coroutines-start — Простое приложение, чтобы изучить, как сделать свою первую корутину
  • kotlin-coroutines-repository — Проект основан на обратных вызовах, которые вы конвертируете для использования корутин.
  • kotlin-coroutines-end — Проект с корутинами уже добавлен

Запуск приложения

Во-первых, давайте посмотрим, как выглядит исходный пример приложения. Следуйте этим инструкциям, чтобы открыть образец приложения в Android Studio.

  1. Если вы загрузили zip-файл kotlin-coroutines, распакуйте его.
  2. Откройте проект kotlin-coroutines-start в Android Studio.
  3. Нажмите кнопку Run и выберите эмулятор или подключите устройство Android, которое должно поддерживать Android Lollipop (минимальный поддерживаемый SDK — 21). Экран Kotlin Coroutines должен появиться:

Это стартовое приложение использует потоки, чтобы отобразить снэк-бар через секунду после нажатия в любом месте экрана. Попробуйте сейчас, и вы должны увидеть «Hello, from threads!» после небольшой задержки В первой части этого курса вы конвертируете это приложение для использования корутин.

Это приложение использует компоненты архитектуры для отделения кода пользовательского интерфейса в MainActivity от логики приложения в MainViewModel. Найдите минутку, чтобы ознакомиться со структурой проекта.

  1. MainActivity отображает пользовательский интерфейс, регистрирует слушатели кнопок и может отображать Snackbar . Он передает события MainViewModel и обновляет экран на основе LiveData в MainViewModel .
  2. MainViewModel обрабатывает события в onMainViewClicked и будет общаться с MainActivity используя LiveData.
  3. Executors определяет BACKGROUND, который может запускать работу в фоновом потоке.
  4. MainViewModelTest определяет тест для MainViewModel .

Добавление корутин в проект

Чтобы использовать корутины в Kotlin, необходимо включить библиотеку coroutines-core в файл build.gradle (Module: app) вашего проекта. В текущем проекте это уже сделано.

Корутины на Android доступны как базовая библиотека, а также специальные расширения для Android:

  • kotlinx-corountines-core — Основной интерфейс для использования корутин в Kotlin
  • kotlinx-coroutines-android — Поддержка основного потока Android в корутинах

Источник

Современный подход к конкурентности в Android: корутины в Kotlin

Напоминаем, что у нас уже открыт предзаказ на долгожданную книгу о языке Kotlin из знаменитой серии Big Nerd Ranch Guides. Сегодня мы решили предложить вашему вниманию перевод статьи, рассказывающей о корутинах Kotlin и о правильной работе с потоками в Android. Тема обсуждается очень активно, поэтому для полноты картины также рекомендуем посмотреть эту статью с Хабра и этот подробный пост из блога компании Axmor Software.

Читайте также:  Реальный футбольный менеджер для андроид

Современный фреймворк для обеспечения конкурентности в Java/Android учиняет ад обратных вызовов и приводит к блокирующим состояниям, так как в Android нет достаточно простого способа гарантировать потокобезопасность.

Корутины Kotlin – это очень эффективный и полный инструментарий, позволяющий гораздо проще и производительнее управлять конкурентностью.

Приостановка и блокирование: в чем разница

Корутины не заменяют потоков, а скорее дают фреймворк для управления ими. Философия корутин заключается в определении контекста, позволяющего ожидать, пока завершатся фоновые операции, не блокируя при этом основного потока.

Цель корутин в данном случае – обойтись без обратных вызовов и упростить конкуренцию.

Для начала возьмем самый простой пример: запустим корутину в контексте Main (главный поток). В нем мы извлечем изображение из потока IO и отправим это изображение на обработку обратно в Main .

Код прост как однопоточная функция. Причем, пока getImage выполняется в выделенном пуле потоков IO , главный поток свободен и может взяться за любую другую задачу! Функция withContext приостанавливает текущую корутину, пока работает ее действие ( getImage() ). Как только getImage() возвратится и looper из главного потока станет доступен, корутина возобновит работу в главном потоке и вызовет imageView.setImageBitmap(image) .

Второй пример: теперь нам требуется, чтобы были выполнены 2 фоновые задачи, чтобы ими можно было воспользоваться. Мы применим дуэт async/await, чтобы две эти задачи выполнялись параллельно, и воспользуемся их результатом в главном потоке, как только обе задачи будут готовы:

async подобен launch , но возвращает deferred (сущность Kotlin, эквивалентная Future ), поэтому ее результат можно получить при помощи await() . При вызове без параметров она работает в контексте, задаваемом по умолчанию для текущей области видимости.

Опять же, главный поток остается свободен, пока мы дожидаемся наших 2 значений.
Как видите, функция launch возвращает Job , который можно использовать для ожидания, пока операция завершится – это делается при помощи функции join() . Она работает как и в любом другом языке, с той оговоркой, что просто приостанавливает корутину, а не блокирует поток.

Диспетчеризация – ключевая концепция при работе с корутинами. Это действие, позволяющее «перепрыгнуть» от одного потока к другому.

Рассмотрим, как в java выглядит эквивалент для диспетчеризации в Main , то есть,

Реализация контекста Main для Android – это диспетчер на основе Handler . Итак, это действительно очень подходящая реализация:

launch(Dispatchers.Main) посылает Runnable в Handler , так что его код выполняется не сразу.

launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) немедленно выполнит свое лямбда-выражение в текущем потоке.

Dispatchers.Main гарантирует, что когда корутина возобновит работу, она будет направлена в главный поток; кроме того, Handler используется здесь как нативная реализация Android для отправки в цикл событий приложения.

Точная реализация выглядит так:

Вот хорошая статья помогающая разобраться в тонкостях диспетчеризации в Android:
Understanding Android Core: Looper, Handler, and HandlerThread.

Контекст корутины (он же – диспетчер корутины) определяет, в каком потоке будет выполняться ее код, что делать, если будет выброшено исключение, и обращается к родительскому контексту для распространения отмены.

job.cancel() отменит все корутины, родителем которых является job . A exceptionHandler получит все исключения, выброшенные в этих корутинах.

Интерфейс coroutineScope упрощает обработку ошибок:
Если откажет какая-либо из его дочерних корутин, то откажет и вся область видимости, и все дочерние корутины будут отменены.

В примере async , если извлечь значение не удалось, а другая задача при этом продолжила работу – у нас возникает поврежденное состояние, и с этим надо что-то делать.

При работе с coroutineScope функция useValues будет вызываться лишь в случае, если извлечение обоих значений прошло успешно. Также, если deferred2 откажет, deferred1 будет отменена.

Также можно “поместить в область видимости” целый класс, чтобы задать для него контекст CoroutineContext по умолчанию и использовать его.

Пример класса, реализующего интерфейс CoroutineScope :

Запуск корутин в CoroutineScope :

Диспетчер launch или async , задаваемый по умолчанию, теперь становится диспетчером актуальной области видимости.

Автономный запуск корутины (вне какого-либо CoroutineScope):

Можно даже определить область видимости для приложения, задав диспетчер Main по умолчанию:

  • Корутины ограничивают интероперабельность с Java
  • Ограничивают изменяемость во избежание блокировок
  • Корутины предназначены для ожидания, а не для организации потоков
  • Избегайте I/O в Dispatchers.Default (и Main …) — для этого предназначен Dispatchers.IO
  • Потоки ресурсозатратны, поэтому используются однопоточные контексты
  • Dispatchers.Default основан на ForkJoinPool , появившемся в Android 5+
  • Корутины можно использовать посредством каналов
Читайте также:  Таймер для бокса для андроид

Избавляемся от блокировок и обратных вызовов при помощи каналов

Определение канала из документации JetBrains:

Канал Channel концептуально очень похож на BlockingQueue . Ключевое отличие заключается в том, что он не блокирует операцию put, он предусматривает приостанавливающий send (или неблокирующий offer ), а вместо блокирования операции take предусматривает приостанавливающий receive .

Рассмотрим простой инструмент для работы с каналами: Actor .

Actor , опять же, очень похож на Handler : мы определяем контекст корутины (то, есть, поток, в котором собираемся выполнять действия) и работаем с ним в последовательном порядке.

Разница, конечно же, заключается в том, что здесь используются корутины; можно указать мощность, а выполняемый код – приостановить.

В принципе, actor будет переадресовывать любую команду каналу корутины. Он гарантирует выполнение команды и ограничивает операции в ее контексте. Такой подход отлично помогает избавиться от вызовов synchronize и держать все потоки свободными!

В данном примере мы пользуемся запечатанными классами Kotlin, выбирая, какое именно действие выполнить.

Причем, все эти действия будут поставлены в очередь, параллельно выполняться они никогда не будут. Это удобный способ добиться ограничения изменяемости.

Жизненный цикл Android + корутины

Акторы могут очень пригодиться и для управления пользовательским интерфейсом Android, упрощают отмену задач и предотвращают перегрузку главного потока.
Давайте это реализуем и вызовем job.cancel() при уничтожении активности.

Класс SupervisorJob похож на обычный Job с тем единственным исключением, что отмена распространяется только в нисходящем направлении.

Поэтому мы не отменяем всех корутин в Activity , когда одна из них отказывает.

Чуть лучше дела обстоят с функцией расширения, позволяющей открыть доступ к этому CoroutineContext из любого View в CoroutineScope .

Теперь мы можем все это скомбинировать, функция setOnClick создает объединенный actor для управления ее действиями onClick . В случае множественных нажатий промежуточные действия будут игнорироваться, исключая таким образом ошибки ANR (приложение не отвечает), и эти действия будут выполняться в области видимости Activity . Поэтому при уничтожении активности все это будет отменено.

В данном примере мы задаем для Channel значение Conflated , чтобы он игнорировал часть событий, если их будет слишком много. Можно заменить его на Channel.UNLIMITED , если вы предпочитаете ставить события в очередь, не теряя ни одного из них, но все равно хотите защитить приложение от ошибки ANR.

Также можно комбинировать корутины и фреймворки Lifecycle, чтобы автоматизировать отмену задач, связанных с пользовательским интерфейсом:

Упрощаем ситуацию с обратными вызовами (часть 1)

Вот как можно преобразить использование API, основанного на обратных вызовах, благодаря Channel .

API работает вот так:

  1. requestBrowsing(url, listener) инициирует синтаксический разбор папки, расположенной по адресу url .
  2. Слушатель listener получает onMediaAdded(media: Media) для любого медиа-файла, обнаруженного в этой папке.
  3. listener.onBrowseEnd() вызывается по завершении синтаксического разбора папки

Вот старая функция refresh в поставщике контента для обозревателя VLC:

Создаем канал, который будет запускаться в refresh . Теперь обратные вызовы обозревателя будут лишь направлять медиа в этот канал, а затем закрывать его.

Теперь функция refresh стала понятнее. Она создает канал, вызывает обозреватель VLC, затем формирует список медиа-файлов и обрабатывает его.

Вместо функций select или consumeEach можно использовать for для ожидания медиа, и этот цикл будет разрываться, как только канал browserChannel закроется.

Упрощаем ситуацию с обратными вызовами (часть 2): Retrofit

Второй подход: мы вообще не используем корутины kotlinx, зато применяем корутинный core-фреймворк.

Смотрите, как на самом деле работают корутины!

Функция retrofitSuspendCall оборачивает запрос на вызов Retrofit Call , чтобы сделать из него функцию suspend .

При помощи suspendCoroutine мы вызываем метод Call.enqueue и приостанавливаем корутину. Предоставленный таким образом обратный вызов обратится к continuation.resume(response) , чтобы возобновить корутину откликом от сервера, как только тот будет получен.

Далее нам остается просто объединить наши функции Retrofit в retrofitSuspendCall , чтобы с их помощью возвращать результаты запросов.

Таким образом, вызов, блокирующий сеть, делается в выделенном потоке Retrofit, корутина находится здесь, ожидая отклика от сервера, а использовать ее в приложении – проще некуда!

Такая реализация вдохновлена библиотекой gildor/kotlin-coroutines-retrofit.

Также имеется JakeWharton/retrofit2-kotlin-coroutines-adapter с другой реализацией, дающей аналогичный результат.

Channel можно использовать и многими другими способами; посмотрите в BroadcastChannel более мощные реализации, которые могут вам пригодиться.

Также можно создавать каналы при помощи функции Produce.

Наконец, при помощи каналов удобно организовать коммуникацию между компонентами UI: адаптер может передавать события нажатий в свой фрагмент/активность через Channel или, например, через Actor .

Источник

Оцените статью