Service

https://developer.android.com/about/versions/14/changes/fgs-types-required
https://developer.android.com/about/versions/15/changes/foreground-service-types
https://developer.android.com/reference/android/app/DownloadManager
16.06.2023https://habr.com/ru/companies/broadcast/articles/734236/
14.02.2018https://habr.com/ru/post/349102
28.06.2024https://youtu.be/DgCmmL4FNAs
16.06.2023https://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_CAMERAAndroid 10
connectedDeviceустройства, фитнес-трекерFOREGROUND_SERVICE_CONNECTED_DEVICEAndroid 10
dataSyncсинхронизация данныхFOREGROUND_SERVICE_DATA_SYNCAndroid 10
locationдоступ к локации, навигацияFOREGROUND_SERVICE_LOCATIONAndroid 10
mediaPlaybackвоспроизведение аудиоFOREGROUND_SERVICE_MEDIA_PLAYBACKAndroid 10
mediaProjectionпроекция контентаFOREGROUND_SERVICE_MEDIA_PROJECTIONAndroid 10
microphoneдиктофонFOREGROUND_SERVICE_MICROPHONEAndroid 10
phoneCallтелефонный звонокFOREGROUND_SERVICE_PHONE_CALLAndroid 10
healthфитнес, трекер тренировокFOREGROUND_SERVICE_HEALTHAndroid 14
remoteMessagingобмен смсFOREGROUND_SERVICE_REMOTE_MESSAGINGAndroid 14
shortServiceтаска на 3 мин-Android 14
specialUseлюбая таскаFOREGROUND_SERVICE_SPECIAL_USEAndroid 14
systemExemptedзарезервировано для системыFOREGROUND_SERVICE_SYSTEM_EXEMPTEDAndroid 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?
  1. Через BroadcastReceiver. Он позволяет отправлять и получать данные между компонентами. Подходит для простых случаев, когда необходимо отправить данные от Service к Activity или наоборот.
  1. Через Messenger и IncomingHandler. Подходит для обмена более структурированными данными и для взаимодействия между Activity и Service в том же процессе.
  1. Используя Bound Service и AIDL. Используется для более сложного и высокоуровневого обмена данными, особенно когда Service и Activity находятся в разных процессах.
Android Service. Вопросы на собесе
  1. Как ОS Android понимает какой процесс сейчас приоритетнее, а какой можно остановить?
  1. С чем связаны ограничения на запуск сервисов?
  1. Как запустить сервис в отдельном процессе?
  1. В чем отличие Service и IntentService?
  1. Зачем нужен метод onBind?
  1. Как сделать обмен данных между Activity и Service?
  1. Какое исключение возникает при долгой работе сервиса?
  1. Какие есть варианты запуска сервисов?
  1. Жизненный цикл сервиса?

    Различается для обычных и Bound-сервисов.

    Для обычных: onCreate onStartCommand onDestroy.

    Для Bound: onCreate onStartCommand onBind onUnbind onDestroy.

  1. Какие бывают типы сервисов?

    1. Foreground Service (видим через уведомление)

    2. Background Service (не взаимодействует с пользователем)

    3. Bound Service (можно привязаться из другого компонента и взаимодействовать с ним через IBinder, может быть как Foreground так и Background)

  1. На каком потоке вызывается метод Service.onStartCommand?

    На клавном (UI) потоке приложения