Как в Service выполнить работу не на главном потоке

Ответ

Для выполнения работы в Service не на главном потоке можно использовать:

  1. IntentService (устарел, но прост в использовании):
class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        // Работа в фоновом потоке
    }
}
  1. JobIntentService (современная замена IntentService):
class MyJobIntentService : JobIntentService() {
    override fun onHandleWork(intent: Intent) {
        // Работа в фоновом потоке
    }
}
  1. Coroutines (рекомендуемый способ):
class MyService : Service() {
    private val scope = CoroutineScope(Dispatchers.Default)

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        scope.launch {
            // Работа в фоновом потоке
        }
        return START_STICKY
    }

    override fun onDestroy() {
        scope.cancel()
        super.onDestroy()
    }
}
  1. Thread/HandlerThread (низкоуровневый подход):
val handlerThread = HandlerThread("ServiceHandler")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post { /* Работа в фоне */ }

Ответ 18+ 🔞

Э, слушай, вот сидишь такой, пишешь свой сервис, а потом бац — приложение виснет, потому что ты в главном потоке какую-то тяжёлую хуйню запустил. Ядрёна вошь! Так делать низя, ёпта. Ну, то есть можно, но потом пользователи тебе такую хиросиму устроят, что мало не покажется.

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

1. IntentService (старый, но простой, как валенок) Это как дедушка в пальто — уже не модно, но своё дело знает. Создаёшь, пишешь логику в одном методе, и он сам в отдельном потоке всё сделает и закроется. Красота. Но его в новых апи уже не любят, так что имей в виду.

class MyIntentService : IntentService("MyIntentService") {
    override fun onHandleIntent(intent: Intent?) {
        // Вот тут твоя работа в фоновом потоке
        // Можешь хоть до завтра спать тут, главный поток не пострадает
    }
}

2. JobIntentService (молодой и шустрый наследник) Тот же принцип, но уже с учётом современных реалий ОС. Более правильный выбор, если хочется по-старому, но не совсем.

class MyJobIntentService : JobIntentService() {
    override fun onHandleWork(intent: Intent) {
        // Опять же, вся магия тут, в фоне
    }
}

3. Корутины (то, что сейчас все советуют, и правильно делают) Вот это, бля, уже серьёзно. Современно, гибко, управляемо. Заводишь скоуп в сервисе и запускаешь свои дела в нём. Главное — не забыть отменить всё при уничтожении, а то будет тебе утечка памяти, пидарас шерстяной.

class MyService : Service() {
    // Создаём свою область видимости для корутин
    private val scope = CoroutineScope(Dispatchers.Default)

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // Запускаем работу НЕ в главном потоке
        scope.launch {
            // Вся тяжёлая работа тут. Скачивай файлы, парсь JSON, делай что хочешь.
        }
        return START_STICKY
    }

    override fun onDestroy() {
        // ОБЯЗАТЕЛЬНО чистим за собой! Иначе сервис будет висеть как груз.
        scope.cancel()
        super.onDestroy()
    }
}

4. Thread или HandlerThread (дедовский метод, когда ты сам себе хозяин) Ну это если ты совсем старовер, или нужно что-то на низком уровне. Создаёшь поток, кидаешь в него задачу. Просто, но управлять этим хозяйством — тот ещё геморрой. Можно наломать таких дров, что потом охуеешь.

val handlerThread = HandlerThread("ServiceHandler")
handlerThread.start()
val handler = Handler(handlerThread.looper)
handler.post {
    /* Работай тут, парись в своём соку */
}

Вот, короче, основные варианты. Самый адекватный и современный — это, конечно, корутины. С ними и код чище, и управление проще. Остальные — либо для легаси, либо для каких-то очень специфичных случаев. Выбирай с умом, а не как попало!