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

Android Studio:AudioTrack高频播放生成音频崩溃问题求助

解决AudioTrack高频播放崩溃与二次播放无响应问题

看起来你遇到的两个问题都和AudioTrack资源管理以及MODE_STATIC模式的使用逻辑直接相关,咱们一步步拆解解决:

问题1:高频播放(15Hz+)崩溃的核心原因

每次调用playGeneratedSound()都新建AudioTrack对象,但完全没有释放资源。Android系统的音频硬件资源是有限的,高频创建未回收的AudioTrack会快速耗尽系统资源,触发内存溢出(OOM)或者系统强制终止应用。

问题2:单次生成音频后二次播放无响应的原因

在MODE_STATIC模式下,AudioTrack播放结束后会停在缓冲区末尾,状态变为PLAYSTATE_STOPPED,如果不重置播放位置,直接再次调用play()不会有任何反应。另外你的generateSound()方法还存在逻辑漏洞:参数传入的duration重新计算后,循环却使用this.buffer.length,如果传入的duration和构造函数初始化的不一致,会导致数据写入不完整或数组越界。

修正方案与代码示例

1. 核心优化点

  • 复用AudioTrack对象,避免频繁创建销毁导致的资源泄漏
  • 修正generateSound的逻辑错误,确保音频数据长度与buffer匹配
  • 正确处理MODE_STATIC模式的二次播放流程,重置播放头位置

2. 修正后的完整代码

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;

public class SoundGenerator {
    private float duration;
    private short[] buffer;
    // 将AudioTrack设为类属性,实现资源复用
    private AudioTrack generatedSound;

    public SoundGenerator() { }

    public SoundGenerator(float duration) {
        this.duration = duration;
        // 计算buffer长度:采样率44100 * 位深2字节(16bit) * 时长(秒)
        int bufferSize = (int) (duration * 44100 * 2);
        buffer = new short[bufferSize / 2]; // short类型占2字节,所以数组长度为bufferSize/2
        // 提前初始化AudioTrack资源
        initAudioTrack(bufferSize);
    }

    private void initAudioTrack(int bufferSize) {
        if (generatedSound != null) {
            // 先释放旧资源再创建新的
            releaseAudioTrack();
        }
        generatedSound = new AudioTrack(
                AudioManager.STREAM_MUSIC,
                44100,
                AudioFormat.CHANNEL_OUT_MONO,
                AudioFormat.ENCODING_PCM_16BIT,
                bufferSize,
                AudioTrack.MODE_STATIC
        );
    }

    public void generateSound(double frequency) {
        int sampleCount = buffer.length;
        for (int i = 0; i < sampleCount; i++) {
            // 简化正弦波计算公式,避免浮点运算误差
            double sineValue = Math.sin(2.0 * Math.PI * i * frequency / 44100);
            buffer[i] = (short) (sineValue * Short.MAX_VALUE);
        }
        // 重新写入更新后的音频数据到AudioTrack
        if (generatedSound != null) {
            generatedSound.write(buffer, 0, sampleCount);
        }
    }

    public void playGeneratedSound(float volume) {
        if (generatedSound == null) {
            return;
        }
        // 停止当前正在播放的音频(如果有的话)
        if (generatedSound.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            generatedSound.stop();
        }
        // 重置播放头到缓冲区起始位置,确保二次播放从开头开始
        generatedSound.setPlaybackHeadPosition(0);
        // 设置播放音量
        generatedSound.setStereoVolume(volume, volume);
        // 启动播放
        generatedSound.play();
    }

    // 提供资源释放方法,务必在页面销毁时调用,避免内存泄漏
    public void releaseAudioTrack() {
        if (generatedSound != null) {
            if (generatedSound.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
                generatedSound.stop();
            }
            generatedSound.release();
            generatedSound = null;
        }
    }
}

3. 额外注意事项

  • 调用完SoundGenerator后,一定要在合适的生命周期(比如Activity的onDestroy())调用releaseAudioTrack(),彻底释放音频资源
  • 如果需要动态修改音频时长,不要直接修改duration,而是新增类似updateDuration(float newDuration)的方法,重新初始化buffer和AudioTrack
  • 高频播放时,确保playGeneratedSound()的调用间隔不要小于音频实际时长,避免音频叠加或播放混乱

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:06:27