less
复制代码
```
List<String> voicePlay = VoiceTextTemplate.genVoiceListByBean(builder);
if (voicePlay == null || voicePlay.isEmpty()) {
return;
}
// Logger.d("================executeStart:getTransAmount::" + builder.getVoicePlayBean().getTransAmount());
mExecutorService.execute(() -> startPlayAudio(voicePlay));
```
/**
* 开始播报 (支持间隔延迟)
*
* @param nameList 音频资源名称列表
*/
private void startPlayAudio(final List<String> nameList) {
if (nameList == null || nameList.isEmpty()) {
return;
}
// 启动递归播放,从索引 0 开始
playWithDelay(nameList, 0);
}
/**
* 递归播放单个音频,结束后延迟再播下一个
*
* @param nameList 列表
* @param index 当前播放索引
*/
private void playWithDelay(final List<String> nameList, final int index) {
// 终止条件:索引超出范围,表示播放完毕
if (index >= nameList.size()) {
return;
}
synchronized (VoicePlay.this) {
final CountDownLatch mCountDownLatch = new CountDownLatch(1);
SimpleExoPlayer player = ExplayerNetUtil.getInstance(mContext).getCurrentPlayer();
// 1. 停止并清理之前的状态,防止重叠
player.stop();
// player.clearMediaSources();
// 2. 获取当前音频资源
String resourceName = nameList.get(index);
int resId = mContext.getResources().getIdentifier(resourceName, "raw", mContext.getPackageName());
if (resId == 0) {
// 资源不存在,记录日志或直接跳过播放下一个
Log.w("VoicePlay", "Resource not found: " + resourceName);
// 直接递归调用下一个,不等待也不延迟(或者你也可以选择在这里 sleep)
playWithDelay(nameList, index + 1);
return;
}
Uri uri = getByUri(mContext, resId);
// 3. 使用 Factory 构建 MediaSource (修复 deprecated 警告)
MediaSource mediaSource = new ExtractorMediaSource.Factory(
ExplayerNetUtil.getInstance(mContext).getDataSourceFactory()
)
.setExtractorsFactory(ExplayerNetUtil.getInstance(mContext).getDefaultExtractorsFactory())
.createMediaSource(uri);
// 4. 配置播放器属性
player.setAudioAttributes(
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
.setContentType(C.CONTENT_TYPE_SPEECH)
.setUsage(C.USAGE_MEDIA)
.build()
// true // 处理音频焦点
);
player.setPlaybackParameters(new PlaybackParameters(1.0f));
// 5. 添加监听器
// 注意:这里每次都会 addListener,ExoPlayer 内部会管理,但最好确保逻辑简单
player.addListener(new Player.EventListener() {
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_ENDED) {
mCountDownLatch.countDown();
}
}
@Override
public void onPlayerError(ExoPlaybackException error) {
// 发生错误也释放锁,避免死锁,并尝试播放下一个
Log.e("VoicePlay", "Playback error", error);
mCountDownLatch.countDown();
}
// 其他回调省略...
@Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {}
@Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
@Override public void onLoadingChanged(boolean isLoading) {}
@Override public void onRepeatModeChanged(int repeatMode) {}
@Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {}
@Override public void onPositionDiscontinuity(int reason) {}
@Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {}
@Override public void onSeekProcessed() {}
});
// 6. 准备并播放
player.prepare(mediaSource);
player.setPlayWhenReady(true);
try {
// 等待当前音频播放完成 (设置超时防止无限等待,例如 30 秒)
boolean completed = mCountDownLatch.await(30, TimeUnit.SECONDS);
if (completed) {
// 7. 【核心逻辑】播放完成后,强制休眠 10 毫秒 (10ms)
Thread.sleep(10);
} else {
Log.w("VoicePlay", "Playback timed out for index: " + index);
}
// 8. 递归调用播放下一个
playWithDelay(nameList, index + 1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.e("VoicePlay", "Thread interrupted", e);
}
}
}