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

Flutter中AudioPlayers包Android端播放异常原因排查

问题分析:Widget音频播放行为不稳定及错误日志解读

需求为Widget渲染完成后循环播放音频,销毁时停止,但当前代码运行行为不稳定:有时正常循环播放,有时仅播放一次,有时完全不播放。真实Android设备测试时出现如下错误日志:

E/MediaPlayerNative(21219): stop called in state 1, mPlayer(0x0)
E/MediaPlayerNative(21219): error (-38, 0)
V/MediaPlayer(21219): resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
V/MediaPlayer(21219): cleanDrmObj: mDrmObj=null mDrmSessionId=null
V/MediaPlayer(21219): resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
V/MediaPlayer(21219): cleanDrmObj: mDrmObj=null mDrmSessionId=null

对应的Widget代码:

class TopButtonBackground extends StatefulWidget {
  const TopButtonBackground({
    Key? key,
  }) : super(key: key);

  @override
  State<TopButtonBackground> createState() => _TopButtonBackgroundState();
}

class _TopButtonBackgroundState extends State<TopButtonBackground>
    with SingleTickerProviderStateMixin {
  Animation<double>? _animation;
  AnimationController? _controller;
  AudioPlayer player = AudioPlayer();
  static AudioCache audioCache = AudioCache(prefix: 'assets/');
  @override
  void initState() {
    super.initState();
    _playAudio();
    _controller =
        AnimationController(duration: const Duration(seconds: 12), vsync: this);
    _animation = Tween<double>(begin: 0, end: 1).animate(_controller!)
      ..addListener(() {
        setState(() {});
      });
    _controller?.forward();
  }

  void _playAudio() async {
    final url = await audioCache.load("direct_request_tune.mp3");
    player.setReleaseMode(ReleaseMode.loop);
    player.play(DeviceFileSource(url.path));
  }

  @override
  void dispose() {
    _controller?.dispose();
    player.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_animation != null) {
      return Opacity(
        opacity: min((sin(_animation!.value * 50) + 1.3) / 2, 1),
        child: Container(
            height: 70,
            width: MediaQuery.of(context).size.width * _animation!.value,
            color: Colors.grey.withOpacity(.2)),
      );
    }
    return Container(height: 70);
  }
}
行为不稳定及错误原因分析

1. 音频播放时机与Widget生命周期不匹配

initState中直接调用_playAudio(),但此时Widget还未完成渲染(build方法尚未执行完毕),音频播放操作可能因Widget未完全挂载而被系统中断或优先级降低,导致播放行为不可预测。

2. AudioPlayer状态管理不当

  • player.dispose()在dispose方法中直接调用,但未先停止播放或重置播放器状态。错误日志中的stop called in state 1表明调用停止操作时,播放器处于不允许该操作的状态(如未初始化完成或已释放),触发error (-38, 0)(代表操作无效,状态不匹配)。
  • _playAudio中设置循环模式后直接调用play,未处理播放过程中的异常或状态回调,一旦播放出现错误(如文件加载延迟、播放器状态异常),无法自动恢复,导致播放中断或无法启动。

3. 异步加载与播放的时序问题

audioCache.load是异步操作,若文件加载完成前Widget因setState触发重建或生命周期变化,可能导致播放器实例状态混乱。比如Widget快速重建时,可能出现多个播放器实例竞争资源,或旧实例被提前释放。

4. 缺少状态监听与错误处理

代码未对AudioPlayer的播放状态(如onPlayerComplete、onPlayerError)进行监听,无法及时捕获播放过程中的错误并处理,出现问题时无法恢复播放,表现为仅播放一次或完全不播放。

内容的提问来源于stack exchange,提问作者Sami Al-Subhi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 10:30:52