Какие знаешь особенности Background Service?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какие знаешь особенности Background Service?
Background Service — это Service, который работает в фоне без видимого уведомления. Вот главные особенности и ограничения.
1. Что такое Background Service?
class MyBackgroundService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
// Работает в фоне, без Notification
doBackgroundWork()
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
}
// Запуск
startService(Intent(this, MyBackgroundService::class.java))
2. Важная особенность: Doze Mode (Android 6.0+)
Doze Mode — режим экономии батареи, который замораживает фоновые приложения.
Экран выключен → Батарея низкая → Doze Mode активирован
↓
Background Service приостанавливается
Как система относится к Background Service в Doze Mode:
- Сетевые запросы замораживаются
- JobScheduler отложен
- Alarm отложены
- WorkManager почти не работает (только приоритетные)
Решение: используй WorkManager вместо Service
// Вместо:
startService(intent)
// Используй:
val work = OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"my_work",
ExistingWorkPolicy.KEEP,
work
)
3. Background Service vs Foreground Service
| Параметр | Background Service | Foreground Service |
|---|---|---|
| Notification | Нет | Обязательна |
| Приоритет | Низкий | Высокий |
| Doze Mode | Замораживается | Работает |
| Когда использовать | Синхронизация | GPS, музыка, запись |
| Убийство системой | Может быть убит | Не убивается |
4. Ограничения Background Service в Android 8.0+
Android 8.0 (Oreo) ввёл серьёзные ограничения:
// НЕПРАВИЛЬНО на Android 8.0+ (будет нарушение)
startService(intent) // Service будет задержан!
// ПРАВИЛЬНО: использовать startForegroundService
startForegroundService(intent)
// Или WorkManager
WorkManager.getInstance(context).enqueueUniqueWork(...)
Ошибка в Logcat:
IllegalStateException: Service.startForeground() not called within 5 seconds
Исправление:
class MyBackgroundService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
// На Android 8.0+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(
1,
createNotification() // Обязательно!
)
}
doBackgroundWork()
stopSelf() // Остановить себя
return START_NOT_STICKY
}
}
5. Background Execution Limits
Android 8.0+ ограничивает Background Service на 1 минуту работы:
// Service будет убит через 1 минуту, если не в foreground
class MyService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
// Это сработает максимум 1 минуту
Thread {
for (i in 1..1000) { // Может не выполниться до конца
println("Working $i")
Thread.sleep(100)
}
}.start()
return START_STICKY
}
}
6. Implicit Intent отключены (Android 12+)
Раньше можно было запустить Service другого приложения:
// СТАРАЯ ВЕРСИЯ (Android 12-)
val intent = Intent()
intent.action = "com.example.MY_ACTION" // Implicit intent
startService(intent)
// ANDROID 12+ это не работает!
// Нужно явно указать компонент
val intent = Intent(this, MyService::class.java) // Explicit intent
startService(intent)
7. Service вызывается из главного потока
class MyService : Service() {
override fun onStartCommand(...): Int {
// Это выполняется на Main потоке!
println(Thread.currentThread().name) // "main"
// Если здесь долгая операция, UI замерзнет
// Нужно запустить в фоновом потоке
Thread {
println("Фоновый поток")
}.start()
return START_STICKY
}
}
8. START_STICKY vs START_NOT_STICKY
// START_STICKY: переcоздаст Service без Intent при убийстве
return START_STICKY
// Используй для: музыкальный плеер, GPS
// START_NOT_STICKY: не пересоздаст
return START_NOT_STICKY
// Используй для: одноразовые задачи
// START_REDELIVER_INTENT: пересоздаст С тем же Intent
return START_REDELIVER_INTENT
// Используй для: синхронизация данных
9. Практический пример: Data Sync Service (НЕПРАВИЛЬНО)
class SyncService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
// ❌ ПЛОХО! Это неправильный подход в 2024
syncDataFromServer()
return START_STICKY
}
private fun syncDataFromServer() {
try {
val response = api.sync() // Может быть медленным
saveToDatabase(response)
} catch (e: Exception) {
// Service может быть убит в любой момент
}
}
}
10. Правильный способ: WorkManager
// ✅ ПРАВИЛЬНО
val syncWork = OneTimeWorkRequestBuilder<SyncWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
)
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"sync",
ExistingWorkPolicy.KEEP,
syncWork
)
// Worker
class SyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
return try {
val response = api.sync()
saveToDatabase(response)
Result.success()
} catch (e: Exception) {
Result.retry() // Автоматический retry!
}
}
}
11. Разница между Service и WorkManager
| Параметр | Service | WorkManager |
|---|---|---|
| Гарантия выполнения | Нет | Да (даже после перезагрузки) |
| Батарея | Плохо | Отлично |
| Retry | Ручной | Автоматический |
| Constraints | Нет | Есть (сеть, батарея, idle) |
| Persisted | Нет | Да |
| Когда использовать | Редко | Почти всегда |
12. Когда Background Service всё ещё полезен?
1. Долгоживущая видимая работа (как минимум foreground)
class LongRunningService : Service() {
override fun onStartCommand(...): Int {
startForeground(1, createNotification())
// Долгая работа
doLongWork()
return START_STICKY
}
}
2. Привязанный сервис (Bound Service)
class ControlledService : Service() {
private val binder = LocalBinder()
override fun onBind(intent: Intent?): IBinder = binder
fun doSomething() { /* ... */ }
inner class LocalBinder : Binder() {
fun getService() = this@ControlledService
}
}
// Activity контролирует Service
val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as ControlledService.LocalBinder
val myService = binder.getService()
myService.doSomething()
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
bindService(intent, connection, Context.BIND_AUTO_CREATE)
13. Основные проблемы Background Service
Проблема 1: Service может быть убит в любой момент
startService(intent) // Может быть убит на 1-10 минут
Проблема 2: Нет гарантии выполнения
// Если приложение закрыта, Service умрёт
// (если не START_STICKY и не Foreground)
Проблема 3: Плохо для батареи
// Background Service будет заблокирован в Doze Mode
// Батарея разредится
Проблема 4: Сложнее в отладке
// Трудно найти утечки памяти
// Трудно найти crash'и
14. Решение: используй WorkManager
// Вместо Background Service, используй это:
val work = OneTimeWorkRequestBuilder<MyWorker>()
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresDeviceIdle(false)
.build()
)
.setBackoffCriteria(
BackoffPolicy.EXPONENTIAL,
Duration.ofSeconds(30)
)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"background_work",
ExistingWorkPolicy.KEEP,
work
)
15. Summary: Особенности Background Service
✅ Плюсы:
- Простой API
- Привычный для старых проектов
- Подходит для некоторых нишей
❌ Минусы:
- Убивается системой в Doze Mode
- Нет гарантии выполнения
- Плохо для батареи
- Ограничено 1 минутой (Android 8.0+)
- Нет retry, нет constraints
- Требует foreground на Android 8.0+
РЕКОМЕНДАЦИЯ:
Используй WorkManager для фоновой работы (99% случаев). Используй Foreground Service если ОЧЕНЬ нужно (GPS, музыка). Забудь про Background Service (устаревший подход).