应用程序生命周期改变全局停止所有正在运行的音频和视频流?
商业级 Flutter 应用中因为场景的多样性,很难避免其中包含多个音频和视频播放器(例如 video_player、just_audio、audioplayers、audio_session)。当应用生命周期发生变化时(例如,应用转入后台或分离),需要停止或暂停所有正在运行的媒体流,而无需手动管理每个音频或视频实例。
解决办法 1:
在每个播放组件的 didChangeAppLifecycleState() 中暂停每个实力。使用生命周期钩子进行手动管理,但是很麻烦。如果要二次调整停止流媒体时机,每个地方都需要修改,维护性差。
解决办法 2:
通过订阅模式持有方法指针,全局统一管理,完美解决问题。
二、订阅模式管理音视频流管理实现
1、实现音视频管理单例类
import 'dart:io';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/foundation.dart';
/// AudioSession 音视频管理类
class AudioSessionManager {
static final AudioSessionManager _instance = AudioSessionManager._();
AudioSessionManager._();
factory AudioSessionManager() => _instance;
/// 监听列表(实现音频统一管理)
final List<AudioSessionSoundPlayerModel> _listeners = [];
// 添加监听
void addListener(AudioSessionSoundPlayerModel cb) {
if (_listeners.contains(cb)) {
return;
}
_listeners.add(cb);
}
// 移除监听
void removeListener(AudioSessionSoundPlayerModel cb) {
_listeners.remove(cb);
}
// 通知所有监听器
Future<void> notifyListeners(Future<void> Function(AudioSessionSoundPlayerModel e) action) async {
for (var ltr in _listeners) {
await action(ltr);
}
}
void clearListeners() {
_listeners.clear();
}
}
class AudioSessionSoundPlayerModel {
AudioSessionSoundPlayerModel({
this.data,
this.onPlay,
this.onStop,
});
/// 唯一值
Map<String, dynamic>? data;
/// 播放
Future<void> Function()? onPlay;
/// 停止播放
Future<void> Function()? onStop;
AudioSessionSoundPlayerModel.fromJson(Map<String, dynamic>? json) {
if (json == null) {
return;
}
data = json['data'] ?? {};
onPlay = json['onPlay'];
onStop = json['onStop'];
}
Map<String, dynamic> toJson() {
final data = Map<String, dynamic>();
data['data'] = data;
data['onPlay'] = onPlay.hashCode;
data['onStop'] = onStop.hashCode;
return data;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
final isEqual = other is AudioSessionSoundPlayerModel &&
runtimeType == other.runtimeType &&
mapEquals(toJson(), other.toJson());
return isEqual;
}
@override
int get hashCode => data.hashCode ^ onPlay.hashCode ^ onStop.hashCode;
}
2、注册到 AudioSessionManager 示例:
class SoundPlayerAndRecorderState extends State<SoundPlayerAndRecorder>
with AutomaticKeepAliveClientMixin, SafeSetStateMixin {
/// current audio model
AudioSessionSoundPlayerModel get audioSessionSoundPlayerModel => AudioSessionSoundPlayerModel(
data: widget.model?.toJson(),
onPlay: onPlay,
onStop: onStop,
);
...
Future<void> onPlay() async {
await AudioSessionManager().notifyListeners((e) async {
await e.onStop?.call();
});
AudioSessionManager().addListener(audioSessionSoundPlayerModel);
//your audio play codes
}
Future<void> onStop() async {
//your audio stop play codes
AudioSessionManager().removeListener(audioSessionSoundPlayerModel);
}
...
}
3、监听app生命周期回调方法中
switch (state) {
case AppLifecycleState.inactive:
{
AudioSessionManager().notifyListeners((e) async {
await e.onStop?.call();// 停止所有音视频播放
});
}
break;
default:
debugPrint("$state");
}
总结
在项目开发中,因为场景众多,很难避免各种疑难问题。这时候就需要我们发散思维,结合现有知识跳出窠臼,提出创造性的解决办法,维护时会省时省力。方法1虽然也能解决问题,但维护性较差,方法2才是兼顾维护性和扩展性的最佳方法。