Почему нельзя работать с сетевыми запросами в MainActivity

Ответ

Работа с сетевыми запросами в MainThread (UI-потоке) приведет к ANR (Application Not Responding), так как сетевые операции блокируют поток. Android запрещает сетевые вызовы в основном потоке начиная с Honeycomb (API 11).

Используй отдельный поток или корутины:

// С корутиной
lifecycleScope.launch(Dispatchers.IO) {
    val result = api.getData()
    withContext(Dispatchers.Main) {
        updateUI(result)
    }
}

// С RxJava
api.getData()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe { result -> updateUI(result) }

Ответ 18+ 🔞

О, слушай, тут такая тема, что у меня аж волнение ебать! Представь себе: ты пишешь приложение, всё красиво, кнопочки нажимаются, анимации плавные... И тут — бац! — ты решаешь в основном потоке, прямо там, где интерфейс рисуется, дернуть данные из интернета. Ну, типа, api.getData() прямо в кнопке нажал.

И тут начинается ёперный театр. Приложение просто встаёт колом, как будто его хуй с горы придавил. Экран темнеет, система тебе выдает: "Такое-то приложение не отвечает". Это и есть тот самый ANR, Application Not Responding, ядрёна вошь!

А всё почему? Потому что сетевой запрос — штука медленная, непредсказуемая. Сервер может тупить, связь — хромать. И пока твой запрос ползёт по проводам, главный поток, который должен реагировать на касания и обновлять экран, просто спит. А система Android, начиная ещё с древних времён (Honeycomb, API 11), смотрит на это и говорит: "Ну ты даёшь, чувак! В рот меня чих-пых, так нельзя!" И намертво запрещает сетевые вызовы в UI-потоке.

Так что делать-то? Да похуй, вариантов — овердохуища! Главное — вынести эту тягомотину в фоновый поток.

Смотри, как сейчас модно с корутинами:

// Запускаем в скоупе жизненного цикла (чтоб не утекло)
lifecycleScope.launch(Dispatchers.IO) { // Говорим: "Работай, сука, в фоне!"
    val result = api.getData() // Тут можно долго и нудно ждать
    withContext(Dispatchers.Main) { // А потом говорим: "А теперь вернись в главный поток!"
        updateUI(result) // И только тут лезем в UI
    }
}

Красота же? Всё чётко, понятно. Фоновая работа — в Dispatchers.IO, обновление интерфейса — в Dispatchers.Main.

А если ты староват и любишь RxJava, то вот тебе классика, проверенная временем:

api.getData()
    .subscribeOn(Schedulers.io()) // "Подписывайся и работай в IO-потоке"
    .observeOn(AndroidSchedulers.mainThread()) // "А результат наблюдай в главном"
    .subscribe { result -> updateUI(result) } // "И вот тут обновляй UI"

Суть одна: не еби главный поток сетевыми запросами. Вынеси эту работу на сторону, а когда результат будет готов — аккуратно, с поклоном, верни его в UI-поток для отображения. Иначе пользователь тебе такое устроит, что мало не покажется. Сам от себя охуеешь, когда начнут прилетать плохие отзывы!