Ответ
Handler работает на том потоке, в котором был создан его Looper. По умолчанию, если не передать Looper явно, Handler использует Looper текущего потока. Если Looper не был подготовлен (например, в фоновом потоке без вызова Looper.prepare()), будет выброшено исключение.
Пример:
// UI поток (Looper уже есть)
val mainHandler = Handler(Looper.getMainLooper())
// Фоновый поток (нужен prepare())
thread {
Looper.prepare()
val bgHandler = Handler(Looper.myLooper()!!)
Looper.loop()
}