Audio-音频-播放的方式

Android 音频播放的多种方式详解

1. 按抽象层次分类

1.1 高层API(最简单)

MediaPlayer - 全功能媒体播放器
java 复制代码
// 1. 创建MediaPlayer
MediaPlayer mediaPlayer = new MediaPlayer();

// 2. 设置数据源
mediaPlayer.setDataSource(context, uri);
// 或
mediaPlayer.setDataSource(filePath);

// 3. 准备播放
mediaPlayer.prepareAsync();  // 异步准备
// mediaPlayer.prepare();    // 同步准备

// 4. 设置监听器
mediaPlayer.setOnPreparedListener(mp -> {
    mp.start();  // 开始播放
});

mediaPlayer.setOnCompletionListener(mp -> {
    mp.release();  // 播放完成释放资源
});

// 5. 控制方法
mediaPlayer.start();     // 开始/恢复
mediaPlayer.pause();     // 暂停
mediaPlayer.stop();      // 停止
mediaPlayer.seekTo(ms);  // 跳转到指定位置
mediaPlayer.reset();     // 重置

特点

  • 支持多种格式(MP3, AAC, WAV, MP4, 3GP等)
  • 自动处理音频焦点
  • 支持后台播放
  • 网络流媒体支持
  • 音量控制、循环播放
SoundPool - 短音频效果播放
java 复制代码
// 1. 创建SoundPool
SoundPool soundPool = new SoundPool.Builder()
    .setMaxStreams(10)      // 最大同时播放数
    .setAudioAttributes(new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .build())
    .build();

// 2. 加载音频
int soundId = soundPool.load(context, R.raw.beep, 1);

// 3. 播放音频
soundPool.play(soundId,    // soundId
    1.0f, 1.0f,           // 左右声道音量
    1,                    // 优先级
    0,                    // 循环次数(0=不循环)
    1.0f);                // 播放速度

// 4. 资源管理
soundPool.unload(soundId);
soundPool.release();

特点

  • 低延迟播放短音频
  • 支持同时播放多个音频
  • 适合游戏音效、提示音
  • 音频预加载到内存
JetPlayer - MIDI音乐播放
java 复制代码
JetPlayer jetPlayer = JetPlayer.getJetPlayer();
jetPlayer.loadJetFile(context, R.raw.music);
jetPlayer.play();

1.2 中层API(更灵活)

AudioTrack - 原始PCM数据播放
java 复制代码
// 1. 创建AudioTrack
AudioTrack audioTrack = new AudioTrack.Builder()
    .setAudioAttributes(new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build())
    .setAudioFormat(new AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
        .setSampleRate(44100)
        .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
        .build())
    .setBufferSizeInBytes(minBufferSize)
    .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
    .build();

// 2. 播放模式
// MODE_STATIC: 一次性加载所有数据
// MODE_STREAM: 流式播放

// 3. 写入数据
byte[] audioData = getPCMData();
int written = audioTrack.write(audioData, 0, audioData.length, 
                              AudioTrack.WRITE_BLOCKING);

// 4. 控制播放
audioTrack.play();
audioTrack.pause();
audioTrack.stop();
audioTrack.flush();

// 5. 设置监听
audioTrack.setPlaybackPositionUpdateListener(
    new AudioTrack.OnPlaybackPositionUpdateListener() {
        @Override
        public void onMarkerReached(AudioTrack track) {
            // 标记点到达
        }
        
        @Override
        public void onPeriodicNotification(AudioTrack track) {
            // 定期通知
        }
    });

特点

  • 直接播放PCM数据
  • 低延迟控制
  • 支持多种音频格式
  • 适合音频处理、实时合成
ToneGenerator - DTMF/提示音生成
java 复制代码
ToneGenerator toneGen = new ToneGenerator(
    AudioManager.STREAM_MUSIC,  // 流类型
    ToneGenerator.MAX_VOLUME);  // 音量

// 播放DTMF音
toneGen.startTone(ToneGenerator.TONE_DTMF_0);

// 停止
toneGen.stopTone();

1.3 底层API(最灵活,最复杂)

OpenSL ES - 跨平台音频API
c 复制代码
// C++/NDK中使用
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

