AudioCacheOptimization.md 4.6 KB

音频缓存优化解决方案

问题描述

在点击广告按钮时,发现会重复加载已经在游戏初始化时加载过的音效资源,导致以下问题:

  1. 重复资源加载:每次播放音效都会重新从Bundle加载资源
  2. 性能影响:不必要的资源加载消耗性能
  3. 错误风险:频繁的资源加载可能导致系统错误

错误日志示例

[BundleLoader] 从Bundle data 加载资源: 弹球音效/ui play 
index.js? [sm]:33 [BundleLoader] 资源加载成功: data/弹球音效/ui play 
index.js? [sm]:7 [AudioManager] 播放UI音效: data/弹球音效/ui play 
Error: SystemError (appServiceSDKScriptError) 
{"errMsg":"updateTextView:fail 33409 not found"}

问题根源分析

1. 触发链路

  • 点击广告按钮 → 播放UI音效 Audio.playUISound('data/弹球音效/ui play')
  • 广告成功回调 → 触发奖励动画 → MoneyAni播放动画 → 播放获得金币音效 Audio.playUISound('data/弹球音效/get money')

2. 原始AudioManager问题

// 每次播放都重新加载资源
const clip = await this.bundleLoader.loadAssetFromBundle<AudioClip>('data', bundlePath, AudioClip);

3. BundleLoader无缓存机制

BundleLoader的loadAssetFromBundle方法每次都会重新从Bundle加载资源,没有缓存机制。

解决方案

1. 音频缓存机制

在AudioManager中添加音频缓存:

// 音频缓存
private audioClipCache: Map<string, AudioClip> = new Map();

private async playSound(audioSource: AudioSource, sfxPath: string, volume: number | undefined, baseVolume: number, soundType: string) {
    // 检查缓存中是否已有该音频
    let clip = this.audioClipCache.get(cleanPath);
    
    if (!clip) {
        // 首次加载并缓存
        clip = await this.bundleLoader.loadAssetFromBundle<AudioClip>('data', bundlePath, AudioClip);
        this.audioClipCache.set(cleanPath, clip);
        console.log(`[AudioManager] 音效已缓存: ${cleanPath}`);
    } else {
        console.log(`[AudioManager] 使用缓存音效: ${cleanPath}`);
    }
    
    audioSource.playOneShot(clip);
}

2. 缓存管理方法

// 清理缓存
public clearAudioCache() {
    console.log(`[AudioManager] 清理音频缓存,共${this.audioClipCache.size}个音效`);
    this.audioClipCache.clear();
}

// 获取缓存信息
public getCacheInfo(): {count: number, paths: string[]} {
    return {
        count: this.audioClipCache.size,
        paths: Array.from(this.audioClipCache.keys())
    };
}

3. 静态方法支持

// Audio静态类中添加缓存管理
static clearAudioCache() {
    const manager = AudioManager.getInstance();
    if (manager) {
        manager.clearAudioCache();
    }
}

static getCacheInfo(): {count: number, paths: string[]} {
    const manager = AudioManager.getInstance();
    if (manager) {
        return manager.getCacheInfo();
    }
    return {count: 0, paths: []};
}

优化效果

1. 性能提升

  • 首次加载:音效资源从Bundle加载并缓存
  • 后续播放:直接使用缓存,无需重复加载
  • 减少I/O:避免频繁的文件系统访问

2. 稳定性提升

  • 减少错误:避免重复加载导致的系统错误
  • 内存管理:提供缓存清理机制,防止内存泄漏

3. 调试支持

  • 缓存状态:可查看当前缓存的音效数量和路径
  • 日志优化:区分首次加载和缓存使用

使用方法

1. 正常使用(无需改动)

// 现有代码无需修改,自动享受缓存优化
Audio.playUISound('data/弹球音效/ui play');
Audio.playUISound('data/弹球音效/get money');

2. 缓存管理(可选)

// 查看缓存状态
const cacheInfo = Audio.getCacheInfo();
console.log(`缓存音效数量: ${cacheInfo.count}`);
console.log(`缓存音效列表:`, cacheInfo.paths);

// 清理缓存(在内存紧张时)
Audio.clearAudioCache();

注意事项

  1. 内存使用:缓存会占用一定内存,但音效文件通常较小
  2. 自动清理:AudioManager销毁时会自动清理缓存
  3. 向后兼容:现有代码无需修改,自动享受优化
  4. 调试信息:控制台会显示缓存使用情况,便于调试

测试验证

优化后的表现:

  • 首次播放[AudioManager] 从Bundle加载新音效: 弹球音效/ui play
  • 后续播放[AudioManager] 使用缓存音效: data/弹球音效/ui play
  • 无重复加载:不再出现重复的Bundle加载日志
  • 错误消除:避免因重复加载导致的系统错误