Как работают сервисы в Android и как их использовать
Сервисы — это компоненты для фоновой работы без UI: используйте startService/startForegroundService для длительных задач и bindService/ServiceConnection для прямого взаимодействия. Remote Service (AIDL) применяется при необходимости IPC между процессами. Ниже — практическое описание, шаблоны кода и советы, чтобы сразу применить.
Что такое Service и какие бывают
Service — компонент, выполняющий работу в фоне: загрузки, синхронизация, воспроизведение аудио, обработка сетевых событий. Важно выбирать тип сервиса под задачу:
- Started Service (запуск через startService/startForegroundService): работает независимо от активностей. Жизненный цикл: onCreate → onStartCommand → onDestroy. Используйте startForeground() + уведомление на Android 8+ для устойчивости.
- Bound Service (привязка через bindService): предоставляет интерфейс для взаимодействия (обычно через Binder). Живёт, пока есть привязанные клиенты.
- Remote Service (отдельный процесс, AIDL): для IPC между приложениями/модулями или изоляции тяжёлых/критичных операций.
Краткий пример started+bound (Kotlin):
class MyService : Service() {
private val binder = MyBinder()
inner class MyBinder : Binder() { fun getService() = this@MyService }
override fun onBind(intent: Intent?) = binder
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY
}
Регистрация в AndroidManifest:
<service android:name=".MyService" />
Для периодических задач предпочтительнее WorkManager (учитывает Doze и ограничения батареи). Service — когда нужен длительный foreground-процесс или немедленное непрерывное исполнение.
ServiceConnection: как правильно привязываться и общаться
ServiceConnection — интерфейс, через который клиент получает IBinder для вызова методов сервиса. Типичный паттерн для локальной привязки:
private var service: MyService? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
val myBinder = binder as MyService.MyBinder
service = myBinder.getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
service = null
}
}
override fun onStart() {
bindService(Intent(this, MyService::class.java), connection, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
unbindService(connection)
}
Практические рекомендации:
- Всегда делать unbindService в onStop/onDestroy, чтобы избежать утечек.
- Для асинхронных сообщений между процессами используйте Messenger вместо AIDL, если интерфейс прост.
- Не блокируйте UI-поток внутри сервиса — используйте Coroutine, Executor или IntentService/JobIntentService.
Remote Service (AIDL): когда и как внедрять
Remote Service нужен для IPC: разные процессы, модульная архитектура, защита критичных данных. Шаги:
- Создайте .aidl-файл (интерфейс). Пример IMyService.aidl:
package com.example;
interface IMyService {
String getData();
}
- Система сгенерирует Stub/Proxy. В сервисе реализуйте Stub и возвращайте его в onBind():
private val stub = object : IMyService.Stub() {
override fun getData(): String = "ok"
}
override fun onBind(intent: Intent?) = stub.asBinder()
- Клиент через ServiceConnection получает прокси и вызывает удалённые методы.
Remote Service повышает расход памяти и батареи, сложнее отлаживать и требует сериализации Parcelable. Используйте только при реальной необходимости IPC.
Частые ошибки
- Не вызывать unbindService — утечки памяти.
- Забудьте вызвать startForeground() на Android 8+ для длительных background задач — сервис будет убит.
- Использовать AIDL для простых сообщений — перегрузка. Выберите Messenger или BroadcastReceiver.
- Игнорировать права (BIND_SERVICE) и intent-filter security.
- Блокировать главный поток в методах сервиса.
FAQ
- Когда выбирать startService vs bindService?
- startService — если задача должна выполняться независимо от UI. bindService — если нужен прямой доступ к методам сервиса.
- Как корректно остановить сервис?
- Для started service: stopSelf() или stopService(intent). Для bound service — unbindService; сервис завершится, если нет других клиентов и не был стартован.
- Как протестировать поведение при оптимизации батареи?
- Тестируйте на реальных устройствах с включённой оптимизацией батареи и в Doze-режиме; используйте adb для симуляции Doze.
- Можно ли обращаться к UI из сервиса?
- Нет. Сервис работает в фоновом потоке — взаимодействуйте через Broadcast/Handler/LiveData или отправляйте интенты/уведомления.
Если нужно, могу подготовить компактный шаблон Service + Foreground Notification или пример AIDL с Parcelable.