From 3d631dbadf98f3f77c6e955cb36219217ef73ab5 Mon Sep 17 00:00:00 2001
From: 唐浩 <15928265276@163.com>
Date: 星期二, 09 九月 2025 12:56:48 +0800
Subject: [PATCH] change by tanghao

---
 app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java |  364 +++++++++++++++++++++++++--------------------------
 1 files changed, 177 insertions(+), 187 deletions(-)

diff --git a/app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java b/app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java
index 3ab7b4e..677dc2e 100644
--- a/app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java
+++ b/app/src/main/java/com/ziang/driver/utils/AudioRecoderUtils.java
@@ -10,10 +10,49 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.LinkedList;
+import java.util.Queue;
 
 import cn.sinata.xldutils.utils.TimeUtils;
 
 public class AudioRecoderUtils {
+    // --- 单例实例 ---
+    private static AudioRecoderUtils instance; // 静态变量,持有唯一实例
+
+    // 私有化构造函数,防止外部直接创建新实例
+    private AudioRecoderUtils() {
+        // 默认保存路径为/sdcard/record/下
+        this(Environment.getExternalStorageDirectory() + "/record/");
+    }
+
+    // 私有化带 filePath 的构造函数
+    private AudioRecoderUtils(String filePath) {
+        File path = new File(filePath);
+        if (!path.exists())
+            path.mkdirs();
+        this.FolderPath = filePath;
+    }
+
+    // --- 获取单例的公共静态方法 ---
+    // 使用 synchronized 确保线程安全,防止多线程环境下创建多个实例
+    public static synchronized AudioRecoderUtils getInstance() {
+        if (instance == null) {
+            instance = new AudioRecoderUtils();
+        }
+        return instance;
+    }
+
+    // 如果需要指定文件路径,可以提供另一个获取实例的方法
+    public static synchronized AudioRecoderUtils getInstance(String filePath) {
+        if (instance == null) {
+            instance = new AudioRecoderUtils(filePath);
+        }
+        // 如果实例已存在但路径不同,这里通常不修改路径。
+        // 若要支持动态路径,需要更复杂的逻辑,或者在外部处理路径逻辑。
+        return instance;
+    }
+    // --- 单例修改结束 ---
+
 
     //文件路径
     private String filePath;
@@ -27,130 +66,13 @@
     private OnAudioStatusUpdateListener audioStatusUpdateListener;
     private MediaPlayer mMediaPlayer;
 
-    /**
-     * 文件存储默认sdcard/record
-     */
-    public AudioRecoderUtils() {
-        //默认保存路径为/sdcard/record/下
-        this(Environment.getExternalStorageDirectory() + "/record/");
-    }
-
-    public AudioRecoderUtils(String filePath) {
-
-        File path = new File(filePath);
-        if (!path.exists())
-            path.mkdirs();
-
-        this.FolderPath = filePath;
-    }
+    // --- 新增:播放队列和状态变量 ---
+    private Queue<String> playQueue = new LinkedList<>(); // 存储待播放的音频文件路径
+    private boolean isPlayingQueue = false; // 标记是否正在播放队列中的音频
+    // --- 新增结束 ---
 
     private long startTime;
     private long endTime;
-
-
-    /**
-     * 开始录音 使用amr格式
-     * 录音文件
-     *
-     * @return
-     */
-    public void startRecord() {
-        // 开始录音
-        /* ①Initial:实例化MediaRecorder对象 */
-        if (mMediaRecorder == null)
-            mMediaRecorder = new MediaRecorder();
-        try {
-            /* ②setAudioSource/setVedioSource */
-            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置麦克风
-            /* ②设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default 声音的(波形)的采样 */
-            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
-            /*
-             * ②设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
-             * ,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
-             */
-            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
-
-            filePath = FolderPath + TimeUtils.getCurrentTime() + ".caf";
-            /* ③准备 */
-            mMediaRecorder.setOutputFile(filePath);
-            mMediaRecorder.setMaxDuration(MAX_LENGTH);
-            mMediaRecorder.prepare();
-            /* ④开始 */
-            mMediaRecorder.start();
-            // AudioRecord audioRecord.
-            /* 获取开始时间* */
-            startTime = System.currentTimeMillis();
-            updateMicStatus();
-            Log.e("fan", "startTime" + startTime);
-        } catch (IllegalStateException e) {
-            Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
-        } catch (IOException e) {
-            Log.i(TAG, "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
-        }
-    }
-
-    /**
-     * 停止录音
-     */
-    public long stopRecord() {
-        if (mMediaRecorder == null)
-            return 0L;
-        endTime = System.currentTimeMillis();
-
-        //有一些网友反应在5.0以上在调用stop的时候会报错,翻阅了一下谷歌文档发现上面确实写的有可能会报错的情况,捕获异常清理一下就行了,感谢大家反馈!
-        try {
-            mMediaRecorder.stop();
-            mMediaRecorder.reset();
-            mMediaRecorder.release();
-            mMediaRecorder = null;
-
-            audioStatusUpdateListener.onStop(filePath);
-            filePath = "";
-
-        } catch (RuntimeException e) {
-            if (mMediaRecorder != null) {
-                mMediaRecorder.reset();
-                mMediaRecorder.release();
-                mMediaRecorder = null;
-            }
-            File file = new File(filePath);
-            if (file.exists())
-                file.delete();
-
-            filePath = "";
-
-        }
-        return endTime - startTime;
-    }
-
-    public String getFilePath() {
-        return filePath;
-    }
-
-    /**
-     * 取消录音
-     */
-    public void cancelRecord() {
-
-        try {
-
-            mMediaRecorder.stop();
-            mMediaRecorder.reset();
-            mMediaRecorder.release();
-            mMediaRecorder = null;
-
-        } catch (RuntimeException e) {
-            mMediaRecorder.reset();
-            mMediaRecorder.release();
-            mMediaRecorder = null;
-        }
-        File file = new File(filePath);
-        if (file.exists())
-            file.delete();
-
-        filePath = "";
-
-    }
 
     private final Handler mHandler = new Handler();
     private Runnable mUpdateMicStatusTimer = new Runnable() {
@@ -163,9 +85,7 @@
     private int BASE = 1;
     private int SPACE = 100;// 间隔取样时间
 
-    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
-        this.audioStatusUpdateListener = audioStatusUpdateListener;
-    }
+
 
     /**
      * 更新麦克状态
@@ -206,80 +126,150 @@
         public void onFinishPlay();
     }
 
-    //开始播放录音
+
     public void startplayMusic(Context context, String filePath) {
-        if (mMediaPlayer != null) {
-            stopPlayMusic();
-        }
-        mMediaPlayer = new MediaPlayer();
-        try {
-            Log.e("mmp", "path:" + filePath);
-            mMediaPlayer.setDataSource(context, Uri.parse(filePath));
-            mMediaPlayer.prepareAsync();
-            mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
-                @Override
-                public void onPrepared(MediaPlayer mp) {
-                    // 装载完毕回调
-                    mMediaPlayer.start();
-                    if (audioStatusUpdateListener != null)
-                        audioStatusUpdateListener.onStartPlay();
-                }
-            });
-            mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
-                @Override
-                public void onCompletion(MediaPlayer mp) {
-                    if (audioStatusUpdateListener != null)
-                        audioStatusUpdateListener.onFinishPlay();
-                    stopPlayMusic();
-//                    BaseEvent baseEvent = new BaseEvent(BaseEvent.SEND_AODIO_PLAY_OVER);
-//                    baseEvent.setMsg(filePath);
-//                    EventBus.getDefault().post(baseEvent);
-                }
-            });
+        // 将新的音频路径添加到播放队列
+        playQueue.offer(filePath); // 将文件路径添加到队列尾部
+        Log.e("AudioRecoderUtils", "Added to queue: " + filePath + ", Queue size: " + playQueue.size());
 
-        } catch (Exception e) {
-            e.printStackTrace();
+        // 如果当前没有在播放,则开始播放队列中的第一个
+        if (!isPlayingQueue) {
+            playNextInQueue(context);
         }
     }
 
-    //结束录音播放
-    public void stopPlayMusic() {
-        if (mMediaPlayer == null)
-            return;
-        try {
-            mMediaPlayer.stop();
-            mMediaPlayer.release();
-            if (audioStatusUpdateListener != null)
+    // --- 新增:播放队列中的下一个音频 ---
+    private void playNextInQueue(Context context) {
+        if (playQueue.isEmpty()) {
+            isPlayingQueue = false;
+            Log.d(TAG, "playNextInQueue: Play queue finished.");
+            if (audioStatusUpdateListener != null) {
                 audioStatusUpdateListener.onFinishPlay();
+            }
+            return;
+        }
+
+        String currentFilePath = playQueue.poll();
+        Log.d(TAG, "playNextInQueue: Now attempting to play from queue: " + currentFilePath);
+
+        // 确保每次播放前,旧的 MediaPlayer 都已经被彻底释放
+        // 这行虽然在前面已经有,但此处是核心逻辑,再次强调其重要性
+        if (mMediaPlayer != null) {
+            stopPlayMusic(); // 确保旧的播放器已完全释放
+        }
+
+        mMediaPlayer = new MediaPlayer();
+        isPlayingQueue = true; // 标记正在播放队列中的音频
+
+        try {
+            mMediaPlayer.setDataSource(context, Uri.parse(currentFilePath));
+            Log.d(TAG, "playNextInQueue: Set data source for: " + currentFilePath + ", preparing async.");
+            mMediaPlayer.prepareAsync();
+
+            mMediaPlayer.setOnPreparedListener(mp -> {
+                Log.d(TAG, "onPrepared: MediaPlayer prepared for " + currentFilePath + ", starting playback.");
+                mp.start(); // 这里使用 mp,它是 MediaPlayer 实例本身
+                if (audioStatusUpdateListener != null)
+                    audioStatusUpdateListener.onStartPlay();
+            });
+
+            mMediaPlayer.setOnCompletionListener(mp -> {
+                Log.d(TAG, "onCompletion: Playback finished for " + currentFilePath + ", moving to next.");
+                // 注意:onFinishPlay() 应该在整个队列播放完毕时才触发,而不是单个文件
+                stopPlayMusic(); // 播放完成后释放当前播放器
+                playNextInQueue(context); // 递归调用,播放队列中的下一个
+            });
+
+            mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
+                Log.e(TAG, "onError: MediaPlayer error occurred: what=" + what + ", extra=" + extra + " for " + currentFilePath);
+                // 错误发生时,也要通知UI(如果需要),并尝试播放下一个
+                if (audioStatusUpdateListener != null)
+                    audioStatusUpdateListener.onFinishPlay(); // 通知当前文件播放失败
+                stopPlayMusic(); // 释放当前播放器
+                playNextInQueue(context); // 继续播放队列中的下一个
+                return true; // 表示已处理错误
+            });
+
+        } catch (IOException e) {
+            // IOException 通常是文件不存在、无法读取或URL无效
+            Log.e(TAG, "IOException during MediaPlayer setup for " + currentFilePath, e);
+            // 捕获异常后,也要尝试播放下一个
+            if (audioStatusUpdateListener != null)
+                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
+            stopPlayMusic(); // 释放当前播放器
+            playNextInQueue(context); // 继续播放队列中的下一个
+        } catch (IllegalArgumentException e) {
+            // IllegalArgumentException 可能是URI格式不正确等
+            Log.e(TAG, "IllegalArgumentException during MediaPlayer setup for " + currentFilePath, e);
+            if (audioStatusUpdateListener != null)
+                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
+            stopPlayMusic();
+            playNextInQueue(context);
+        } catch (SecurityException e) {
+            // SecurityException 可能是权限问题
+            Log.e(TAG, "SecurityException during MediaPlayer setup for " + currentFilePath, e);
+            if (audioStatusUpdateListener != null)
+                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
+            stopPlayMusic();
+            playNextInQueue(context);
         } catch (IllegalStateException e) {
+            // IllegalStateException 可能是 MediaPlayer 状态不正确
+            Log.e(TAG, "IllegalStateException during MediaPlayer setup for " + currentFilePath, e);
+            if (audioStatusUpdateListener != null)
+                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
+            stopPlayMusic();
+            playNextInQueue(context);
+        } catch (Exception e) { // 最后捕获所有其他未知异常
+            Log.e(TAG, "Generic Exception during MediaPlayer setup for " + currentFilePath, e);
             e.printStackTrace();
+            if (audioStatusUpdateListener != null)
+                audioStatusUpdateListener.onFinishPlay(); // 通知播放失败
+            stopPlayMusic();
+            playNextInQueue(context);
+        }
+    }
+    // --- 新增结束 ---
+
+
+    public void stopPlayMusic() {
+//        playQueue.clear();
+        isPlayingQueue = false;
+        if (mMediaPlayer == null) {
+            Log.d(TAG, "stopPlayMusic: MediaPlayer is null, nothing to stop.");
+            return;
+        }
+        try {
+            // 检查 MediaPlayer 是否处于可停止的状态
+            // 如果正在准备或处于错误状态,直接 release 可能更安全
+            if (mMediaPlayer.isPlaying()) {
+                mMediaPlayer.stop(); // 停止当前播放
+                Log.d(TAG, "stopPlayMusic: Stopped active MediaPlayer.");
+            }
+            mMediaPlayer.release(); // 释放资源
+            mMediaPlayer = null; // 置为 null,防止内存泄漏和引用旧对象
+            Log.d(TAG, "stopPlayMusic: MediaPlayer released and set to null.");
+        } catch (IllegalStateException e) {
+            // 捕获 MediaPlayer 状态异常,例如在 prepareAsync() 期间调用 stop()
+            Log.e(TAG, "stopPlayMusic: IllegalStateException during stop/release.", e);
+            // 即使异常,也要尝试释放并置null,防止后续问题
+            if (mMediaPlayer != null) {
+                mMediaPlayer.release();
+                mMediaPlayer = null;
+            }
+        } catch (Exception e) {
+            // 捕获其他未知异常
+            Log.e(TAG, "stopPlayMusic: General Exception during stop/release.", e);
+            if (mMediaPlayer != null) {
+                mMediaPlayer.release();
+                mMediaPlayer = null;
+            }
         }
     }
 
-    /**
-     * 获取文件长度
-     *
-     * @param context
-     * @param filePath
-     * @return
-     */
-    public int getMusicTime(Context context, String filePath) {
-        if (mMediaPlayer != null) {
-            stopPlayMusic();
-        }
-        mMediaPlayer = new MediaPlayer();
-        try {
-            mMediaPlayer.setDataSource(context, Uri.parse(filePath));
-            mMediaPlayer.prepare();
-            int duration = mMediaPlayer.getDuration();
-            if (0 != duration) {
-                mMediaPlayer.release();
-                return duration / 1000;
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return 0;
+    // 新增设置监听器的方法
+    public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) {
+        this.audioStatusUpdateListener = audioStatusUpdateListener;
     }
 
+
 }
\ No newline at end of file

--
Gitblit v1.7.1