// 1. 创建引擎
SLresult result;
SLEngineItf engineEngine;

result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

// 2. 创建输出混音器
SLObjectItf outputMixObject;
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);

// 3. 创建播放器
SLObjectItf playerObject;
SLPlayItf playerPlay;

SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
    SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {
    SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
    SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
    SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};

SLDataSource audioSrc = {&loc_bufq, &format_pcm};
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};

const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};

result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, 
                                           &audioSrc, &audioSnk, 1, ids, req);
result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);

// 4. 设置缓冲队列回调
SLAndroidSimpleBufferQueueItf playerBufferQueue;
result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, 
                                      &playerBufferQueue);
result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, 
                                               bqPlayerCallback, NULL);

// 5. 播放
result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);

特点

  • 跨平台标准
  • 极低延迟
  • 适合专业音频应用
  • 需要NDK开发
AAudio - Android 8.0+ 高性能音频
cpp 复制代码
// C++/NDK中使用
#include <aaudio/AAudio.h>

// 1. 创建流构建器
AAudioStreamBuilder *builder;
aaudio_result_t result = AAudio_createStreamBuilder(&builder);

// 2. 配置流参数
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
AAudioStreamBuilder_setSampleRate(builder, 48000);
AAudioStreamBuilder_setChannelCount(builder, 2);
AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
AAudioStreamBuilder_setPerformanceMode(builder, 
    AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

// 3. 设置数据回调
AAudioStreamBuilder_setDataCallback(builder, dataCallback, nullptr);
AAudioStreamBuilder_setErrorCallback(builder, errorCallback, nullptr);

// 4. 打开流
AAudioStream *stream;
result = AAudioStreamBuilder_openStream(builder, &stream);

// 5. 开始播放
result = AAudioStream_requestStart(stream);

// 6. 数据回调函数
aaudio_data_callback_result_t dataCallback(
    AAudioStream *stream,
    void *userData,
    void *audioData,
    int32_t numFrames) {
    
    // 填充音频数据
    int16_t *buffer = (int16_t *)audioData;
    for (int i = 0; i < numFrames * 2; i++) {
        buffer[i] = generateSample();  // 生成音频样本
    }
    
    return AAUDIO_CALLBACK_RESULT_CONTINUE;
}

特点

  • Android 8.0+ 原生高性能API
  • 比OpenSL ES更简单
  • 极低延迟(<10ms)
  • 支持独占模式

2. 按使用场景分类

2.1 音乐播放场景

java 复制代码
// 使用MediaPlayer + Service实现后台播放
public class MusicService extends Service {
    private MediaPlayer mediaPlayer;
    private AudioManager audioManager;
    
    @Override
    public void onCreate() {
        mediaPlayer = new MediaPlayer();
        
        // 设置音频属性
        mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .build());
            
        // 音频焦点监听
        audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        audioManager.requestAudioFocus(focusChangeListener,
            AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN);
    }
    
    // 实现音频焦点变化监听
    private AudioManager.OnAudioFocusChangeListener focusChangeListener = 
        new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    mediaPlayer.start();
                    mediaPlayer.setVolume(1.0f, 1.0f);
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    mediaPlayer.stop();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    mediaPlayer.pause();
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    mediaPlayer.setVolume(0.1f, 0.1f);
                    break;
            }
        }
    };
}

2.2 游戏音效场景

java 复制代码
// SoundPool适合短音效
public class GameAudioManager {
    private SoundPool soundPool;
    private HashMap<String, Integer> soundMap = new HashMap<>();
    
    public void init(Context context) {
        soundPool = new SoundPool.Builder()
            .setMaxStreams(20)
            .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_GAME)
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .build())
            .build();
            
        // 预加载音效
        loadSound(context, R.raw.shoot, "shoot");
        loadSound(context, R.raw.explosion, "explosion");
        loadSound(context, R.raw.jump, "jump");
    }
    
    public void playSound(String soundName) {
        Integer soundId = soundMap.get(soundName);
        if (soundId != null) {
            soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
        }
    }
}

2.3 实时语音/通话场景

