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

React Native expo-av 如何预加载多音频消除连续播放的停顿?

解决方案

核心优化思路:

  • 提前预加载所有数字音频资源,避免播放时才初始化实例、加载文件产生的耗时停顿
  • 用播放队列统一管理待播放的音频序号,消除嵌套回调和重复绑定播放状态监听的冗余逻辑
  • 复用统一的播放状态回调,音频播放完成后直接从预加载列表取对应音频无缝切换播放

完整实现代码

import React, { useState, useEffect } from 'react';
import { Audio } from 'expo-av';

// 提前定义所有音频资源
const audioAssets = [
  require(`../../assets/en_num_0.mp3`),
  require(`../../assets/en_num_1.mp3`),
  require(`../../assets/en_num_2.mp3`),
  require(`../../assets/en_num_3.mp3`),
  require(`../../assets/en_num_4.mp3`),
  require(`../../assets/en_num_5.mp3`),
  require(`../../assets/en_num_6.mp3`),
  require(`../../assets/en_num_7.mp3`),
  require(`../../assets/en_num_8.mp3`),
  require(`../../assets/en_num_9.mp3`),
];

export default function NumberAudioPlayer() {
  // 存储所有预加载完成的sound实例
  const [soundList, setSoundList] = useState([]);
  // 当前待播放的音频队列,存储数字序号
  const [playQueue, setPlayQueue] = useState([]);
  // 当前播放到队列的索引位置
  const [currentQueueIndex, setCurrentQueueIndex] = useState(0);

  // 组件挂载时预加载所有音频
  useEffect(() => {
    let isMounted = true;
    const preloadAllAudios = async () => {
      const loadedSounds = [];
      for (let i = 0; i < audioAssets.length; i++) {
        // 预加载时不自动播放
        const { sound } = await Audio.Sound.createAsync(audioAssets[i], { shouldPlay: false });
        // 所有音频统一绑定同一个播放状态监听回调
        sound.setOnPlaybackStatusUpdate(playStatusHandler);
        loadedSounds.push(sound);
      }
      if (isMounted) {
        setSoundList(loadedSounds);
      }
    };
    preloadAllAudios();

    // 组件卸载时释放所有音频资源,避免内存泄漏
    return () => {
      isMounted = false;
      soundList.forEach(sound => {
        sound?.unloadAsync();
      });
    };
  }, []);

  // 统一的播放状态监听回调
  const playStatusHandler = async (status) => {
    if (!status.didJustFinish) return;
    // 当前音频播放完成,检查队列是否有下一个待播放音频
    const nextIndex = currentQueueIndex + 1;
    if (nextIndex < playQueue.length) {
      // 直接播放预加载好的下一个音频,无加载耗时
      setCurrentQueueIndex(nextIndex);
      const nextSound = soundList[playQueue[nextIndex]];
      // 重置播放位置到开头
      await nextSound.setPositionAsync(0);
      await nextSound.playAsync();
    }
  };

  // 支持传入任意数量的数字参数进行连续播放
  async function playSound(...nums) {
    if (soundList.length === 0) return; // 预加载未完成时可自行添加加载提示
    // 先停止当前正在播放的音频
    if (playQueue.length > 0 && currentQueueIndex < playQueue.length) {
      const currentSound = soundList[playQueue[currentQueueIndex]];
      await currentSound.stopAsync();
    }
    // 更新播放队列,重置播放索引
    setPlayQueue(nums);
    setCurrentQueueIndex(0);
    // 播放队列第一个音频
    const firstSound = soundList[nums[0]];
    await firstSound.setPositionAsync(0);
    await firstSound.playAsync();
  }

  // 此处补充你的UI组件逻辑
  return <></>;
}

实现说明

  • 预加载逻辑只会在组件挂载时执行一次,所有音频实例初始化完成后常驻内存,播放切换时不需要再做文件加载、实例初始化操作,完全消除了切换间隙的停顿
  • 播放状态监听只需要绑定一次,不管连续播放多少个音频都不需要重复注册回调,也没有嵌套回调的冗余代码
  • 支持传入任意数量的数字进行连续播放,不需要修改底层逻辑
  • 若后续音频资源数量变大,可将全量预加载调整为预加载队列中接下来1-2个待播放音频,进一步降低内存占用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 09:36:04