Skip to content

Keep the Flutter engine alive when the service is destroyed mid-playback - #1161

Open
leonardo-oliveir-a wants to merge 1 commit into
ryanheise:minorfrom
leonardo-oliveir-a:fix/keep-engine-alive-while-playing
Open

Keep the Flutter engine alive when the service is destroyed mid-playback#1161
leonardo-oliveir-a wants to merge 1 commit into
ryanheise:minorfrom
leonardo-oliveir-a:fix/keep-engine-alive-while-playing

Conversation

@leonardo-oliveir-a

Copy link
Copy Markdown

The problem

On some devices, enabling battery saver makes App Standby stop the AudioService after ~9 minutes without user interaction, even while audio is actively playing. Captured on a OnePlus 6T (Android 11) with a production meditation app:

10:07:26 W/ActivityManager: Stopping service due to app idle: u0a4526 -9m12s647ms br.com.lojong/com.ryanheise.audioservice.AudioService

When that happens, AudioService.onDestroylistener.onDestroy()disposeFlutterEngine() destroys the shared Flutter engine unconditionally. But the audio is rendered by plugins living in that engine (audioplayers/just_audio/etc.), not by the service itself — so a service stop that would otherwise only remove the media notification ends up killing the ongoing playback. The process survives (verified: same pid before/after), so the user later reopens the app to a cold start from the splash screen with their session gone.

The change

In the service-destroyed path, only dispose the engine when playback is not active (AudioService.instance.isPlaying() still reflects the state at the moment of the kill, since the system stop never goes through the handler). One call site, behavior unchanged for the normal flows where playback was already stopped/paused when the service goes away.

Trade-off: when the engine is kept alive, audio continues without the media notification, and the engine lives in a now-cached process until the OS reclaims it — which seems strictly better than silently killing the user's playback.

How to reproduce

  1. Start playback, lock the screen, enable battery saver (or force it: adb shell am set-inactive <pkg> true on a non-allowlisted app).
  2. Wait for the system to stop the service ("Stopping service due to app idle").
  3. Before this change: audio stops; next app launch cold-starts (engine was destroyed despite the process being alive). After: audio keeps playing.

When the OS stops the AudioService while audio is still playing — e.g.
battery saver's App Standby restriction logs "Stopping service due to app
idle" after ~9 minutes without user interaction on some devices — the
plugin's onDestroy listener unconditionally destroyed the shared Flutter
engine. The audio itself is rendered by plugins living in that engine, not
by the service, so this needlessly killed the ongoing playback; the process
survives, and the next app launch cold-starts from the splash screen.

Only dispose the engine when playback is not active. In the normal flows
(playback stopped or paused before the service goes away) the behavior is
unchanged. When the engine is kept alive, audio continues without the media
notification; the engine is reclaimed with the (cached) process by the OS.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant