我正在使用flutter audio_service(https://pub.dev/packages/audio_service)和just_audio(https://pub.dev/packages/just_audio)以在前台和后台播放音频文件。
我将BackgroundAudioTask划分为子类,添加了一个AudioPlayer实例,并覆盖了所需的方法。例如,我更新了重复模式:
@override
Future<void> onSetRepeatMode(AudioServiceRepeatMode repeatMode) async {
super.onSetRepeatMode(repeatMode);
switch (repeatMode)
{
case AudioServiceRepeatMode.all:
_audioPlayer.setLoopMode(LoopMode.all);
break;
case AudioServiceRepeatMode.none:
_audioPlayer.setLoopMode(LoopMode.off);
break;
case AudioServiceRepeatMode.one:
_audioPlayer.setLoopMode(LoopMode.one);
break;
case AudioServiceRepeatMode.group:
_audioPlayer.setLoopMode(LoopMode.all);
break;
}
}
@override
Future<void> onPlayFromMediaId(String mediaId) async {
await _audioPlayer.stop();
// Get queue index by mediaId.
_queueIndex = _queue.indexWhere((test) => test.id == mediaId);
// Set url source to _audioPlayer.
downloadAndPlay(_mediaItem);
}
我正试图添加一个播放列表,并有一个音频文件循环,但似乎缺少一些东西,在播放曲目#1后,播放器试图开始播放相同的#1曲目并再次播放。这是我的代码(下载和播放的一部分(:
var list=List<AudioSource>();
for (int t=0;t<_queue.length;t++)
{
var mi=_queue[t];
var url = mi.extras['source'];
list.add(AudioSource.uri(Uri.parse(url)));
}
D("Loading list with ${list.length} items");
D("Seeking to index : $_queueIndex");
await _audioPlayer.load(ConcatenatingAudioSource(children: list),
initialIndex: _queueIndex, initialPosition: Duration.zero);
AudioService.updateQueue(_queue);
AudioService.setRepeatMode(AudioServiceRepeatMode.all);
_audioPlayer.play();
我添加这个是为了检查ProcessingState.com是否已完成发射,这意味着它到达了轨道的尽头,但它没有发射:
playerEventSubscription = _audioPlayer.playbackEventStream.listen((event) {
D("audioPlayerTask: playbackEventStream : ${event.processingState}");
switch (event.processingState) {
case ProcessingState.ready:
_setState(state: AudioProcessingState.ready);
break;
case ProcessingState.buffering:
_setState(state: AudioProcessingState.buffering);
break;
case ProcessingState.completed:
_handlePlaybackCompleted();
break;
default:
break;
}
});
在BackgroundAudioTask
子类中,应该重写onSetRepeatMode
方法。
import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';
class AudioPlayerTask extends BackgroundAudioTask {
AudioPlayer _player = new AudioPlayer();
...
@override
Future<void> onSetRepeatMode(AudioServiceRepeatMode repeatMode) async {
switch (repeatMode) {
case AudioServiceRepeatMode.none:
await _player.setLoopMode(LoopMode.off);
break;
case AudioServiceRepeatMode.one:
await _player.setLoopMode(LoopMode.one);
break;
case AudioServiceRepeatMode.all:
case AudioServiceRepeatMode.group:
await _player.setLoopMode(LoopMode.all);
break;
}
}
}
这个特殊的实现使用just_audio作为音频播放器。请注意,AudioPlayerTask
是just_audioAudioPlayer
出现的唯一类。它不应该出现在应用程序的任何其他类中。在其他任何地方,您都会使用audio_service的AudioService
类。
例如,当用户按下按钮时,您可以运行以下代码行:
AudioService.setRepeatMode(AudioServiceRepeatMode.none);
这将通知系统,并让BackgroundAudioTask
运行onSetRepeatMode
,以便内部AudioPlayer
类可以执行其任务。
您从未调用过_audioPlayer.setLoopMode()
,因此它不会循环。
您也在使用audio_service,但您的音频代码在内部显示为半,在audio_service外部显示为半。具体来说,_audioPlayer
的实例位于audio_service之外,而setRepeatMode
的实现位于audio_service内,并且无法访问位于audio_services之外的_audioPlayer
实例。
将所有音频逻辑封装在audio_service中,以便setRepeatMode
实现能够引用_audioPlayer
实例并在其上调用_audioPlayer.setLoopMode()
。这实际上就是我们封装(将方法与它们需要操作的数据捆绑在一起(的原因,但在这种情况下,我们在audio_service,即audio_ service之外的一切";胶囊";随时可能被摧毁。因此,如果你的应用程序转换到后台,你的UI被破坏,你不希望你的音频逻辑被破坏一半。如果您将其全部封装在audio_service中,它将能够在UI的破坏中幸存下来,并且由于它是自包含的,它将拥有在后台继续播放音频所需的一切。
此外,您的应用程序可能也想在前台播放音频,这并不意味着您需要打破这种封装。这个封装的音频代码完全能够在有UI和没有UI的情况下运行,因此您实际上不需要更改编程风格来支持前台情况。
我做这件事的方法是简单地放入onStart方法:
await _player.setLoopMode(LoopMode.all);
当然,这是用户无法更改的。