Почему используется один поток для работы с UI

Ответ

В Android UI-поток (главный поток) используется для работы с интерфейсом, потому что:

  1. Потоко-небезопасность — компоненты UI не поддерживают многопоточность. Если несколько потоков попытаются изменить View одновременно, это приведёт к ошибкам.
  2. Предсказуемость — синхронизация изменений UI в одном потоке упрощает отладку и предотвращает race conditions.
  3. Производительность — Android оптимизирован для рендеринга в главном потоке, а блокировка UI из-за долгих операций приводит к ANR.

Пример ошибки:

// Неправильно — изменение UI в фоновом потоке
thread {
    textView.text = "Updated" // Вызовет исключение
}

Правильный подход — runOnUiThread или Handler:

runOnUiThread {
    textView.text = "Updated"
}

Для фоновых задач используйте Coroutine(Dispatchers.IO), RxJava или AsyncTask (deprecated).

Ответ 18+ 🔞

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

А теперь представь, что в эту комнату врывается овердохуища других мудаков с красками и кистями. Один начинает малевать котов, второй — собак, третий вообще решил стену снести. Что получится? Правильно, пиздец рулю. Стена развалится, картина будет похожа на блевоту мартышлюшки, а приложение просто накроется медным тазом с ошибкой CalledFromWrongThreadException. Вот зачем всё в одном потоке — чтобы не было этой толкотни и бардака. Один чувак рисует — порядок, предсказуемость, никаких гонок.

И производительность, ёпта! Система Android заточена под то, чтобы этот один мужик-художник работал быстро и чётко. А если он вдруг решит не рисовать, а, допустим, пошёл пельмени варить на десять часов (то есть выполнять долгую операцию в UI-потоке), то что? А то, что интерфейс зависнет, пользователь будет пялиться в пустой экран, а система ему через пять секунд выкатит окошко "Application Not Responding". И ты, чувак, получишь в рейтинге магазина приложений звезду, да похуй, одну, и комментарий "приложение — говно".

Смотри, как делать НЕ НАДО, если не хочешь, чтобы тебе вилкой в глаз:

// Делаем в фоне, ага, щас
thread {
    textView.text = "Updated" // БАБАХ! Исключение, чувак. Сам от себя охуел.
}

Вот это — прямой билет в logcat с кучей красного текста. Терпения ноль ебать с такими подходами.

А как надо? Да элементарно! Сказать тому самому мужику в комнате: "Э, дружище, когда освободишься, нарисуй вот тут обновлённый текст".

runOnUiThread {
    textView.text = "Updated" // Всё чинно, благородно, в своём потоке.
}

Или через Handler, или, что сейчас модно и правильно — корутины. Для всей тяжёлой работы (сеть, база данных, сложные вычисления) ты запускаешь фоновую задачу где-нибудь на Dispatchers.IO, а когда результат готов, просто шлёшь его обратно в UI-поток через Dispatchers.Main.

viewModelScope.launch(Dispatchers.IO) {
    val data = repository.loadHeavyData() // Тут твоя магия в фоне
    withContext(Dispatchers.Main) {
        textView.text = data // И тут уже безопасно красим стену
    }
}

AsyncTask, конечно, был, но он сейчас как тот полупидор на вечеринке, которого все уже стесняются — deprecated, чувак, не используй.

Короче, суть в чём: главный поток — священная корова. Не грузи его, не лезь к UI из других потоков, и будет тебе счастье, а не волнение ебать при каждом запуске. Всё просто, как три копейки.