MAUI中如何利用IDisposable在AVAudioPlayer播放完成后释放资源
MAUI中AVAudioPlayer播放完成后自动释放资源的解决方案
问题根源
- 使用
using语句时,代码块执行完毕会立即调用Dispose,而AVAudioPlayer.Play()是异步执行的,此时音频还未开始播放就被销毁,导致无声音输出。 - 不使用
using时,实例由GC管理,但如果AVAudioPlayer的回调事件持有实例引用,GC无法及时回收,析构函数迟迟不执行,资源无法释放。
方案1:通过播放完成回调自动触发释放
修改VoicePlayer类,订阅AVAudioPlayer的FinishedPlaying事件,在播放完成时主动调用Dispose,实现资源自动释放。
代码实现
public class VoicePlayer : IVoicePlayer, IDisposable { private AVAudioPlayer? _audioPlayer; private bool _disposed = false; public void Initialize(string audioFilePath) { var audioUrl = NSUrl.FromFilename(audioFilePath); _audioPlayer = AVAudioPlayer.FromUrl(audioUrl); // 订阅播放完成事件 _audioPlayer.FinishedPlaying += AudioPlaybackFinished; _audioPlayer.PrepareToPlay(); } public void Play() { _audioPlayer?.Play(); } private void AudioPlaybackFinished(object? sender, AVStatusEventArgs e) { // 播放完成后立即释放资源 Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 清理托管资源:移除事件订阅、销毁AVAudioPlayer if (_audioPlayer != null) { _audioPlayer.FinishedPlaying -= AudioPlaybackFinished; _audioPlayer.Stop(); _audioPlayer.Dispose(); _audioPlayer = null; } } // 清理非托管资源(如有) _disposed = true; } ~VoicePlayer() { Dispose(false); } }
使用方式
无需using,直接创建实例调用播放方法,播放完成后自动释放:
var voicePlayer = new VoicePlayer(); voicePlayer.Initialize("Resources/Audio/notification.mp3"); voicePlayer.Play();
方案2:异步播放+using语句(可控性更强)
封装异步播放方法,等待播放完成后再执行using的释放逻辑,既保证资源自动释放,又能同步控制播放流程。
代码实现
public class VoicePlayer : IVoicePlayer, IDisposable { private AVAudioPlayer? _audioPlayer; private bool _disposed = false; private TaskCompletionSource<bool>? _playCompletionSource; public void Initialize(string audioFilePath) { var audioUrl = NSUrl.FromFilename(audioFilePath); _audioPlayer = AVAudioPlayer.FromUrl(audioUrl); _audioPlayer.FinishedPlaying += AudioPlaybackFinished; _audioPlayer.PrepareToPlay(); } public async Task PlayAsync() { _playCompletionSource = new TaskCompletionSource<bool>(); _audioPlayer?.Play(); // 等待播放完成 await _playCompletionSource.Task; } private void AudioPlaybackFinished(object? sender, AVStatusEventArgs e) { _playCompletionSource?.TrySetResult(e.Succeeded); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { if (_audioPlayer != null) { _audioPlayer.FinishedPlaying -= AudioPlaybackFinished; _audioPlayer.Stop(); _audioPlayer.Dispose(); _audioPlayer = null; } _playCompletionSource?.TrySetCanceled(); } _disposed = true; } ~VoicePlayer() { Dispose(false); } }
使用方式
通过await配合using,确保播放完成后自动释放:
using var voicePlayer = new VoicePlayer(); voicePlayer.Initialize("Resources/Audio/notification.mp3"); await voicePlayer.PlayAsync(); // 播放完成后自动执行Dispose
核心注意事项
- 必须在
Dispose中移除FinishedPlaying事件订阅,避免因事件引用导致的内存泄漏。 - 严格遵循IDisposable标准实现模式,区分托管/非托管资源的释放逻辑。
- 若需支持中途停止播放,需在
Stop方法中完成_playCompletionSource的状态设置,避免异步任务挂起。
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

