Unable to retrieve AudioTrack pointer for write

本文探讨了Android中AudioTrack在停止后尝试写入数据引发的IllegalStateException异常,并提出了同步控制或状态检查的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


E/AndroidRuntime: FATAL EXCEPTION: Thread-18065
                  java.lang.IllegalStateException: Unable to retrieve AudioTrack pointer for write()
                  at android.media.AudioTrack.native_write_byte(Native Method)
                  at android.media.AudioTrack.write(AudioTrack.java:983)

推测是播放过程中,执行了AudioTrack.stop(),依然异步执行AudioTrack.write()导致,因此,在执行两个函数时做下同步,或者判断下是否已经stop,如果否,才去write数据。

<think>我们正在解决的是 `java.lang.IllegalStateException: Unable to retrieve AudioTrack pointer for write()` 错误。 根据引用[1]中的分析,这个异常通常发生在播放过程中,当我们执行了`AudioTrack.stop()`后,仍然有异步线程尝试执行`AudioTrack.write()`,此时AudioTrack对象已经失效(指针为空)。 引用[2]和[3]则展示了在调用`start()`方法时,如果获取不到AudioTrack指针也会抛出类似的异常(但这里是针对`start()`方法)。而我们的问题是发生在`write()`方法上。 解决方案的核心思路:确保在调用`write()`时,AudioTrack对象处于可写状态(即没有停止或释放)。 具体步骤: 1. **同步控制**:在调用`stop()`和`write()`的地方进行同步,确保在`stop()`之后不会再有`write()`调用。 2. **状态检查**:在调用`write()`之前,检查AudioTrack的状态(是否已经停止或释放)。 以下是一个具体的解决方案: ### 一、错误原因分析 - 当调用`AudioTrack.stop()`或`release()`后,底层的AudioTrack对象会被销毁,其指针变为NULL。 - 如果此时还有异步线程(如音频数据写入线程)尝试调用`write()`,就会抛出`IllegalStateException`。 ### 二、解决方案 #### 1. 使用同步锁控制 在写入音频数据和停止播放的操作上加上同步锁,确保不会同时进行。 ```java private final Object lock = new Object(); // 用于同步的对象 // 写入音频数据的方法 public void writeData(byte[] data, int offset, int size) { synchronized (lock) { if (audioTrack != null && audioTrack.getState() == AudioTrack.STATE_INITIALIZED) { // 检查是否已经停止(可以添加自定义的状态标志) if (!isStopped) { audioTrack.write(data, offset, size); } } } } // 停止播放的方法 public void stop() { synchronized (lock) { if (audioTrack != null) { isStopped = true; // 设置停止标志 audioTrack.stop(); audioTrack.release(); audioTrack = null; } } } ``` #### 2. 添加状态标志 添加一个标志位(如`isStopped`),在停止时设置为`true`,在写入前检查该标志。 #### 3. 确保在正确的状态下操作 在调用`write()`之前,检查AudioTrack的状态: ```java if (audioTrack != null && audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) { audioTrack.write(...); } ``` 注意:`getPlayState()`返回的是播放状态,而`getState()`返回的是初始化状态。我们需要确保AudioTrack已经被初始化并且没有停止。 ### 三、完整示例代码 ```java public class AudioPlayer { private AudioTrack audioTrack; private boolean isStopped = false; private final Object lock = new Object(); public void initAudioTrack() { // 初始化AudioTrack int sampleRate = 44100; int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM); isStopped = false; } public void play() { if (audioTrack != null) { audioTrack.play(); } } public void writeData(byte[] data, int offset, int size) { synchronized (lock) { if (audioTrack != null && audioTrack.getState() == AudioTrack.STATE_INITIALIZED && !isStopped) { // 在写入前再次检查状态 if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) { audioTrack.write(data, offset, size); } } } } public void stop() { synchronized (lock) { isStopped = true; // 设置标志,阻止后续写入 if (audioTrack != null) { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.stop(); } audioTrack.release(); audioTrack = null; } } } } ``` ### 四、注意事项 1. **及时释放资源**:在停止播放后,一定要调用`release()`释放资源,并将`audioTrack`置为`null`。 2. **避免多次释放**:确保不要重复调用`stop()`和`release()`,否则可能引发异常。 3. **线程安全**:如果音频写入在单独的线程,那么停止操作可能发生在另一个线程,因此同步锁是必要的。 通过上述方法,我们可以避免在停止播放后继续写入数据导致的指针异常。 §§相关问题§§ 1. 如何正确使用AudioTrack进行音频流播放? 2. AudioTrack的MODE_STATIC和MODE_STREAM模式有什么区别? 3. 如何解决AudioTrack播放过程中的延迟问题?</think>### 解决 Android 中 `java.lang.IllegalStateException: Unable to retrieve AudioTrack pointer for write()` 错误 #### 错误原因分析 该异常发生在音频播放过程中,当调用 `AudioTrack.write()` 时底层指针已失效[^1]。主要原因包括: 1. **异步操作冲突**:`AudioTrack.stop()` 释放资源后,异步线程仍尝试执行 `write()` 操作 2. **无效指针访问**:`AudioTrack` 对象被释放后(`release()` 或 `stop()`),其底层指针变为 NULL 3. **线程同步问题**:多线程环境下状态管理不当导致竞态条件 #### 完整解决方案 ##### 1. 添加同步锁机制(核心修复) ```java private final Object audioLock = new Object(); // 同步锁对象 private boolean isStopped = false; // 状态标志 // 写入音频数据 public void writeAudioData(byte[] data) { synchronized (audioLock) { if (audioTrack != null && !isStopped) { try { audioTrack.write(data, 0, data.length); } catch (IllegalStateException e) { Log.e("Audio", "Write failed: " + e.getMessage()); } } } } // 停止播放 public void stopPlayback() { synchronized (audioLock) { isStopped = true; // 先设置状态标志 if (audioTrack != null) { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.stop(); } audioTrack.release(); audioTrack = null; } } } ``` ##### 2. 增强指针有效性检查 ```java private void safeWrite(byte[] buffer) { if (audioTrack == null) { Log.w("Audio", "AudioTrack is null, skip write"); return; } // 检查AudioTrack状态 if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { Log.w("Audio", "AudioTrack not initialized, state: " + audioTrack.getState()); return; } // 检查播放状态 if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) { Log.w("Audio", "AudioTrack is stopped, skip write"); return; } audioTrack.write(buffer, 0, buffer.length); } ``` ##### 3. 完整的生命周期管理 ```java public class AudioPlayer { private AudioTrack audioTrack; private Thread playbackThread; private volatile boolean isPlaying = false; public void startPlayback() { if (isPlaying) return; // 初始化AudioTrack int sampleRate = 44100; int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM ); audioTrack.play(); isPlaying = true; // 启动播放线程 playbackThread = new Thread(() -> { byte[] buffer = new byte[bufferSize]; while (isPlaying) { // 获取音频数据(示例) // audioSource.read(buffer, 0, buffer.length); synchronized (audioLock) { if (isPlaying && audioTrack != null) { audioTrack.write(buffer, 0, buffer.length); } } } }); playbackThread.start(); } public void stopPlayback() { isPlaying = false; // 先设置状态标志 if (playbackThread != null) { try { playbackThread.join(300); // 等待线程结束 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } synchronized (audioLock) { if (audioTrack != null) { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.stop(); } audioTrack.release(); audioTrack = null; } } } } ``` #### 关键修复点说明 1. **同步锁 (`synchronized`)** 确保 `stop()` 和 `write()` 不会同时执行,防止状态冲突[^1] 2. **状态标志 (`isStopped`)** 明确标识 AudioTrack 状态,避免在释放后尝试写入 3. **双重检查机制** 在关键操作前检查: - 对象是否为 `null` - 状态是否已停止 - `getState()` 是否初始化完成 - `getPlayState()` 是否正在播放 4. **线程安全关闭** 使用 `volatile` 标志停止线程,配合 `Thread.join()` 确保线程安全退出 #### 各 Android 版本适配要点 | **Android 版本** | **注意事项** | |------------------|--------------------------------------| | **Android 5.0+** | 使用 `getState()` 检查初始化状态 | | **Android 6.0+** | 注意运行时权限(RECORD_AUDIO) | | **Android 8.0+** | 后台服务限制,需使用前台服务播放音频 | | **Android 10+** | 限制后台启动Activity,需适配音频焦点 | #### 错误排查清单 1. ✅ 所有 `write()` 操作前检查 `isStopped` 标志 2. ✅ 使用 `synchronized` 保护关键代码段 3. ✅ 在 `stop()` 中先设置标志再释放资源 4. ✅ 避免在 `release()` 后保留 AudioTrack 引用 5. ✅ 使用 `getState()` 和 `getPlayState()` 双重验证 6. ✅ 后台播放使用前台服务(Android 8.0+) > **性能提示**:对于实时音频流,建议使用 `AudioTrack.MODE_STREAM` 模式配合环形缓冲区,避免因同步锁导致音频卡顿。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值