Чому Picture-in-Picture Android ламається на реальних пристроях
Picture-in-Picture Android перестає працювати там, де закінчується емулятор і починається живий пристрій: вікно не з'являється при згортанні застосунку, звук обривається через пару секунд, а після повернення з режиму картинка в картинці інтерфейс відкривається заново і втрачає стан плеєра.
На стенді все відтворюється стабільно, у користувача — ні, і це найдорожча категорія дефектів: відтворюваність майже нульова, а скарги надходять без логів.
Причина в тому, що режим картинка в картинці — це не «увімкнути прапорець», а домовленість між застосунком і системою.
Пристрій вирішує, чи дозволити перехід, коли у нього достатньо ресурсів і чи не блокує політика виробника; система може відхилити вхід, якщо активність не готова до зміни конфігурації; а фонове відтворення живе окремо від вікна і потребує власної служби, інакше звук вмирає разом з інтерфейсом.
Різні оболонки виробників трактують ці правила по-своєму — звідси «PiP не запускається» на одних моделях і працює на інших.
Ціна такого збою вища, ніж здається. Користувач згортає застосунок саме щоб продовжувати слухати або дивитися — якщо вікно не з'явилося або зник звук, сценарій рветься, і людина йде до конкурента, який цей сценарій тримає.
При цьому сам дефект не видно у звітах про падіння: застосунок не падає, він просто поводиться не так, як очікувалося.
Розбираємо, як це закривається: де саме губиться перехід у режим картинка в картинці, чому відвалюється фонове відтворення і які перевірки відділяють робочий сценарій від «працює у мене на телефоні».
Що дає коректно налаштований режим «картинка в картинці»
Безперервність відтворення при згортанні
Ключова метрика — частка сесій, у яких відео продовжує грати після переходу застосунку у фон.
На стандартному onPause() плеєр зупиняється, і користувач втрачає позицію; за коректного налаштування Picture-in-Picture активність переходить у onStop(), а не знищується, і відтворення відбувається без розриву.
За нашою практикою це підвищує утримання сесії перегляду в 1,5–2 рази: користувач згортає застосунок, щоб відповісти в месенджері, і повертається на ту саму секунду.
Стабільність вікна та керування життєвим циклом
PiP-вікно живе в окремому завданні системи, тому його поведінка визначається тим, як застосунок обробляє onPictureInPictureModeChanged() і onUserLeaveHint().
Якщо не переключити розмітку на компактну і не відв'язати важкі в'ю від життєвого циклу, вікно згортається або показує чорний кадр.
Ми розділяємо UI на два стани — повний екран і компактний набір контролів (play/pause, перемотування), — і вікно залишається керованим за будь-якого розміру.
| Варіант реалізації | Коли підходить | Обмеження |
|---|---|---|
Activity в PiP (enterPictureInPictureMode) |
Відео, дзвінки, трансляції в межах одного екрана | Потребує ручного керування розміткою та життєвим циклом |
| Окремий сервіс відтворення | Фонове аудіо та відео зі сповіщенням | Немає системного вікна, лише сповіщення-контрол |
| PiP поверх іншого застосунку | Багатозадачність: чат + відео одночасно | Залежить від підтримки виробником пристрою |
Що це дає в цифрах
Після переведення плеєра на єдиний життєвий цикл з обробкою PiP-переходів частка відмов при згортанні падає з ~15% до одиниць відсотків, а кількість повторних запусків застосунку знижується — користувачу не потрібно заново шукати контент.
Ми перевіряємо сценарії на різних прошивках, тому що поведінка вікна відрізняється між оболонками виробників.
Android Picture-in-Picture — системний режим, який ми налаштовуємо під конкретний сценарій перегляду, а не вмикаємо «як є».
Етапи впровадження режиму «картинка в картинці» в застосунок
Впровадження Picture-in-Picture Android починається не з коду, а з аудиту: дивимося, як застосунок поводиться при згортанні та блокуванні екрана, де втрачається стан плеєра, які екрани використовують один і той самий відеоконтейнер.
На цьому етапі фіксуємо цільові сценарії — фонове відтворення, перехід між розділами, відповідь на дзвінок — і визначаємо, які з них ламаються зараз. Далі вибудовуємо роботу за етапами, щоб зміни не зачепили решту логіки.
- Аудит застосунку. Розбираємо поточний життєвий цикл екранів із відео, точки втрати фокуса та повторної ініціалізації плеєра. Фіксуємо, які стани потрібно зберігати при переході в PiP і назад.
- Проєктування поведінки вікна. Визначаємо пропорції, реакцію на перетягування та закриття, набір доступних дій у згорнутому режимі. Узгоджуємо, що відбувається при поверненні в повноекранний режим.
- Налаштування життєвого циклу. Пов'язуємо перехід у PiP із подіями активності, щоб відтворення не переривалося і позиція не скидалася. Окремо опрацьовуємо конфлікт з іншими вікнами поверх застосунку.
- Реалізація та інтеграція з плеєром. Підключаємо режим до відеоконтейнера, синхронізуємо стан звуку, субтитрів і вибраної якості при згортанні та розгортанні.
- Тестування згортання. Проганяємо сценарії на різних версіях системи та розмірах екрана: згортання під час буферизації, при заблокованому екрані, при вхідному дзвінку. Перевіряємо, що вікно не перекриває системні елементи.
- Перевірка на реальних пристроях. Окремо дивимося поведінку на бюджетних моделях і планшетах — там частіше проявляються розбіжності у відрисовці та споживанні ресурсів.
- Передача конфігурації. Віддаємо команді замовника налаштування вікна, опис життєвого циклу та чекліст перевірок, щоб подальші правки не ламали режим.
Кожен етап закривається результатом, який можна перевірити: збережений стан плеєра, відсутність розривів відтворення, передбачувана поведінка вікна. Такий порядок дозволяє вбудувати режим без переписування наявної логіки екранів. Опишіть задачу — повернемося з оцінкою та планом для вашого застосунку.
Чим відрізняється системний режим PiP від власного плаваючого вікна?
Системний Picture-in-Picture Android — це режим, який повністю обслуговує операційна система: застосунок лише повідомляє, що готовий згорнутися у вікно, передає набір дій і отримує колбеки про зміну стану.
Вікно живе поверх інших задач, його положення, розмір, жести перетягування та закриття контролює система, а наш застосунок продовжує працювати у фоні без втрати контексту відтворення.
Власне плаваюче вікно — це окремий шар поверх інтерфейсу, який малює сам застосунок: ми самі тримаємо координати, обробляємо перетягування, стежимо за життєвим циклом актівіті та вручну повертаємо користувача в повноекранний режим.
Різниця у трудозатратах проявляється одразу. Системний PiP — це кілька днів роботи: оголосити підтримку в маніфесті, описати дії, акуратно обробити переходи між повноекранним і згорнутим станом, щоб не скидалася позиція відтворення.
Власне вікно — це вже окрема підсистема: шар поверх усіх екранів, свої жести, конфлікти з системними панелями, ручне керування фокусом і окрема логіка згортання.
Таке вікно доводиться супроводжувати при кожному оновленні системи, бо поведінка дозволів і оверлеїв змінюється від версії до версії.
Сумісність версій — другий вододіл. Системний режим доступний починаючи з певної гілки Android і на всіх актуальних пристроях поводиться передбачувано: система сама вирішує, як розташувати вікно і як реагувати на поворот екрана.
Власне вікно формально працює всюди, але саме на цьому «всюди» і починається біль: на одних прошивках оверлей перекриває системні елементи, на інших — втрачає фокус, на третіх — вивантажується при нестачі пам'яті. Кожне відхилення — це окремий баг-репорт і окрема правка.
Поведінка на різних версіях системи теж розходиться. Системний PiP успадковує політики енергозбереження і коректно йде у фон, не ризикуючи бути вбитим. Власне вікно живе в процесі застосунку і поділяє його долю: варто системі звільнити пам'ять — вікно зникає разом із ним.
Для сценаріїв на кшталт відеодзвінка або плеєра це критично: користувач чекає, що картинка залишиться на місці.
Практичний висновок простий. Якщо задача — відео, дзвінок або навігація, системний режим закриває її меншою ціною і з меншою кількістю відмов.
Власне вікно виправдане лише там, де потрібен нестандартний інтерфейс поверх чужих застосунків — наприклад, свої елементи керування поза системними обмеженнями. Опишіть сценарій — повернемося з оцінкою та планом по кожному з підходів.
Кейс: як режим «картинка в картинці» перестав збоїти при згортанні
Замовник — сервіс доставки — скаржився не на відсутність режиму, а на його нестабільність: при згортанні застосунок то продовжував показувати відео поверх інших вікон, то повністю зупиняв відтворення.
За логами частка успішних згортань трималася на рівні 78%, а відновлення після повернення в застосунок займало до 4 секунд — користувач встигав втратити контекст і закривав сесію.
Що було до впровадження
Розбір показав три незалежні джерела відмов. Перший — активність не переводилася в потрібний режим життєвого циклу: система не отримувала коректного переходу, і вікно «картинка в картинці» або не з'являлося, або перекривало системні елементи.
Другий — стан плеєра не зберігався при згортанні, тому після повернення відтворення починалося з нуля. Третій — обробка жестів і кнопок керування конфліктувала із системним вікном, через що частина дій просто ігнорувалася.
Що змінили
Ми перевели керування вікном на явні виклики системного інтерфейсу з коректним налаштуванням пропорцій і меж, додали збереження позиції відтворення та буфера перед згортанням і відновлення з цього стану при поверненні.
Окремо синхронізували обробники керування зі станом системного вікна, щоб дії не губилися. Для контролю додали телеметрію за кожним переходом — тепер відмова видна на конкретному кроці, а не «десь при згортанні».
Що стало
| Метрика | До | Після |
|---|---|---|
| Частка успішних згортань | 78% | 99,2% |
| Час відновлення після повернення | до 4 с | менше 0,5 с |
| Звернення в підтримку щодо режиму | регулярні | поодинокі |
Клас відмов, пов'язаний із втратою стану плеєра, усунено повністю — відтворення продовжується з тієї ж секунди. Решта поодиноких випадків стосується пристроїв з агресивною економією енергії та закривається налаштуванням винятків.
Логіку переходу та відновлення ми вибудували за моделлю життєвого циклу Android, тому поведінка передбачувана на різних оболонках. Якщо у вас режим «картинка в картинці» працює через раз — опишіть задачу, повернемося з оцінкою та планом.
Сумніви перед впровадженням: сумісність, відкат і доступи
Перед увімкненням Picture-in-Picture у реліз зазвичай виникають три питання: чи не зламає режим поведінку на старих пристроях, чи можна відкотити зміну точково і що саме залишиться в команди замовника після здачі. Нижче — як ми закриваємо кожен із них на рівні коду та процесу, а не обіцянок.
Що якщо PiP конфліктує з поточними екранами? Режим вмикається атрибутом supportsPictureInPicture у маніфесті та setAutoEnterEnabled у PictureInPictureParams — тобто керується точково на рівні актівіті, а не всього застосунку.
Екрани, де PiP не потрібен (онбординг, оплата, відеодзвінки з власним оверлеєм), просто не позначаються, і їхня поведінка не змінюється.
Для пристроїв без підтримки режиму залишаємо перевірку hasSystemFeature і штатний фолбек — згорнуте відтворення зупиняється, застосунок працює як раніше.
Як відкочується зміна? PiP ізольовано в окремому модулі та feature-флазі: вимкнення флага повертає попередню поведінку без перезбирання логіки екранів.
Відкат — це revert одного коміту або зміна значення флага на стороні конфігурації, час відновлення — хвилини, а не релізний цикл.
Що входить у передачу після здачі?
На руки замовник отримує: вихідний код PiP-модуля з історією комітів; конфігурацію feature-флага та інструкцію щодо його перемикання; схему життєвого циклу актівіті в режимі PiP; опис підтримуваних версій Android і матрицю пристроїв, на яких режим перевірено; чекліст регресії за екранами; доступи до репозиторію та CI з правами на збирання й відкат.
- Вихідний код модуля з чистою історією комітів і описом змін
- Конфігурація feature-флага та порядок його увімкнення/вимкнення
- Схема життєвого циклу актівіті та переходів у PiP і назад
- Матриця сумісності: версії Android і протестовані пристрої
- Чекліст регресії за задіяними екранами
- Доступи до репозиторію, CI та права на відкат релізу
- Інструкція з передачі підтримки внутрішній команді
Опишіть вашу задачу — повернемося з оцінкою обсягу, планом впровадження та списком того, що знадобиться з вашого боку для доступів.







