-
-
Notifications
You must be signed in to change notification settings - Fork 484
First, it is a good idea to search through past GitHub issues to ensure that you are not posting a duplicate issue.
Second, it is a good idea to search through this FAQ, since some errors are not necessarily bugs, and can be avoided.
Finally, click on the Issues page and click the button to create a new bug report. Follow the instructions carefully and supply all requested information, leaving the section headings intact, but filling in the body of each section. Please do not delete or skip these sections as they are necessary in order to investigate a bug.
Make sure you have done the following:
- Your background task entrypoint must be a top-level function, i.e. not inside a class.
- If on Android, you must follow the Android setup instructions in the README file.
- The notification on Android won't appear until the first time you call
AudioServiceBackground.setState(playing: true, ...)
.
You must be connected before you can call AudioService.start
and your background audio task must be started before you can send any messages to it (e.g. play
, skipToNext
, customAction
, etc.).
AudioService.start
is an asynchronous method and it takes time to start. The background audio task is not started until this method has completed. If you intend to send messages to the background audio task immediately after starting, you will therefore need to await the call to start
. e.g.:
await AudioService.start(...);
await AudioService.updateQueue(...);
On Android, such button clicks are handled by a broadcast receiver which must be declared in your AndroidManifest.xml
file:
<receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
If you configured your project prior to version 0.10.0, your old broadcast receiver configuration will no longer work and must be updated to the one above.
There are a number of possible reasons for this error:
- You have not yet updated your project to the Flutter 1.12 project structure.
- You have just recently added a new plugin to your app. You may need to shut down the VM and clear the cache.
- Another plugin in your app is preventing normal plugin registration (unfortunately, one misbehaving plugin causes all other plugins in the app to generate the same error, so tracking down which one is causing it is a process of elimination.)
This can result from a conflict with the flutter_tts
plugin, which is also used in the example. (See the open issue #202)
It can also result from your AVAudioSession settings. Use the audio_session package to configure appropriate AVAudioSession settings for your app. E.g.:
audioSession.configure(AudioSessionConfiguration.speech());
// or...
audioSession.configure(AudioSessionConfiguration.music());
// or a custom configuration:
audioSession.configure(...);
(Note: The default preset AudioSessionConfiguration.music()
in audio_session 0.0.4 and earlier had some options set in it that were not appropriate for showing a notification. This is fixed in version 0.0.5 and later.)
Certain Plugins like android_alarm_manager let your app "wake up" from the background and execute Dart code.
As of audio_service
0.9.0, you can connect to AudioService
from such background Dart code, and then send messages to your already running background audio task. e.g.:
await AudioService.connect(); // Note: the "await" is necessary!
AudioService.play();
It is currently not possible to start a new background audio task from the background on Android, only to communicate with one that is already running. That is, you must still start the background audio task from the main isolate of your Flutter view.
audioplayers
has both worked and not worked at different times in the past. If it currently does not work, you can contact the author of that plugin and make a feature request.
Relevant information: For an audio player plugin to work with audio_service
there needs to be clearly delineated roles between the two plugins such that only audio_service
controls the various APIs involved in:
- Enabling background execution
- Displaying notifications, lock screen controls, Control Center info, etc.
- Configuring the audio session
- Handling remote controls from headsets, smart watches, car stereos, etc.
- Handling audio focus events (e.g. if another app takes over audio focus temporarily)
If the other plugin does any of these things, the two plugins will overlap in responsibility and things will probably not work. In other words, you will need an audio player plugin that just plays audio, and doesn't take on any of these other responsibilities listed above.
If at present audioplayers
doesn't work for you, you can alternatively try my plugin just_audio
which was designed with this delineation in mind.
Yes. Some audio players may provide an option to do this for you. If not, you can use the audio_session library to listen to audio interruption events and play/pause as appropriate:
session.interruptionEventStream.listen((event) {
// play/pause/duck audio depending on the event
});
See the audio_session package for further information.
There is an issue on Android, but there is a workaround. Android requires an active media session to route media button events to, and actual audio needs to be played to activate a media session (text-to-speech does not qualify). One trick is to play a short segment of silent audio to activate the session. You can use AudioServiceBackground.androidForceEnableMediaButtons()
to achieve this.
Arbitrary parameters can be passed to AudioService.start
via the params
parameter. You can use AudioService.customAction(...)
to pass an arbitrary message to the background task after it has started. To ensure it has started, you must await the result of AudioService.start
:
if (await AudioService.start(...)) {
AudioService.customAction(...);
}
As per the README file:
Keep in mind that your Flutter UI and background task run in separate isolates and do not share memory. The only way they communicate is via message passing. Your Flutter UI will only use the AudioService API to communicate with the background task, while your background task will only use the AudioServiceBackground API to interact with the clients, which include the Flutter UI.
There are a number of predefined messages you can pass from the UI to the background isolate via the AudioService
API such as play
, pause
, seekTo
, skipToNext
, etc. If you want to send another message that is not one of the predefined messages in this API, you can use customAction
(See "How do I send a custom action?").
There are also a number of predefined messages you can pass from the background to the UI isolate via the AudioServiceBackground
API such as setState
, setMediaItem
and setQueue
. If you want to send another message that is not one of the predefined messages in this API, you can use sendCustomEvent
(See "How do I send a custom event?").
If you want to send a message from the UI to your background audio task but it is not one of the predefined messages defined in AudioService
, you can send the message as a custom action. E.g.
// In your UI
await AudioService.customAction("setVolume", 0.5);
And then in your background audio task, implement the onCustomAction
method:
@override
Future onCustomAction(String name, dynamic arguments) async {
if (name == 'setVolume') {
_audioPlayer.setVolume(arguments);
}
}
Note that the parameter of a custom action can be any data structure that can be encoded by the standard message codec. If you return a value from onCustomAction
, it will also be returned back to the caller of customAction
.
If you want to send an event from your background audio task to the Flutter UI but it is not one of the predefined messages defined in AudioServiceBackground
, you can send the message as a custom event. E.g.
// In your background task
AudioServiceBackground.sendCustomEvent("itHappened");
And then in your Flutter UI:
AudioService.customEventStream.listen((event) {
print("Received event: $event");
}
The event parameter can contain any data permitted by Dart's SendPort/ReceivePort API. Please consult the relevant documentation for further information. This means that you can create your own application-specific event class encapsulating event data relevant to your app.
You cannot update fields in a MediaItem
object since it is immutable, although in the same style as ThemeData.copyWith
, you can use MediaItem.copyWith
to create a copy with some fields modified. e.g.
modifiedMediaItem = mediaItem.copyWith(duration: duration);
If you are updating the current media item in your background audio task, remember to broadcast this change to all clients:
AudioServiceBackground.setMediaItem(modifiedMediaItem);
If you are updating a media item in the queue in your background audio task, remember to broadcast your modified queue to all clients:
myQueue[pos] = modifiedMediaItem;
AudioServiceBackground.setQueue(myQueue);
For Android, you can override the onTaskRemoved
callback to control what happens when the user swipes away the task in the task manager. By default, the operating system will just clear your app's activity (UI) from memory leaving your service to run in the background with the audio. But you can override this callback as follows to cause your service to also be shut down:
void onTaskRemoved() {
onStop();
}
If you use the androidStopForegroundOnPause
option, when your audio is paused, the operating system moves your service to a lower priority level where it can be destroyed at any time to reclaim memory. If the user swipes away your task under these conditions, the operating system will destroy your service, and you may override this method to do any cleanup. For example:
void onTaskRemoved() {
if (!AudioServiceBackground.state.playing) {
onStop();
}
}
Sometimes you may want to try out the latest code on git master, e.g. to try out a bug fix or new feature that hasn't been released yet. To do so, you can temporarily change your pubspec.yaml
file to:
audio_service:
git:
url: https://github.com/ryanheise/audio_service.git
Note that git master is not stable and should not be used in production.