java 复制代码
// AudioTrack适合实时音频处理
public class VoicePlayer {
    private AudioTrack audioTrack;
    private Thread playbackThread;
    private volatile boolean isPlaying = false;
    
    public void startPlayback() {
        int minBufferSize = AudioTrack.getMinBufferSize(
            16000,  // 采样率
            AudioFormat.CHANNEL_OUT_MONO,
            AudioFormat.ENCODING_PCM_16BIT);
            
        audioTrack = new AudioTrack.Builder()
            .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                .build())
            .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .setSampleRate(16000)
                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                .build())
            .setBufferSizeInBytes(minBufferSize * 2)
            .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
            .build();
            
        audioTrack.play();
        isPlaying = true;
        
        // 启动播放线程
        playbackThread = new Thread(() -> {
            byte[] buffer = new byte[minBufferSize];
            while (isPlaying) {
                // 从网络或麦克风获取数据
                int bytesRead = getAudioData(buffer);
                if (bytesRead > 0) {
                    audioTrack.write(buffer, 0, bytesRead);
                }
            }
        });
        playbackThread.start();
    }
}

2.4 音频处理/分析场景

java 复制代码
// 使用AudioRecord录音 + AudioTrack播放
public class AudioProcessor {
    private AudioRecord audioRecord;
    private AudioTrack audioTrack;
    
    public void startEcho() {
        int sampleRate = 44100;
        int channelConfig = AudioFormat.CHANNEL_IN_MONO;
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
        
        int bufferSize = AudioRecord.getMinBufferSize(sampleRate, 
            channelConfig, audioFormat);
            
        // 录音
        audioRecord = new AudioRecord(
            MediaRecorder.AudioSource.MIC,
            sampleRate,
            channelConfig,
            audioFormat,
            bufferSize);
            
        // 播放
        audioTrack = new AudioTrack.Builder()
            .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(audioFormat)
                .setSampleRate(sampleRate)
                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                .build())
            .setBufferSizeInBytes(bufferSize)
            .build();
            
        audioRecord.startRecording();
        audioTrack.play();
        
        // 实时处理线程
        new Thread(() -> {
            byte[] buffer = new byte[bufferSize];
            while (true) {
                int bytesRead = audioRecord.read(buffer, 0, buffer.length);
                
                // 可以在这里处理音频数据(回声、混响等)
                processAudio(buffer, bytesRead);
                
                // 播放处理后的数据
                audioTrack.write(buffer, 0, bytesRead);
            }
        }).start();
    }
}

3. 音频焦点管理

3.1 请求音频焦点

java 复制代码
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

// 请求焦点
int result = audioManager.requestAudioFocus(
    new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            // 处理焦点变化
        }
    },
    AudioManager.STREAM_MUSIC,      // 流类型
    AudioManager.AUDIOFOCUS_GAIN    // 焦点类型
);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // 获得焦点,可以播放
    mediaPlayer.start();
}

3.2 焦点类型

java 复制代码
// 长期获取焦点(音乐播放)
AudioManager.AUDIOFOCUS_GAIN

// 临时获取焦点(导航提示)
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT

// 临时获取,其他应用降低音量(通知音)
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

// 独占临时焦点(语音助手)
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE

4. 性能优化

4.1 低延迟配置

java 复制代码
// AudioTrack低延迟配置
AudioTrack audioTrack = new AudioTrack.Builder()
    .setAudioAttributes(new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setFlags(AudioAttributes.FLAG_LOW_LATENCY)  // 低延迟标志
        .build())
    .setAudioFormat(new AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
        .setSampleRate(48000)
        .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
        .build())
    .setBufferSizeInBytes(AudioTrack.getMinBufferSize(48000,
        AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT))
    .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
    .build();

4.2 内存优化

java 复制代码
// 使用直接缓冲区
ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize);
directBuffer.order(ByteOrder.nativeOrder());

// 写入AudioTrack
int written = audioTrack.write(directBuffer, bufferSize, 
                              AudioTrack.WRITE_BLOCKING);

// 避免频繁创建/销毁对象
private static final AudioTrack[] audioTrackPool = new AudioTrack[5];
private static int poolIndex = 0;

