Media3: как фоновый плеер связывается с экраном, системой и гарнитурой
Player, MediaSession, служба, контроллер и audio focus отвечают за разные стороны воспроизведения — от кнопки паузы до восстановления очереди.

Почему плеер живёт отдельно от Activity
Для фонового воспроизведения Media3 размещает Player и MediaSession в отдельной службе. Activity может исчезнуть с экрана, пока пользователь слушает звук через системные элементы управления. MediaSessionService позволяет внешним клиентам обнаружить сессию и подключиться к ней, не открывая интерфейс приложения.
Player и сессия создаются в жизненном цикле службы и освобождаются при её уничтожении. Для foreground service воспроизведения объявляются соответствующие разрешения и тип mediaPlayback. Это не означает бессрочного права выполнять любую работу в фоне: служба организована вокруг конкретного сценария воспроизведения. Удаление Activity из списка недавних задач также не обязано немедленно обрывать текущий звук — документация описывает отдельное поведение службы при продолжающемся воспроизведении.

Сессия принимает команды, но не декодирует звук
Player — интерфейс воспроизведения; ExoPlayer является его реализацией. MediaSession связывает этот объект с внешними источниками команд. Кнопка гарнитуры, системная карточка, часы или автомобильный интерфейс обращаются к сессии, а она передаёт подходящие операции плееру. Наличие сессии не заменяет источник медиаданных или декодер.
На подключении приложение получает информацию о контроллере и может принять либо отклонить запрос. Для разных клиентов могут различаться доступные команды. Это важно для операций, выходящих за пределы обычной паузы: например, изменение очереди не обязано разрешаться каждому участнику. Предпочтения media buttons описывают желаемые кнопки, команды и места, но конкретный контроллер решает, какие элементы способен показать в своём интерфейсе. Системная панель, часы и большой экран не обязаны воспроизводить одинаковую раскладку. Общим остаётся смысл команды и состояние сессии, а не точная композиция кнопок приложения.

Контроллер внутри самого приложения
MediaController нужен не только сторонним клиентам. Экран приложения может использовать его для доступа к плееру, который находится в отдельной службе. Контроллер реализует Player: вызов play на нём отправляет команду сессии, которая затем обращается к реальному проигрывателю. Это единый интерфейс управления, но не второй самостоятельно играющий экземпляр.
Подключение через SessionToken выполняется асинхронно. До завершения построения контроллера интерфейс не должен считать связь уже установленной. Через слушателей поступают изменение состояния, доступных команд и сведения об отключении. Освобождение контроллера разрывает ненужную связь с экраном, а не обязательно останавливает фоновой плеер. MediaBrowser расширяет этот механизм навигацией по библиотеке MediaLibraryService: можно получить корневой узел и его потомков. Дерево каталога и текущая очередь воспроизведения при этом не являются одним объектом. Пользователь может просматривать один раздел библиотеки, продолжая слушать ранее сформированную очередь.
READY ещё не означает, что слышен звук
Player различает IDLE, BUFFERING, READY и ENDED. READY означает готовность немедленно воспроизводить с текущей позиции, но не доказывает фактического воспроизведения. Дополнительно учитывается playWhenReady — намерение проигрывать — и отсутствие причины подавления. Обобщённое isPlaying истинно только при совпадении необходимых условий.
Изменения доступны через Player.Listener. Отдельные callback удобны для конкретного события, а onEvents вызывается после группы связанных событий и позволяет читать согласованное состояние. Ошибка воспроизведения приходит через onPlayerError перед переходом в IDLE. Следовательно, одинаковая неподвижная позиция может означать паузу, ожидание буфера, конец списка или ошибку. Одной переменной «играет/не играет» недостаточно, чтобы объяснить пользователю причину. Кроме того, намерение продолжить после буферизации может сохраняться, даже когда isPlaying временно ложно. Интерфейс плеера отражает одновременно готовность ресурса, пользовательскую команду и системные ограничения, которые не сводятся к одному переключателю.
| Значение | Смысл |
|---|---|
| IDLE | Начальное, остановленное или ошибочное состояние |
| BUFFERING | Нельзя немедленно воспроизводить с текущей позиции |
| READY | Плеер готов; воспроизведение дополнительно зависит от намерения и suppression |
| ENDED | Завершено воспроизведение всего медиа |
Источник: Android: Player states
Audio focus согласует приложения
Audio focus регулирует взаимодействие источников звука разных приложений. Получение фокуса не означает, что его удастся удерживать до конца файла: другое приложение может запросить его позже. В зависимости от ситуации исходное воспроизведение приостанавливается либо уменьшается громкость. Тип содержимого, например речь, задаётся через AudioAttributes.
ExoPlayer умеет управлять фокусом автоматически при соответствующей настройке setAudioAttributes. В этом случае не требуется параллельно создавать конкурирующую собственную обработку тех же изменений. Платформенные условия зависят от версии Android. Для приложений с target Android15/API35 и выше запрос фокуса требует, чтобы приложение было верхним либо выполняло foreground service; иначе возможен AUDIOFOCUS_REQUEST_FAILED. Это условие нельзя заменить наличием видимой кнопки play. Фокус также не является разрешением на доступ к микрофону или файлу. Он описывает право текущего источника участвовать в воспроизведении в данный момент, когда на устройстве могут звучать другие приложения и системные события.
Очередь — изменяемый набор MediaItem
Player предоставляет API списка воспроизведения. Элементы можно добавлять, перемещать, удалять и заменять до старта и во время проигрывания. Один и тот же MediaItem допускается использовать несколько раз. У списка не требуется единый формат всех элементов: API способен описывать разные типы медиа, если соответствующее воспроизведение поддерживается выбранной конфигурацией.
Позиция в очереди и идентичность записи различаются. После перемещения элемента его индекс меняется, но содержательное обозначение записи не обязано изменяться вместе с ним. Операция с очередью также не означает скачивания всех файлов для офлайн-доступа: MediaItem описывает источник и связанные сведения, а получение данных организуется отдельным механизмом. Режимы повтора и перемешивания изменяют порядок перехода между элементами, но не превращают сам список в постоянное хранилище пользовательской библиотеки. Поэтому сохранённая очередь, текущая позиция и доступность исходных ресурсов остаются отдельными частями состояния приложения.
Восстановление после остановки службы
Playback resumption включается отдельно. По умолчанию пользователь не может возобновить остановленную службу только потому, что раньше существовал MediaSessionService. Для этого объявляются media button receiver и callback onPlaybackResumption. Приложение возвращает список элементов и начальную позицию, сохранённые им ранее.
Восстановление не означает сохранения прежнего процесса в памяти. Очередь, метаданные и положение нужно восстановить из устойчивых данных, а доступность сетевых источников может измениться. Документация отдельно отмечает, что сразу после перезагрузки сети может ещё не быть. Таким образом, команда возобновления запускает новый жизненный цикл с состоянием, а не продолжает существование старой Activity. Источник управления, жизненный цикл службы и фактическая готовность медиаданных снова остаются независимыми условиями.


