我正在构建运行一组音频的颤动音乐应用程序。我的应用程序的想法是在播放一个音频时我会收听它,当它完成时我将通过向 api 发送请求并获取歌曲 url 并再次播放来移动到下一个音频。
我为我的应用程序使用了两个颤振包just_audio来播放音频,并使用audio_service来处理后台播放和通知。
一切正常,直到我将移动屏幕关闭音频工作正常,完成后停止并且没有进入下一个时间。
和我的音频处理程序代码
Future<AudioHandler> initAudioService(player) async {
return await AudioService.init(
builder: () => MyAudioHandler(player),
config: AudioServiceConfig(
androidNotificationChannelId: 'com.mycompany.myapp.audio',
androidNotificationChannelName: 'Audio Service Demo',
androidNotificationOngoing: true,
androidStopForegroundOnPause: false,
androidNotificationIcon: 'mipmap/launcher_icon'),
);
}
class MyAudioHandler extends BaseAudioHandler {
final PlayerProvider playerProvider;
MyAudioHandler(this.playerProvider) {
_notifyAudioHandlerAboutPlaybackEvents();
_listenForDurationChanges();
_listenForCurrentSongIndexChanges();
_listenForSequenceStateChanges();
}
void _notifyAudioHandlerAboutPlaybackEvents() {
playerProvider.player.playbackEventStream.listen((PlaybackEvent event) {
final playing = playerProvider.player.playing;
playbackState.add(playbackState.value.copyWith(
controls: [
MediaControl.skipToPrevious,
if (playing) MediaControl.pause else MediaControl.play,
MediaControl.stop,
MediaControl.skipToNext,
],
systemActions: const {
MediaAction.seek,
},
androidCompactActionIndices: const [0, 1, 3],
processingState: const {
ProcessingState.idle: AudioProcessingState.idle,
ProcessingState.loading: AudioProcessingState.loading,
ProcessingState.buffering: AudioProcessingState.buffering,
ProcessingState.ready: AudioProcessingState.ready,
ProcessingState.completed: AudioProcessingState.completed,
}[playerProvider.player.processingState],
repeatMode: const {
LoopMode.off: AudioServiceRepeatMode.none,
LoopMode.one: AudioServiceRepeatMode.one,
LoopMode.all: AudioServiceRepeatMode.all,
}[playerProvider.player.loopMode],
shuffleMode: (playerProvider.player.shuffleModeEnabled)
? AudioServiceShuffleMode.all
: AudioServiceShuffleMode.none,
playing: playing,
updatePosition: playerProvider.player.position,
bufferedPosition: playerProvider.player.bufferedPosition,
speed: playerProvider.player.speed,
queueIndex: event.currentIndex,
));
});
}
void _listenForDurationChanges() {
playerProvider.player.durationStream.listen((duration) {
var index = playerProvider.player.currentIndex;
final newQueue = queue.value;
if (index == null || newQueue.isEmpty) return;
if (playerProvider.player.shuffleModeEnabled) {
index = playerProvider.player.shuffleIndices[index];
}
final oldMediaItem = newQueue[index];
final newMediaItem = oldMediaItem.copyWith(duration: duration);
newQueue[index] = newMediaItem;
queue.add(newQueue);
mediaItem.add(newMediaItem);
});
}
void listenForAudioFinished() {
playerProvider.player.positionStream.listen((event) {
Duration songDuration = playerProvider.player.duration ?? Duration.zero;
StreamSubscription _subscription;
bool done = false;
// true if current player position >= song duration
if (!(event.inSeconds < songDuration.inSeconds)) {
done = true;
} else {
done = false;
}
if (done) {
if (playerProvider.currentTrack != null &&
playerProvider.nextTrack == false) {
playerProvider.next(action: false);
} else {
playerProvider.nextTrack = false;
}
_subscription?.cancel();
}
});
}
void _listenForCurrentSongIndexChanges() {
playerProvider.player.currentIndexStream.listen((index) {
final playlist = queue.value;
if (index == null || playlist.isEmpty) return;
if (playerProvider.player.shuffleModeEnabled) {
index = playerProvider.player.shuffleIndices[index];
}
mediaItem.add(playlist[index]);
});
}
void _listenForSequenceStateChanges() {
playerProvider.player.sequenceStateStream
.listen((SequenceState sequenceState) {
final sequence = sequenceState?.effectiveSequence;
if (sequence == null || sequence.isEmpty) return;
final items = sequence.map((source) => source.tag as MediaItem);
queue.add(items.toList());
});
}
@override
Future<void> addQueueItems(List<MediaItem> mediaItems) async {
// manage Just Audio
mediaItems.map(_createAudioSource);
// notify system
final newQueue = queue.value..addAll(mediaItems);
queue.add(newQueue);
}
@override
Future<void> addQueueItem(MediaItem mediaItem) async {
final newQueue = queue.value..add(mediaItem);
queue.add(newQueue);
}
UriAudioSource _createAudioSource(MediaItem mediaItem) {
return AudioSource.uri(
Uri.parse(mediaItem.extras['url']),
tag: mediaItem,
);
}
@override
Future<void> removeQueueItemAt(int index) async {
// manage Just Audio
//_playlist.removeAt(index);
// notify system
final newQueue = queue.value..removeAt(index);
queue.add(newQueue);
}
@override
Future<void> play() => playerProvider.player.play();
@override
Future<void> pause() => playerProvider.player.pause();
@override
Future<void> seek(Duration position) async {
playerProvider.seek(position);
}
@override
Future<void> skipToNext() => playerProvider.next();
@override
Future<void> skipToPrevious() => playerProvider.prev();
@override
Future customAction(String name, [Map<String, dynamic> extras]) async {
if (name == 'dispose') {
await playerProvider.player.dispose();
super.stop();
}
}
@override
Future<void> stop() async {
await playerProvider.player.stop();
return super.stop();
}
}
在这里我播放任何音频的功能
final postData = {
'user_id': userString == null ? 0 : user['id'],
'part_id': _currentTrack.id,
'session_id': userString == null ? null : user['sessionId']
};
final Response response = await _httpService.postRequest(url, postData);
if (response.statusCode == 200 && response.data['status']) {
final parsedData = response.data['listening_details'];
if (parsedData['user_session'] == 'yes') {
_session = true;
if (parsedData['allow_hearing_part'] == 'yes') {
_currentTrack.path = parsedData['path'];
_playlist[index].path = parsedData['path'];
}
} else {
_session = false;
_currentTrack.path = null;
_playlist[index].path = null;
}
}
if (_currentTrack.path != null) {
var tag = MediaItem(
// Specify a unique ID for each media item:
id: _currentTrack.id.toString(),
// Metadata to display in the notification:
album: Get.Get.locale.languageCode == 'en'
? _currentTrack.book['title_en']
: _currentTrack.book['title'],
title: Get.Get.locale.languageCode == 'en'
? _currentTrack.titleEn
: _currentTrack.title,
artUri:
Uri.parse('${_currentTrack.image}'),
);
await player.setAudioSource(AudioSource.uri(
Uri.parse(_currentTrack.path),
tag: tag,
));
_audioHandler.play();
_audioHandler.playMediaItem(tag);
_audioHandler.listenForAudioFinished();
onAudioDone();
}
_isLoading = false;
notifyListeners();