-
-
Notifications
You must be signed in to change notification settings - Fork 481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Android] Kill the AudioServiceBackground Isolate when the app is destroyed #159
Comments
There are various ways to detect when an activity is destroyed (e.g. pressing the back button from the root route) and when a task is swiped up from the task manager using platform-side APIs. I'm assuming this is what you mean by closing the app? Since the plugin was designed to continue running in the background, the current behaviour is as intended, as backing out of the activity is a common action when the user wants to multitask while continuing to listen to the audio in your app, but it has the side effect of destroying the activity. Here, even though the activity is destroyed, we want the audio to continue, and this is how mp3 players tend to work. When swiping up a task in the task manager, it is also the usual behaviour in mp3 players to just destroy the activity but still have the music playing in the background. However, in this case I can conceive of it being useful to have an option for this behaviour. So these two different ways of "closing the app" might need to have different options. For crashing, I don't know if there is a reliable way to detect that, and you might want to address it by putting more try/catch blocks in your app. But otherwise, this is a feature I could consider adding. |
Yes, you're correct: crashing should not be listed here in the requirements and should be handled properly in the app logic. Back to the feature request, a few comments: I would add as another argument for the Another point is that some (if not most) of the users are currently familiar with non-persistent audio players when killing the app using the Task Manager. Think about how Spotify and YouTube (premium) handle this situation. I'd agree that it is not necessarily a requirement for every project, but it is a pretty common scenario. |
As a user, I would uninstall and downvote any app that keeps playing after I closed it. |
@vinicius-animo sounds reasonable. @volgin To my knowledge, |
@ryanheise Apple Music, Spotify and Amazon Prime Music stop playing immediately after an app is closed. I am not familiar with any music player that continues to play after it was closed. I am not sure if I understood you correctly, but this is the most common, expected behavior. |
@volgin iOS and Android do have different historical behaviours here, so what you describe as the most common, expected behaviour might be true on iOS, but historically it has not been true on Android where even today, Google's official Android music player "Google Play Music" continues to play music in the background after you destroy the activity or swiping away the task. So swiping away the task is really just a way to free up memory taken up by the activity. If you wanted to actually stop the music, you should instead press the "stop" button or the x button in the notification. This is common behaviour and dates back to the oldest versions of Android where the mechanism Android provided to allow apps to play music in the background was to create a separate background component called a "service" which was designed to continue running even after the UI component (the "activity") was destroyed. It was even advised in the developer documentation that this is how you should build a music player app, and the task APIs that would make it possible to emulate iOS behaviour weren't available until much later. I would say this behaviour is still prevalent on Android today, particularly in music and podcast players where the background service just plays audio and can be fully detached from the user interface. But Android is rather lax on standards and doesn't really enforce U/X design guidelines in the same way Apple does, so there is also a bit of variety on Android, and that is why an option was being considered above. Such an option would give developers the freedom to make their own U/X choices, and even to be able to emulate the iOS behaviour on Android if they wanted to, or to go with the historical behaviours for the respective platforms. I believe the plugin's current behaviour does follow the expected behaviour on iOS, although please let me know if that is not the case. For the Android side, the option to emulate the iOS behaviour is actively being considered in this issue. @vinicius-animo Regarding the proposed option(s) for Android, just to clarify what you mean by closing the app, this could mean two things:
I am inclined to only add an option for (2) for now because I don't see a compelling use case for an option for (1). On Android, if you back out of the main activity and the activity is destroyed, but then the service is also destroyed, I think the user would be quite annoyed if it were a music player. For (2), I can see both ways. If the user swipes away Google Play Music in the task manager, it frees up memory taken by the Google Play Music app user interface, but to stop the music you would just press the stop button. If you swipe away Spotify in the task manager, it frees up the UI memory but also stops the music. At least this should be provided as an option to the developer. But do you have a compelling use case for making an option for (1) or would you be satisfied to just have an option for (2)? |
@ryanheise Thank you for pointing out Google Play Music - I never tried it before. I think that app developers already have control over the back button behavior, and can close/stop the background service in their app logic, if they choose so. |
Any updates on this issue? To me, I think what @ryanheise mentioned as the second option would satisfy my needs as a developer. I also tried to implement it with the existing API: I think we can add the |
I might be able to do a PR to implement the feature. Is this still a wanted feature? |
That's correct,
Definitely, I think it makes sense to give users a choice for the Android behaviour. Pull requests are certainly welcome since I have quite a few high priority issues to work on first before I would get around to this one. Since this is an Android-specific option, I would name the parameter something like @Override
public void onTaskRemoved(Intent rootIntent) {
MediaControllerCompat controller = mediaSession.getController();
if (androidStopForegroundOnPause && controller.getPlaybackState().getState() == PlaybackStateCompat.STATE_PAUSED) {
stopSelf();
}
super.onTaskRemoved(rootIntent);
} Basically that existing code is so that if the user wants it, the service will be shut down when swiping it away "while" in pause mode, which is another behaviour that some users want. However, simply calling listener.onStop(); This will send a message to the isolate to gracefully stop. |
@ryanheise In that case I will fix the old case as well. Does the following code make sense to you? @Override
public void onTaskRemoved(Intent rootIntent) {
MediaControllerCompat controller = mediaSession.getController();
if (androidStopOnRemoveTask || (androidStopForegroundOnPause && controller.getPlaybackState().getState() == PlaybackStateCompat.STATE_PAUSED)) {
listener.onStop();
}
super.onTaskRemoved(rootIntent);
} EDIT: |
Looks good to me. Yes, sorry I should have clarified in my previous comment that after |
PR is ready to merge: #165 |
Thanks, @Camerash ! I've merged your PR. @vinicius-animo and @volgin you can test this on the master branch, and turn on the feature by passing in |
Works perfectly! Thanks @Camerash and @ryanheise. Btw, I had to replace |
Update on iOS: works perfectly. |
Thanks @vinicius-animo , glad to hear it! |
Good life, fellows!!! |
Just turn on the feature by passing in |
This was removed? I'm using just_audio: ^0.2.2 and audio_service: ^0.11.0 and the only options i have in AudioService.start() with the android prefix are:
I tried to use the |
Well, to anyone wondering, i just got it working:
Its not needed anymore to use |
When I swipe away the task in the task manager on Android, it destroys the activity and the service continues to run independently: @OverRide But onstop() only stops the service, not the audio player. Help me fix!!! |
From the documentation for
|
But Audio Player is in Flutter UI, I stop audio: audioPlayer.stop() not working: because flutter UI is destroys? |
So you're not using this plugin in the way it was intended? From the README:
Please read the documentation in both instances to make sure you are using the plugin correctly. |
it worked |
Seems not working as espected since 0.18 and Android 11. audioHandler = await AudioService.init(
config: const AudioServiceConfig(
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
androidNotificationChannelName: "Lecteur de musique",
androidNotificationChannelDescription: "Cette notification doit être activée pour afficher le player dans la zone de notification",
),
builder: () => AudioServiceTask(),
); class AudioServiceTask extends BaseAudioHandler {
AudioPlayer _player = AudioPlayer();
AudioServiceTask() {
_player.playbackEventStream.map(_transformEvent).pipe(playbackState);
}
@override
Future<void> onTaskRemoved() async {
await stop();
super.onTaskRemoved();
}
PlaybackState _transformEvent(PlaybackEvent event) {
return PlaybackState(
controls: [
if (_player.playing) MediaControl.pause else MediaControl.play,
MediaControl.stop,
],
systemActions: const {},
androidCompactActionIndices: const [0, 1],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[_player.processingState]!,
playing: _player.playing,
updatePosition: _player.position,
bufferedPosition: _player.bufferedPosition,
speed: _player.speed,
queueIndex: event.currentIndex,
);
}
@override
Future<void> playMediaItem(MediaItem mediaItem) async {
await _player.pause();
await _player.setUrl(mediaItem.id);
await _player.play();
this.mediaItem.add(mediaItem);
}
@override
Future<void> updateMediaItem(MediaItem mediaItem) async {
this.mediaItem.add(mediaItem);
}
@override
Future<void> pause() => _player.pause();
@override
Future<void> play() async {
await _player.seek(null);
await _player.play();
}
@override
Future<void> stop() async {
await _player.stop();
}
@override
Future<void> click([MediaButton button = MediaButton.media]) async {
switch (button) {
case MediaButton.media:
if (playbackState.value.playing == true) {
await pause();
} else {
await play();
}
break;
case MediaButton.next:
await skipToNext();
break;
case MediaButton.previous:
await skipToPrevious();
break;
}
}
} If the music is playing and if we stop it throug the notification button, the notification didn't disappear but go in lower priority mode and stay visibile. Please, try many time (3/4) it seems to be a little random. |
I'm not even sure from what you're saying whether this is really a bug, but if it is a bug, please don't place a comment somewhere in the backlog of closed issues, please open a new issue and make sure you provide all of the information requested in the template so that I can investigate it. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service. |
Is your feature request related to a problem? Please describe.
On Android, when the app is closed (or crashes) while the audio is playing, the user may expect that the audio stops immediately.
In the latest version of the plugin, the
AudioServiceBackground
keeps running and the user needs to tap the stop button in the notification bar in order to make it happen.Describe the solution you'd like
It would be nice if the
start
method had a flag such aspersist: false
to control theIsolate
behavior in those conditions (app killed, closed or crashed).Does Android offer this kind of control over the app states?
The text was updated successfully, but these errors were encountered: