You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用AudioPlayers与Future.wait播放音效时Flutter应用崩溃求助

Flutter音效播放多次后崩溃问题排查与解决

问题描述

我需要创建List<Future<void>>管理音效播放任务,遍历事件列表(如"sword"类型)并映射为对应音效播放操作。为实现随机播放间隔,每个任务用Future.delayed包裹,设置30-100毫秒随机延迟,最后通过Future.wait批量执行。前几次运行音效播放正常,但执行4次后应用直接崩溃,怀疑是资源管理或audioplayers API使用不当导致。

代码实现

音效播放逻辑

_playActionEventSounds() async {
    if (widget.log.isNotEmpty) {
      final lastRound = widget.log.map((e) => e.round).reduce(max);
      final lastActionEvents = widget.log.where((actionEvent) => actionEvent.round == lastRound);
      final List<Future<void>> eventSounds = lastActionEvents.map((actionEvent) async {
        if (actionEvent.format == "sword") {
          return Future.delayed(Duration(milliseconds: next(30, 100)), () => _soundPlayer.punch());
        }
        if (actionEvent.format == "damage") {
          return Future.delayed(Duration(milliseconds: next(30, 100)), () => _soundPlayer.pain());
        }
        return Future.value(null);
      }).toList();

      await Future.wait(eventSounds);
    }
  }

SoundPlayer类

import 'package:audioplayers/audioplayers.dart';

class SoundPlayer {
  Future<void> punch() async {
    final player = AudioPlayer();
    player.setReleaseMode(ReleaseMode.stop);
    return await player.play(AssetSource("sounds/punch.wav"));
  }

  Future<void> pain() async {
    final player = AudioPlayer();
    player.setReleaseMode(ReleaseMode.stop);
    return await player.play(AssetSource("sounds/pain.wav"));
  }
}

错误日志

I/flutter (28346): Unexpected platform error: MediaPlayer error with what:MEDIA_ERROR_UNKNOWN {what:1} extra:MEDIA_ERROR_UNKNOWN {extra:-19}
E/MediaPlayerNative(28346): pause called in state 0, mPlayer(0xb400007a92646810)
E/MediaPlayerNative(28346): error (-38, 0)
E/MediaPlayerNative(28346): Attempt to call getDuration in wrong state: mPlayer=0xb400007a92646810, mCurrentState=0
E/MediaPlayerNative(28346): error (-38, 0)
E/MediaPlayerNative(28346): stop called in state 0, mPlayer(0xb400007a92646810)
E/MediaPlayerNative(28346): error (-38, 0)
E/MediaPlayerNative(28346): prepareAsync called in state 0, mPlayer(0xb400007a92646810)
D/AndroidRuntime(28346): Shutting down VM
E/AndroidRuntime(28346): FATAL EXCEPTION: main
E/AndroidRuntime(28346): Process: com.example.app, PID: 28346
E/AndroidRuntime(28346): java.lang.IllegalStateException
E/AndroidRuntime(28346):    at android.media.MediaPlayer._prepare(Native Method)
E/AndroidRuntime(28346):    at android.media.MediaPlayer.prepare(MediaPlayer.java:1313)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.MediaPlayerPlayer.prepare(MediaPlayerPlayer.kt:89)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.WrappedPlayer.stop(WrappedPlayer.kt:185)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.WrappedPlayer.onCompletion(WrappedPlayer.kt:248)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.MediaPlayerPlayer.createMediaPlayer$lambda-5$lambda-1(MediaPlayerPlayer.kt:17)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.MediaPlayerPlayer.$r8$lambda$3fK1i48Yert5dbg2Q8ZiB5tiKHg(Unknown Source:0)
E/AndroidRuntime(28346):    at xyz.luan.audioplayers.player.MediaPlayerPlayer$$ExternalSyntheticLambda1.onCompletion(Unknown Source:2)
E/AndroidRuntime(28346):    at android.media.MediaPlayer$EventHandler.handleMessage(MediaPlayer.java:3559)
E/AndroidRuntime(28346):    at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime(28346):    at android.os.Looper.loopOnce(Looper.java:201)
E/AndroidRuntime(28346):    at android.os.Looper.loop(Looper.java:288)
E/AndroidRuntime(28346):    at android.app.ActivityThread.main(ActivityThread.java:7842)
E/AndroidRuntime(28346):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(28346):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/AndroidRuntime(28346):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
I/Process (28346): Sending signal. PID: 28346 SIG: 9
Lost connection to device.

解决方法

1. 修复AudioPlayer资源泄漏问题

当前代码每次播放都新建AudioPlayer实例,且未主动释放资源,多次调用后会耗尽系统媒体播放器资源,触发状态异常。

2. 优化SoundPlayer类,复用播放器实例

改为为每种音效维护单例播放器,并在播放完成后重置状态,页面销毁时释放资源:

import 'package:audioplayers/audioplayers.dart';

class SoundPlayer {
  final AudioPlayer _punchPlayer = AudioPlayer();
  final AudioPlayer _painPlayer = AudioPlayer();

  Future<void> punch() async {
    try {
      await _punchPlayer.setSource(AssetSource("sounds/punch.wav"));
      await _punchPlayer.play();
      // 播放完成后重置状态,避免下次播放出错
      _punchPlayer.onPlayerComplete.listen((_) {
        _punchPlayer.stop();
        _punchPlayer.seek(Duration.zero);
      });
    } catch (e) {
      print("punch音效播放失败: $e");
    }
  }

  Future<void> pain() async {
    try {
      await _painPlayer.setSource(AssetSource("sounds/pain.wav"));
      await _painPlayer.play();
      _painPlayer.onPlayerComplete.listen((_) {
        _painPlayer.stop();
        _painPlayer.seek(Duration.zero);
      });
    } catch (e) {
      print("pain音效播放失败: $e");
    }
  }

  // 在页面销毁时调用,释放所有播放器资源
  void dispose() {
    _punchPlayer.dispose();
    _painPlayer.dispose();
  }
}

3. 修正随机延迟生成逻辑

确保随机数生成正确,使用dart:math的Random类:

import 'dart:math';

final random = Random();
// 生成30-100毫秒的随机延迟
int getRandomDelay() => random.nextInt(71) + 30; // 71 = 100 - 30 + 1

4. 捕获异常避免崩溃扩散

在播放方法中添加异常捕获,防止单个音效播放失败导致整个应用崩溃。

内容的提问来源于stack exchange,提问作者xetra11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 04:55:18