Service
16.06.2023 | https://habr.com/ru/companies/broadcast/articles/734236/ |
14.02.2018 | https://habr.com/ru/post/349102 |
28.06.2024 | https://youtu.be/DgCmmL4FNAs |
16.06.2023 | https://youtu.be/Ovzc2kaeSCY |
Service
Компонент для выполнения длительных операций в фоновом режиме.
• По умолчанию работают в главном потоке (UI thread, не main thread, так как он хоть и главный, но не всегда является потоком пользовательского интерфейса).
• Сервисы нужны, чтобы связать их lifecycle с lifecycle приложения, так как обычные потоки не всегда совпадут с activity lifecycle (+возможность запуститься не только из activity).
• Как работает: в момент создания сервиса вызывается onCreate
, затем запускается thread или executor, который после завершения дает знать сервису, чтобы тот вызвал stopSelf
. Наша задача: сообщить сервису о начале и завершении работы.
• Чтобы сервис работал после закрытия activity нужно в течение 5 секунд после его запуска отобразить уведомление, иначе ANR.
• stopSelf
- завершить работу сервиса.
• Intent.stopService
- завершить работу сервиса через intent, вызовется stopSelf
- onDestroy
.
onCreate
Вызывается один раз при создании сервиса.
override fun onCreate() {
super.onCreate()
Log.d("MyService", "Сервис создан")
}
onStartCommand
Вызывается каждый раз, когда сервис запускается через startService
. Возвращает код, указывающий поведение сервиса при его перезапуске:
• START_NOT_STICKY
: сервис не перезапускается после уничтожения.
• START_STICKY
: сервис перезапускается, но с null
интентом.
• START_REDELIVER_INTENT
: сервис перезапускается с последним интентом.
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = intent?.getStringExtra("key")
Log.d("MyService", "Получены данные: $data")
return START_STICKY
}
onDestroy
Вызывается, когда сервис уничтожается системой или вручную.
override fun onDestroy() {
super.onDestroy()
Log.d("MyService", "Сервис уничтожен")
}
Запуск в отдельном процессе
Чтобы запустить сервис в отдельном процессе нужно указать в манифесте:
android:process=":someProcess"
Плюсы:
• Кейс с пушами. если приложение не работает, при получении пуша не вызывался App.onCreate.
• Независимость от ЖЦ приложения, если упадет сможем закончить работу (например библиотека с крашлитикой).
• Больше ресурсов, например если хотим вынести тяжелую операцию из основного процесса.
Минусы:
• Напрямую привязаться к сервису в другом процессе можно только через AIDL.
Background Service
Выполняет задачи в отдельном потоке без прямого взаимодействия с пользователем.
• Начиная с API 26 (Android 8.0) приложения не могут вызывать startService
, если они не находятся на переднем плане. Если попытаться запустить фоновый сервис из фона, он будет немедленно завершён системой.
• Начиная с API 26 (Android 8.0) вместо startService
, приложения должны использовать startForegroundService
для запуска сервисов в фоне.
• Используй WorkManager
для фоновых операций, не требующих немедленного выполнения.
Foreground Service
Сервис, о котором пользователь уведомлен. Выполняется в UI thread
. Во время выполнения отображает уведомление. Пример: аудиоплеер. Приоритет больше, чем background.
• stopForeground
завершит выполнение сервиса на переднем плане и опционально удалит уведомление.
Foreground Service Type
Этот атрибут дает понять системе к какой категории отнести сервис. Указывается внутри элемента <service>
в AndroidManifest
. Можно комбинировать несколько типов.
• Android 10 (API 29) - добавлен необязательный атрибут android:foregroundServiceType
.
• Android 14 (API 34) - указание типа стало обязательным. Если приложение предназначенное для Android 14 не определяет типы для данной службы в AndroidManifest
система выкинет MissingForegroundServiceTypeException
при вызове startForeground
. Также нужно объявить определенные permissions на основе типа сервиса иначе получим SecurityException
.
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
Foreground service type | Описание | Permission in AndroidManifest | Появился |
---|---|---|---|
camera | доступ к камере, видеочат | FOREGROUND_SERVICE_CAMERA | Android 10 |
connectedDevice | устройства, фитнес-трекер | FOREGROUND_SERVICE_CONNECTED_DEVICE | Android 10 |
dataSync | синхронизация данных | FOREGROUND_SERVICE_DATA_SYNC | Android 10 |
location | доступ к локации, навигация | FOREGROUND_SERVICE_LOCATION | Android 10 |
mediaPlayback | воспроизведение аудио | FOREGROUND_SERVICE_MEDIA_PLAYBACK | Android 10 |
mediaProjection | проекция контента | FOREGROUND_SERVICE_MEDIA_PROJECTION | Android 10 |
microphone | диктофон | FOREGROUND_SERVICE_MICROPHONE | Android 10 |
phoneCall | телефонный звонок | FOREGROUND_SERVICE_PHONE_CALL | Android 10 |
health | фитнес, трекер тренировок | FOREGROUND_SERVICE_HEALTH | Android 14 |
remoteMessaging | обмен смс | FOREGROUND_SERVICE_REMOTE_MESSAGING | Android 14 |
shortService | таска на 3 мин | - | Android 14 |
specialUse | любая таска | FOREGROUND_SERVICE_SPECIAL_USE | Android 14 |
systemExempted | зарезервировано для системы | FOREGROUND_SERVICE_SYSTEM_EXEMPTED | Android 14 |
Bound Service
Особый тип сервиса в Android, который позволяет другим компонентам (например, Activity
) взаимодействовать с ним через интерфейс IBinder
. Он предоставляет механизм для клиентов, чтобы подключаться к сервису и выполнять операции на его экземпляре.
• В отличие от обычных Started Services, которые могут работать независимо от клиентских запросов, Bound Services существуют до тех пор, пока хотя бы один клиент к ним привязан.
• Как только все клиенты отключаются, сервис может быть автоматически остановлен системой.
• С помощью IBinder интерфейса клиент может взаимодействовать с сервисом. Это позволяет клиенту вызывать методы сервиса напрямую.
class MyBoundService: Service() {
// Класс для взаимодействия с клиентами
private val binder = LocalBinder()
// Метод для получения экземпляра сервиса
inner class LocalBinder: Binder() {
fun getService(): MyBoundService = this@MyBoundService
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
// Пример метода, доступного клиентам
fun getData(): String {
return "Some data"
}
}
// Привязка к Bound Service из Activity
class MainActivity: AppCompatActivity() {
private var myService: MyBoundService? = null
private var isBound = false
private val connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as MyBoundService.LocalBinder
myService = binder.getService()
isBound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
isBound = false
}
}
override fun onStart() {
super.onStart()
// Привязка к сервису
Intent(this, MyBoundService::class.java).also { intent ->
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
// Отвязка от сервиса
if (isBound) {
unbindService(connection)
isBound = false
}
}
fun useService() {
if (isBound) {
val data = myService?.getData()
// Использовать данные
}
}
}
IBinder
Интерфейс для взаимодействия между клиентом и сервисом. Он используется, чтобы клиент мог вызывать методы сервиса или получать данные напрямую.
class MyService: Service() {
private val binder = LocalBinder()
override fun onBind(intent: Intent?): IBinder {
return binder
}
inner class LocalBinder : Binder() {
fun getService(): MyService = this@MyService
}
fun performAction(): String {
return "Действие выполнено!"
}
}
onBind
Вызывается, когда клиент пытается подключиться к сервису через bindService
. Он возвращает объект IBinder
, который клиент использует для взаимодействия с сервисом.
class MyService: Service() {
private val binder = LocalBinder()
override fun onBind(intent: Intent?): IBinder {
Log.d("MyService", "Клиент подключён")
return binder
}
inner class LocalBinder : Binder() {
fun getService(): MyService = this@MyService
}
}
onUnbind
Вызывается, когда все клиенты отсоединяются от сервиса, вызвав unbindService
. Это сигнал о том, что больше нет активных подключений.
override fun onUnbind(intent: Intent?): Boolean {
return true
}
IntentService
Класс для выполнения однократных фоновых задач в отдельном потоке. Упрощает работу с многопоточностью, автоматически завершает себя после выполнения задачи.
JobScheduler
API для планирования фоновых задач, оптимизированное для энергосбережения.
• Появился в API 21 (Android 5.0).
JobIntentService
Класс для выполнения фоновых задач, совместимый с API ниже 26. Он сочетает преимущества IntentService
и JobScheduler
.
AlarmManager
Системный сервис для запуска задач в определённое время или с заданной периодичностью, даже если приложение закрыто.
DownloadManager
Системный сервис для загрузки файлов в фоновом режиме.
WakeLock
Механизм для управления энергопотреблением. Он позволяет приложениям удерживать устройство в активном состоянии, предотвращая переход в спящий режим.
Вопросы на собесе (18)
- С чем связаны ограничения на запуск сервисов?
Ограничения на запуск сервисов связаны с экономией ресурсов и управления фоновыми задачами, чтобы избежать чрезмерного потребления батареи и улучшить производительность устройства.
- Какие бывают типы сервисов?
• Foreground Service (видим через уведомление)
• Background Service (не взаимодействует с пользователем)
• Bound Service (можно привязаться из другого компонента и взаимодействовать с ним через
IBinder
, может быть как Foreground так и Background)
- Зачем сделали Foreground Service?
Для выполнения задач, которые требуют постоянного выполнения, даже если пользователь не взаимодействует с приложением. Они обеспечивают важные функции, такие как отслеживание местоположения или воспроизведение музыки, и имеют более высокий приоритет, чем обычные фоновый сервисы. Это помогает предотвратить их остановку системой, обеспечивая надежность приложений.
- Как запустить сервис в отдельном процессе?
Указать атрибут
android:process
в манифесте.
- Разница между Service и IntentService?
Service
работает в основном потоке приложения, что может привести к блокировке интерфейса, если выполняются длительные операции.IntentService
же создает отдельный поток для обработки задач, автоматически останавливаясь после завершения работы. Это делаетIntentService
более подходящим для фоновых задач, требующих долгого выполнения.
- Что такое Bound-сервис?
Bound
Service
позволяет компонентам, таким какActivity
, связываться с сервисом и вызывать его методы через интерфейс. Это обеспечивает более тесное взаимодействие между сервисом и компонентами приложения, что полезно для обмена данными или выполнения задач по запросу.
- Разница между обычным Service и Bound Service?
Обычный
Service
выполняет длительные операции в фоновом режиме и не предоставляет интерфейса для взаимодействия с компонентами приложения. BoundService
, в отличие от него, позволяет компонентам, таким какActivity
, связываться с сервисом и вызывать его методы через интерфейс. Это обеспечивает более тесное взаимодействие между сервисом и компонентами приложения, что полезно для обмена данными или выполнения задач по запросу.
- Как сделать обмен данных между Activity и Service?
• Использовать Bound Service.
• Используя Bound Service и AIDL. Используется для более сложного и высокоуровневого обмена данными, особенно когда
Service
иActivity
находятся в разных процессах.• Через
BroadcastReceiver
. Он позволяет отправлять и получать данные между компонентами. Подходит для простых случаев, когда необходимо отправить данные отService
кActivity
или наоборот.• Через
Messenger
иIncomingHandler
. Подходит для обмена более структурированными данными и для взаимодействия междуActivity
иService
в том же процессе.
- Зачем нужен метод onBind?
Используется для связывания
Activity
с сервисом. Он возвращает объектIBinder
, который позволяетActivity
взаимодействовать с сервисом, вызывая его методы напрямую. Это необходимо для реализации "bound services", где компонент может взаимодействовать с сервисом, пока он запущен.
- Какое исключение возникает при долгой работе сервиса?
При долгой работе сервиса может возникнуть исключение
IllegalStateException
илиServiceIntentTooLongException
, если сервис не отвечает в течение определенного времени, особенно в случае, когда он запущен черезstartService()
.
- Какие есть способы запуска сервисов?
•
startService()
Запускает сервис для фоновой работы.•
bindService()
Связывает компонент с сервисом для взаимодействия.•
IntentService
Автоматически завершает себя после выполнения задачи.•
JobIntentService
Выполняет задачи с учетом ограничений (сеть, заряд).
- Жизненный цикл сервиса?
Различается для обычных и Bound-сервисов.
Для обычных:
onCreate
onStartCommand
onDestroy
.Для Bound:
onCreate
onStartCommand
onBind
onUnbind
onDestroy
.
- На каком потоке вызывается метод Service.onStartCommand?
На клавном (UI) потоке приложения.
- Как работает межпроцессорная коммуникация в сервисах?
•
Binder
Основной механизм IPC в Android. Позволяет процессам обмениваться данными и вызывать методы друг друга. Сервис может возвращать объектBinder
, чтобы другие компоненты могли взаимодействовать с ним.•
Messenger
Обертка надBinder
, которая используетMessage
для отправки и получения данных. Удобно для простых взаимодействий между компонентами.•
AIDL
Позволяет создавать сложные интерфейсы для общения между процессами. Определяете интерфейс в AIDL-файле, и Android автоматически генерирует соответствующий код.•
ContentProvider
Позволяет приложениям обмениваться данными через единый интерфейс. Другие приложения могут запрашивать, вставлять, обновлять или удалять данные черезContentProvider
.
- Есть ли смысл использовать сервис если можно выполнять работу в Application.onCreate?
Использовать сервис имеет смысл, если работа должна выполняться в фоновом режиме, даже когда приложение закрыто, чего нельзя достичь через
Application.onCreate
.
- Какие есть альтернативы сервисам в Android?
•
WorkManager
— используется для фоновых задач, которые должны гарантированно выполняться, например, синхронизация данных, даже если приложение закрыто.•
JobScheduler
— подходит для задач, которые должны быть запущены при выполнении определённых условий, таких как подключение к сети или зарядка устройства.
- Какой из следующих методов используется для запуска сервисов в Android 8.0 и выше?
•
startForegroundService()
•
runService()
•
startService()
•
bindService()
- На каком потоке по умолчанию работает Service?
• MainThread
• ServiceThread
• NewThread
• ApplicationThread