public AudioTrack getAudioTrack() {
    if (audioTrackPool[poolIndex] == null) {
        audioTrackPool[poolIndex] = createAudioTrack();
    }
    return audioTrackPool[poolIndex];
}

5. 最佳实践

5.1 错误处理

java 复制代码
try {
    mediaPlayer.setDataSource(path);
    mediaPlayer.prepare();
    mediaPlayer.start();
} catch (IOException e) {
    Log.e("AudioPlayer", "文件错误: " + e.getMessage());
    // 尝试其他播放方式
    fallbackToAudioTrack();
} catch (IllegalStateException e) {
    Log.e("AudioPlayer", "状态错误: " + e.getMessage());
    resetMediaPlayer();
} finally {
    // 确保资源释放
    if (mediaPlayer != null) {
        mediaPlayer.release();
    }
}

5.2 权限管理

xml 复制代码
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

5.3 后台播放服务

java 复制代码
// 前台服务,避免被系统杀死
public class MusicService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        
        // 创建通知
        Notification notification = new Notification.Builder(this, CHANNEL_ID)
            .setContentTitle("音乐播放中")
            .setContentText("正在播放音乐")
            .setSmallIcon(R.drawable.ic_music)
            .build();
            
        // 启动前台服务
        startForeground(NOTIFICATION_ID, notification);
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 处理播放控制命令
        String action = intent.getAction();
        if ("PLAY".equals(action)) {
            playMusic();
        } else if ("PAUSE".equals(action)) {
            pauseMusic();
        }
        
        return START_STICKY;
    }
}

6. 各方式对比总结

方式 适用场景 延迟 复杂度 功能 API Level
MediaPlayer 音乐播放、网络流媒体 1
SoundPool 游戏音效、提示音 1
AudioTrack 实时音频、PCM处理 3
OpenSL ES 专业音频、游戏 极低 9+
AAudio 高性能音频 极低 26+
ToneGenerator DTMF提示音 1

7. 选择建议

  1. 普通音乐播放MediaPlayer(功能全,易用)
  2. 游戏音效SoundPool(支持并发,低延迟)
  3. 语音通话/录音AudioTrack(实时控制)
  4. 专业音频应用AAudio/OpenSL ES(高性能)
  5. 系统提示音ToneGenerator/Notification

8. 调试工具

bash 复制代码
# 查看音频设备
adb shell dumpsys audio

# 查看音频服务状态
adb shell dumpsys media.audio_flinger
adb shell dumpsys media.audio_policy

# 性能分析
adb shell dumpsys media.metrics

# 延迟测试
adb shell am start -a android.media.action.DISPLAY_AUDIO_LATENCY

根据具体需求选择合适的播放方式,平衡性能、功能和开发复杂度。

相关推荐
2501_9159090619 小时前
没有 Mac 怎么上架 iOS 应用 跨平台团队的可行交付方案分析
android·macos·ios·小程序·uni-app·iphone·webview
吃好喝好玩好睡好19 小时前
OpenHarmony 设备中 Electron 桌面 + Flutter 移动端音视频流互通实战
flutter·electron·音视频
代码or搬砖20 小时前
公共字段抽取自动填充
android·java·数据库
飞Link1 天前
【网络与 AI 工程的交叉】多模态模型的数据传输特点:视频、音频、文本混合通道
网络·人工智能·音视频
音视频牛哥1 天前
从“能播”到“能控”:深入解读 SmartMediakit 与 OTT 播放器的架构裂变
音视频·ott·低延迟rtsp播放器·smartmediakit·低延迟rtmp播放器·低延迟音视频技术方案·具身智能低延迟rtsp方案
_李小白1 天前
【Android FrameWork】第二十四天:Activity生命周期是如何运行的
android
ytttr8731 天前
基于C#的CAN总线数据解析BMS上位机
android·unity·c#
darryrzhong1 天前
FluxImageLoader : 基于Coil3封装的 Android 图片加载库,旨在提供简单、高效且功能丰富的图片加载解决方案
android·github·android jetpack
pandarking1 天前
[CTF]攻防世界:题目名称-warmup
android·web安全·网络安全