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
.
Запуск в отдельном процессе?
Чтобы запустить сервис в отдельном процессе нужно указать в манифесте:
android:process=":someProcess"
Плюсы:
• кейс с пушами. если приложение не работает, при получении пуша не вызывался App.onCreate.
• независимость от ЖЦ приложения, если упадет сможем закончить работу (например библиотека с крашлитикой).
• больше ресурсов, например если хотим вынести тяжелую операцию из основного процесса.
Минусы:
• напрямую привязаться к сервису в другом процессе можно только через AIDL.
Background
Выполняется в фоновом потоке. Начиная с API 26, приложения в фоне не могут создавать фоновые сервисы, решением в этом случае может быть WorkManager. В Android 8.0 (API level 26) введены ограничения на работу с фоновыми сервисами. Эти ограничения касаются приложений с targetSdkVersion ≥ 26, но пользователь может включить ограничения для всех приложений в настройках. Начиная с Android 8.0, Фоновые сервисы работают пока пользователь взаимодействует с приложением. Система убивает все фоновые сервисы через несколько минут после того, как пользователь покидает приложение. Нельзя запустить фоновый сервис для приложения, с которым не взаимодействует пользователь. Вместо фоновых сервисов рекомендуется использовать JobScheduler или видимые сервисы. Чтобы запустить видимый сервис, нужно сначала запустить фоновый сервис методом startService
а потом вызвать метод startForeground() на этом сервисе. Но, начиная с Android 8.0, нельзя запустить фоновый сервис, если пользователь не взаимодействует с приложением. Поэтому в API level 26 был добавлен метод startForegroundService
который делает то же, что и startService(), но обещает системе, что метод startForeground
будет вызван сразу после старта сервиса. Если не вызывать startForeground
то система убьет сервис через несколько секунд.
Lifecycle
Различается для started и bound сервисов.
• onCreate
вызывается один раз, когда сервис создается системой. Для создания started сервиса используется метод startService
или bindService
.
• onStartCommand
вызывается, когда сервис переходит в активное состояние. Код, который выполняет сервис, должен быть написан в этом методе. Вызовется столько раз, сколько был запущен сервис.
• onBind
вызывается системой, когда первый клиент присоединяется к сервису вызовом метода bindService
. После вызова этого метода bound сервис переходит в активное состояние.
• onUnbind
вызывается системой, когда все клиенты отсоединились от сервиса вызовом метода unbindService
.
• onDestroy
вызывается, когда сервис уничтожается системой. Это происходит после вызова stopSelf
или stopService
. Также система может убить процесс с фоновым сервисом когда не хватает ресурсов или, начиная с Android 8.0, для ограничения фоновых работы или вызывается после onUnbind
перед тем как система уничтожит сервис.
onStartCommand
После того, как мы вызываем startService
срабатывает onStartCommand
который возвращает одну из констант
• START_NOT_STICKY
сервис не будет перезапущен после того, как был убит системой
• START_STICKY
сервис будет перезапущен после того, как был убит системой
• START_REDELIVER_INTENT
сервис будет перезапущен после того, как был убит системой. Кроме этого, сервис снова получит все вызовы startService
которые не были завершены методом stopSelf
.
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
Служит для реализации связи между компонентами приложения и сервисом. Пример: переключение треков в плеере. С обычным сервисом можно взаимодействовать только через методы Intent startService
и stopService
которые обработаются в onStartCommand
.
onBind
Вызывается системой, когда клиент вызывает bindService для привязки к сервису. Возвращает объект, реализующий интерфейс IBinder. Этот объект позволяет клиенту взаимодействовать с сервисом. Если сервис не предоставляет интерфейс для взаимодействия, метод должен вернуть null.
onUnbind
Вызывается системой, когда последний клиент, который был привязан к сервису, вызывает unbindService. Если метод возвращает true, это означает, что система может повторно привязать клиента к сервису, и в этом случае onBind будет вызван снова. Если возвращается false, система не будет повторно привязывать клиента к сервису.
JobIntentService
Используется для тех же целей, что и IntentService
и имеет похожий API. Для старта используется статический метод enqueueWork
который использует Context.startService
для API level < 26 и JobScheduler.enqueue
для API level ≥ 26. После этого система стартует сервис и вызывает в фоновом потоке метод onHandleWork
.
IntentService
Запускается и работает в фоновом потоке из коробки, в отличие от обычных сервисов, работающих в главном потоке. Выполняют работу в методе onHandleIntent
после чего останавливается. Операции в onHandleIntent
не могут быть прерваны, связь с основным потоком отсутствует, можно вернуть результат через уведомление или broadcast receiver. На IntentService распространяются все ограничения на работу фоновых сервисов, введенные в API 26. Начиная с API 30 - является устаревшим, рекомендуется заменить на WorkManager или JobIntentService.
JobScheduler
API для планирования различных типов заданий в рамках платформы, которые будут выполняться в собственном процессе приложения. В версии для Android 5.0 максимальное время выполнения заданий составляло одну минуту. Начиная с версии Android 6.0 и заканчивая версией Android 11, максимальное время выполнения заданий составляло 10 минут. Начиная с версии Android 12, задания по-прежнему будут останавливаться через 10 минут, если система занята или нуждается в ресурсах, но в противном случае задания могут продолжать выполняться дольше 10 минут.
AlarmManager
Доступ к службам системной сигнализации. Они позволяют запланировать запуск вашего приложения в какой-то момент в будущем. При срабатывании будильника система транслирует зарегистрированное для него сообщение, автоматически запуская целевое приложение, если оно еще не запущено. Зарегистрированные будильники сохраняются, пока устройство находится в спящем режиме (и могут дополнительно разбудить устройство, если они сработают в это время), но будут удалены, если оно будет выключено и перезагружено. AlarmManager предназначен для случаев, когда вы хотите, чтобы код вашего приложения выполнялся в определенное время, даже если ваше приложение в данный момент не запущено. Для обычных операций синхронизации (тиков, тайм-аутов и т.д.) проще и эффективнее использовать Handler.
DownloadManager
Системный сервис для обработки длительных загрузок. Клиенты могут запросить загрузку URI в конкретный целевой файл. DownloadManager
будет выполнять загрузку в фоновом режиме заботясь о взаимодействии HTTP и повторяя загрузку после сбоев или при изменении подключения и перезагрузке системы.
WakeLock
Чтобы не разрядить аккумулятор, Android-устройство, которое не используется, быстро засыпает. Однако бывают случаи, когда приложению необходимо разбудить экран или ЦП и поддерживать его в активном состоянии, чтобы выполнить некоторую работу.
Как сделать обмен данных между Activity и Service?
- Через BroadcastReceiver. Он позволяет отправлять и получать данные между компонентами. Подходит для простых случаев, когда необходимо отправить данные от Service к Activity или наоборот.
- Через Messenger и IncomingHandler. Подходит для обмена более структурированными данными и для взаимодействия между Activity и Service в том же процессе.
- Используя Bound Service и AIDL. Используется для более сложного и высокоуровневого обмена данными, особенно когда Service и Activity находятся в разных процессах.
Android Service. Вопросы на собесе
- Как ОS Android понимает какой процесс сейчас приоритетнее, а какой можно остановить?
- С чем связаны ограничения на запуск сервисов?
- Как запустить сервис в отдельном процессе?
- В чем отличие Service и IntentService?
- Зачем нужен метод onBind?
- Как сделать обмен данных между Activity и Service?
- Какое исключение возникает при долгой работе сервиса?
- Какие есть варианты запуска сервисов?
- Жизненный цикл сервиса?
Различается для обычных и Bound-сервисов.
Для обычных:
onCreate
onStartCommand
onDestroy
.Для Bound:
onCreate
onStartCommand
onBind
onUnbind
onDestroy
.
- Какие бывают типы сервисов?
1. Foreground Service (видим через уведомление)
2. Background Service (не взаимодействует с пользователем)
3. Bound Service (можно привязаться из другого компонента и взаимодействовать с ним через
IBinder
, может быть как Foreground так и Background)
- На каком потоке вызывается метод Service.onStartCommand?
На клавном (UI) потоке приложения