Реалізація фонового воспроизведення аудіо у мобільному додатку
Фоновое воспроизведення — це не одна рядок коду. iOS та Android за замовчуванням зупиняють аудіо при уходе додатку у фон. Щоб музика, подкаст чи звук гри продовжували грати, потрібно правильно налаштувати AudioSession/AudioFocus, зареєструвати Background Mode та обробити системні перерви. І все це — окремо для кожної платформи.
iOS: AVAudioSession та Background Mode
Три обов'язкових кроки:
1. Info.plist Background Mode. Додати UIBackgroundModes → audio. Без цього iOS убьёт аудіо через кілька секунд після уходу у фон.
2. AVAudioSession категорія .playback. Тільки ця категорія дозволяє воспроизведення при заблокованому екрані та у фоні. .ambient замолкає при блокуванні — типова помилка.
func configureAudioSession() {
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(
.playback,
mode: .default,
options: [.allowBluetooth, .allowAirPlay, .mixWithOthers] // видалити .mixWithOthers якщо потрібно перериватись інші додатки
)
try session.setActive(true)
} catch {
print("AudioSession error: \(error)")
// не ігнорувати — без активної сесії фоновое воспроизведение не работает
}
}
3. Обробка перерив. Вхідний дзвінок — iOS деактивує AVAudioSession. Після — потрібно явно восстановить:
NotificationCenter.default.addObserver(
forName: AVAudioSession.interruptionNotification,
object: nil,
queue: .main
) { notification in
guard let typeValue = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { return }
switch type {
case .began:
audioPlayer.pause()
case .ended:
let shouldResume = (notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? UInt)
.flatMap { AVAudioSession.InterruptionOptions(rawValue: $0).contains(.shouldResume) } ?? false
if shouldResume {
try? AVAudioSession.sharedInstance().setActive(true)
audioPlayer.play()
}
@unknown default: break
}
}
Додатково — AVAudioSession.routeChangeNotification: наушники відключились → поставить на паузу (стандартне поведінка всіх плеєрів).
Android: Foreground Service та Audio Focus
На Android фоновое воспроизведение потребує Foreground Service з типом mediaPlayback. Без Foreground Service Android убьёт процес через кілька хвилин.
class AudioPlaybackService : Service() {
private lateinit var player: ExoPlayer
private lateinit var mediaSession: MediaSessionCompat
override fun onCreate() {
super.onCreate()
player = ExoPlayer.Builder(this).build()
// Foreground notification — обов'язкова для Foreground Service
val notification = buildMediaNotification()
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
mediaSession = MediaSessionCompat(this, "AudioService")
mediaSession.setCallback(mediaSessionCallback)
mediaSession.isActive = true
}
}
Починаючи з Android 14 (API 34) — FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK обов'язковий у манифесті та в startForeground. Без явного типу — SecurityException.
Audio Focus. Запросити перед началом воспроизведения:
val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener { focusChange ->
when (focusChange) {
AudioManager.AUDIOFOCUS_LOSS -> player.pause()
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> player.pause()
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> player.volume = 0.3f
AudioManager.AUDIOFOCUS_GAIN -> {
player.volume = 1.0f
player.play()
}
}
}
.build()
val result = audioManager.requestAudioFocus(audioFocusRequest)
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
player.play()
}
MediaSession та Lock Screen Controls
Без MediaSession (Android) / MPRemoteCommandCenter (iOS) управління з Lock Screen та наушників не работает.
Android (Jetpack Media3 / MediaSessionCompat): MediaNotification.Provider створює уведомлення з кнопками попередній/пауза/наступний. MediaSession.Callback обробляє команди. MediaBrowserServiceCompat дозволяє Android Auto підключатися до сервісу.
iOS (MPNowPlayingInfoCenter + MPRemoteCommandCenter): оновлювати nowPlayingInfo при кожній зміні треку. MPRemoteCommandCenter.shared().playCommand.addTarget — обработчик команди Play. Якщо не оновлювати MPNowPlayingInfoPropertyElapsedPlaybackTime періодично — прогрес-бар на Lock Screen буде неточним.
Типові помилки
Звук після вхідного дзвінка на iOS. Забутий AVAudioSession.setActive(true) після закінчення перерви. Додати в обработчик InterruptionType.ended.
Уведомлення зникає — сервис убивается. На Android при зупинці воспроизведения потрібно або вилучити stopForeground(false) (убрати foreground, але не уведомлення), або правильно перейти в PAUSED стан. Якщо вилучити stopForeground(true) — уведомлення пропаде, OS убьёт сервис вскоре.
Bluetooth-наушники — нет звука. На iOS: options: .allowBluetooth у setCategory. На Android: AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA) — без цього Bluetooth може переключиться у SCO режим (телефонний, погана якість).
Графіки
2–3 робочих дні для стандартного фонового воспроизведения з Lock Screen controls. Інтеграція з Android Auto / CarPlay чи складний lifecycle з кількома типами контенту — до 5 днів. Вартість розраховується індивідуально.







