Почему глобальный скоуп корутин не рекомендуется использовать
Что проверяют
Понимаете ли вы структурированную конкурентность, а не только синтаксис launch. GlobalScope это скоуп верхнего уровня без родительского Job: корутина в нём живёт столько, сколько живёт процесс, и отменить её некому. Вопрос обычно идёт следом за разговором про жизненный цикл экрана.
Как отвечать
Назовите причины по порядку:
- отмены нет: экран закрыт,
ViewModelуже прошлаonCleared, а корутина работает дальше, держит ссылку на контекст или на вью и тянет за собой утечку.viewModelScopeиlifecycleScopeотменяются сами; - нет иерархии: корутина никому не дочерняя, поэтому родитель не ждёт её и не отменяет вместе с собой. Исключение не долетит до обработчика родителя, а уйдёт в глобальный обработчик потока и уронит приложение, если это
launchбез своегоCoroutineExceptionHandler; - тесты: подменить диспетчер у
GlobalScopeнельзя, тест не умеет ни дождаться такой корутины, ни промотать виртуальное время вrunTest; - API помечен
@DelicateCoroutinesApi, то есть авторы сами считают его опасным по умолчанию.
Чем заменять: viewModelScope и lifecycleScope для UI, собственный CoroutineScope(SupervisorJob() + Dispatchers.Default) в классе с понятным временем жизни и явным cancel(), WorkManager для работы, которая обязана пережить смерть процесса.
Где спотыкаются
Говорят только про утечки памяти. Утечка это следствие, а причина в том, что у корутины нет владельца, и вместе с владельцем теряются и отмена, и обработка ошибок.
Второе: заменяют GlobalScope на CoroutineScope(Dispatchers.IO), созданный прямо внутри функции. Это тот же глобальный скоуп с лишними буквами: ссылку на него никто не хранит, отменять нечего и некому.
Третье: тянутся к GlobalScope ради «фоновой отправки», опасаясь, что работа оборвётся вместе с экраном. Если задача действительно должна дожить до конца, её место в WorkManager: корутину внутри процесса система убьёт при первом же дефиците памяти, и гарантий тут нет никаких.