android Button.setEnabled()关闭后开启不了的解决方法

本文介绍了一种在注册界面中实时检查输入框是否为空的方法,并通过定时器每秒更新按钮状态来确保用户只有在完整填写信息后才能提交。

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

注册界面,判断注册信息EditText框是否为空,空则使提交Button不能触发

private EditText ev1, ev2, ev3;
private Button btn1;
ev1 = (EditText) findViewById(R.id.et1);
ev2 = (EditText) findViewById(R.id.et2);
ev3 = (EditText) findViewById(R.id.et3);
btn1 = (Button) findViewById(R.id.login);

new Thread(){
<span style="white-space:pre">	</span>public void run() {	
<span style="white-space:pre">		</span>if (ev1.getText().toString().equals("")
<span style="white-space:pre">			</span>|| ev2.getText().toString().equals("")
<span style="white-space:pre">			</span>|| ev3.getText().toString().equals(""))
<span style="white-space:pre">				</span>btn1.setEnabled(false);
<span style="white-space:pre">		</span>else
<span style="white-space:pre">			</span>btn1.setEnabled(true);
<span style="white-space:pre">	</span>};
}.start();


其中一个信息为空时,Button设为不能触发。问题是,发现所有内容填完后仍不能触发。

解决办法,使用定时器启动,每1s检测一次信息框是否填好

private Handler handler2 = new Handler();
handler2.postDelayed(runnable3,1000);

Runnable runnable3 = new Runnable() {
		@Override
		public void run() {
			try{
				if (ev1.getText().toString().equals("")
						|| ev2.getText().toString().equals("")
						|| ev3.getText().toString().equals(""))
					btn1.setEnabled(false);
				else
					btn1.setEnabled(true);
				
				handler2.postDelayed(this, 1000);				
			}catch(Exception e){
				e.printStackTrace();
			}
			
		}
	};




package com.example.demoapplication; import android.Manifest; import android.content.pm.PackageManager; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.speech.tts.TextToSpeech; import android.util.Log; import android.widget.Button; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.LinkedList; import java.util.Locale; import java.util.Queue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { private static final String TAG = "AudioRecorder"; private Button startRecordButton, stopRecordButton; private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton; private AudioRecord audioRecord; // 音频配置 private static final int SAMPLE_RATE = 16000; private static final int BUFFER_SIZE; static { int minBufferSize = AudioRecord.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT ); BUFFER_SIZE = Math.max(minBufferSize, 4096); } // 线程和状态管理 private ScheduledExecutorService scheduler; private AtomicBoolean isRecording = new AtomicBoolean(false); private static final int PERMISSION_REQUEST_CODE = 1; private final ExecutorService executorService = Executors.newCachedThreadPool(); // 网络通信 private ServerSocket serverSocket; private volatile boolean isServerRunning = true; private volatile Socket clientSocket; private volatile BufferedWriter socketWriter; // TTS和播放控制 private TextToSpeech ttsEngine; private boolean isTtsInitialized = false; private AudioTrack audioTrack; // 录音队列和播放状态 private final Queue<byte[]> recordingQueue = new LinkedList<>(); private final Queue<byte[]> pausedQueue = new LinkedList<>(); private final Queue<byte[]> playbackQueue = new LinkedList<>(); // 使用原子变量确保状态一致性 private final AtomicBoolean isPlaying = new AtomicBoolean(false); private final AtomicBoolean isPaused = new AtomicBoolean(false); private volatile boolean isPlaybackThreadActive = false; // 音频轨道锁对象 private final Object audioTrackLock = new Object(); private final Object playbackQueueLock = new Object(); private final Object recordingQueueLock = new Object(); // 主线程Handler用于更新UI private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x11: // 客户端连接 Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show(); break; case 0x12: // 开始录音 Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show(); sendControlPacket("startRecorder"); playTts("开始录音"); break; case 0x14: // 停止录音 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); sendControlPacket("stopRecorder"); playTts("停止录音"); break; case 0x16: // 错误 Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show(); break; case 0x17: // 播放完成 Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); isPlaying.set(false); isPlaybackThreadActive = false; updatePlayButtonsState(); break; case 0x18: // 播放队列已添加 Toast.makeText(MainActivity.this, "已添加到播放队列", Toast.LENGTH_SHORT).show(); break; case 0x19: // 播放状态更新 updatePlayButtonsState(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化TTS引擎 ttsEngine = new TextToSpeech(this, this); initViews(); setupClickListeners(); checkPermissions(); startServer(30000); } private void initViews() { startRecordButton = findViewById(R.id.startRecordButton); stopRecordButton = findViewById(R.id.stopRecordButton); playSoundButton = findViewById(R.id.playSoundButton); pauseSoundButton = findViewById(R.id.pauseSoundButton); stopSoundButton = findViewById(R.id.stopSoundButton); resumeSoundButton = findViewById(R.id.resumeSoundButton); clearSoundsButton = findViewById(R.id.clearSoundsButton); // 初始按钮状态设置 stopRecordButton.setEnabled(false); pauseSoundButton.setEnabled(false); stopSoundButton.setEnabled(false); resumeSoundButton.setEnabled(false); } private void setupClickListeners() { startRecordButton.setOnClickListener(v -> startRecording()); stopRecordButton.setOnClickListener(v -> stopRecording()); playSoundButton.setOnClickListener(v -> addToPlaybackQueue()); pauseSoundButton.setOnClickListener(v -> pausePlayback()); stopSoundButton.setOnClickListener(v -> stopPlayback()); resumeSoundButton.setOnClickListener(v -> resumePlayback()); clearSoundsButton.setOnClickListener(v -> clearAllRecordings()); } // ==================== 录音功能实现 ==================== private void startRecording() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { sendErrorMessage("没有录音权限"); return; } if (isRecording.get()) { releaseAudioResources(); } try { // 初始化录音器 audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE ); if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { throw new IllegalStateException("AudioRecord 初始化失败"); } // 开始录音 audioRecord.startRecording(); isRecording.set(true); // 清空之前的录音数据 synchronized (recordingQueueLock) { recordingQueue.clear(); } pausedQueue.clear(); // 更新UI状态 startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); updatePlayButtonsState(); // 启动录音数据采集线程 if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdownNow(); } scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS); // 发送开始录音通知 handler.sendEmptyMessage(0x12); } catch (Exception e) { Log.e(TAG, "录音启动失败", e); sendErrorMessage("录音启动失败: " + e.getMessage()); releaseAudioResources(); } } private void stopRecording() { if (!isRecording.get()) return; isRecording.set(false); releaseAudioResources(); // 更新UI状态 stopRecordButton.setEnabled(false); startRecordButton.setEnabled(true); updatePlayButtonsState(); // 发送停止录音通知 handler.sendEmptyMessage(0x14); } // 采集音频数据并保存到队列 private void captureAudioData() { if (!isRecording.get() || audioRecord == null) return; byte[] buffer = new byte[BUFFER_SIZE]; try { int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); if (bytesRead > 0) { // 将录制的音频数据保存到队列 synchronized (recordingQueueLock) { recordingQueue.offer(buffer.clone()); } } } catch (Exception e) { Log.e(TAG, "音频采集失败", e); } } // ==================== 录音功能结束 ==================== // ==================== 播放功能实现 ==================== // 添加到播放队列 private void addToPlaybackQueue() { if (recordingQueue.isEmpty()) { Toast.makeText(this, "没有可播放的录音", Toast.LENGTH_SHORT).show(); return; } // 创建录音数据副本 Queue<byte[]> recordingCopy = new LinkedList<>(); synchronized (recordingQueueLock) { for (byte[] data : recordingQueue) { recordingCopy.offer(data.clone()); } } // 添加到播放队列 synchronized (playbackQueueLock) { playbackQueue.addAll(recordingCopy); } // 如果当前没有播放,立即开始播放 if (!isPlaybackThreadActive && !isPlaying.get()) { executorService.execute(this::playRecordingQueue); } else { handler.sendEmptyMessage(0x18); } } // 播放录音队列 - 修复后的方法 private void playRecordingQueue() { isPlaybackThreadActive = true; isPlaying.set(true); isPaused.set(false); handler.sendEmptyMessage(0x19); // 更新按钮状态 // 配置音频播放器 int bufferSize = AudioTrack.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT ); // 创建新的AudioTrack synchronized (audioTrackLock) { if (audioTrack != null) { try { audioTrack.stop(); audioTrack.release(); } catch (Exception e) { Log.e(TAG, "释放AudioTrack失败", e); } } try { audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM ); audioTrack.play(); } catch (IllegalStateException e) { Log.e(TAG, "创建AudioTrack失败", e); stopPlayback(); return; } } // 播放队列中的所有录音数据 while (isPlaying.get() && !playbackQueue.isEmpty()) { if (isPaused.get()) { // 暂停状态,等待恢复 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } continue; } byte[] audioData; synchronized (playbackQueueLock) { audioData = playbackQueue.poll(); } if (audioData != null) { synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getState() == AudioTrack.STATE_INITIALIZED) { try { audioTrack.write(audioData, 0, audioData.length); } catch (IllegalStateException e) { Log.e(TAG, "音频写入失败: " + e.getMessage()); break; } } else { Log.w(TAG, "AudioTrack不可用,停止播放"); break; } } } } // 播放完成 stopPlayback(); handler.sendEmptyMessage(0x17); } // 暂停播放 - 修复后的方法 private void pausePlayback() { if (!isPlaying.get() || isPaused.get()) return; isPaused.set(true); // 保存当前播放位置 synchronized (playbackQueueLock) { pausedQueue.clear(); pausedQueue.addAll(playbackQueue); playbackQueue.clear(); } // 暂停音频播放 synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { try { audioTrack.pause(); } catch (IllegalStateException e) { Log.e(TAG, "暂停播放失败: " + e.getMessage()); } } } handler.sendEmptyMessage(0x19); // 更新按钮状态 Toast.makeText(this, "播放已暂停", Toast.LENGTH_SHORT).show(); } // 继续播放 - 修复后的方法 private void resumePlayback() { if (!isPlaying.get() || !isPaused.get()) return; isPaused.set(false); // 恢复播放位置 synchronized (playbackQueueLock) { playbackQueue.clear(); playbackQueue.addAll(pausedQueue); pausedQueue.clear(); } // 恢复音频播放 synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) { try { audioTrack.play(); } catch (IllegalStateException e) { Log.e(TAG, "恢复播放失败: " + e.getMessage()); } } } handler.sendEmptyMessage(0x19); // 更新按钮状态 Toast.makeText(this, "继续播放", Toast.LENGTH_SHORT).show(); } // 停止播放 - 修复后的方法 private void stopPlayback() { isPlaying.set(false); isPaused.set(false); synchronized (audioTrackLock) { if (audioTrack != null) { try { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING || audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) { audioTrack.stop(); } audioTrack.release(); } catch (IllegalStateException e) { Log.e(TAG, "停止播放失败: " + e.getMessage()); } finally { audioTrack = null; } } } synchronized (playbackQueueLock) { playbackQueue.clear(); } pausedQueue.clear(); handler.sendEmptyMessage(0x19); // 更新按钮状态 Toast.makeText(this, "播放已停止", Toast.LENGTH_SHORT).show(); } // 清除所有录音 private void clearAllRecordings() { stopPlayback(); synchronized (recordingQueueLock) { recordingQueue.clear(); } pausedQueue.clear(); synchronized (playbackQueueLock) { playbackQueue.clear(); } handler.sendEmptyMessage(0x19); // 更新按钮状态 Toast.makeText(this, "所有录音已清除", Toast.LENGTH_SHORT).show(); } // ==================== 播放功能结束 ==================== // ==================== 辅助方法 ==================== // 更新播放按钮状态 private void updatePlayButtonsState() { runOnUiThread(() -> { boolean hasRecordings = !recordingQueue.isEmpty() || !pausedQueue.isEmpty(); boolean isPlayingState = isPlaying.get() && !isPaused.get(); playSoundButton.setEnabled(hasRecordings && !isPlayingState); pauseSoundButton.setEnabled(isPlayingState); stopSoundButton.setEnabled(isPlaying.get() || isPaused.get()); resumeSoundButton.setEnabled(isPlaying.get() && isPaused.get()); clearSoundsButton.setEnabled(hasRecordings); }); } // 播放TTS语音 private void playTts(String text) { if (isTtsInitialized) { ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null); } } // 释放音频资源 private void releaseAudioResources() { if (audioRecord != null) { try { if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); } audioRecord.release(); } catch (IllegalStateException e) { Log.e(TAG, "停止录音失败", e); } finally { audioRecord = null; } } if (scheduler != null) { try { scheduler.shutdownNow(); if (!scheduler.awaitTermination(500, TimeUnit.MILLISECONDS)) { Log.w(TAG, "录音线程池未正常关闭"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { scheduler = null; } } } // 发送控制包 private void sendControlPacket(String type) { if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) { return; } try { JSONObject packet = new JSONObject(); packet.put("type", type); synchronized (this) { if (socketWriter != null) { socketWriter.write(packet.toString()); socketWriter.write("\n\n"); socketWriter.flush(); } } } catch (Exception e) { Log.e(TAG, "发送控制指令失败", e); } } // 发送错误消息 private void sendErrorMessage(String message) { handler.obtainMessage(0x16, message).sendToTarget(); } // 检查权限 private void checkPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CODE); } } // 启动服务器 private void startServer(int port) { executorService.execute(() -> { try { serverSocket = new ServerSocket(port); Log.i(TAG, "服务器启动: " + port); while (isServerRunning) { try { Socket socket = serverSocket.accept(); clientSocket = socket; synchronized (this) { socketWriter = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); } handler.sendEmptyMessage(0x11); } catch (IOException e) { if (isServerRunning) Log.e(TAG, "接受连接失败", e); } } } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); runOnUiThread(() -> Toast.makeText(this, "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show()); } finally { closeServerSocket(); } }); } // 关闭服务器Socket private void closeServerSocket() { try { if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } } catch (IOException e) { Log.w(TAG, "关闭ServerSocket失败", e); } } // TTS初始化回调 @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { int result = ttsEngine.setLanguage(Locale.CHINESE); if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e(TAG, "TTS语言不支持中文"); } else { isTtsInitialized = true; } } } // 销毁活动时调用 @Override protected void onDestroy() { super.onDestroy(); isServerRunning = false; if (ttsEngine != null) { ttsEngine.stop(); ttsEngine.shutdown(); } closeServerSocket(); closeSocket(clientSocket); // 停止所有录音和播放 stopRecording(); stopPlayback(); // 优雅关闭线程池 executorService.shutdown(); try { if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } // 确保所有资源释放 releaseAudioResources(); } // 关闭Socket private void closeSocket(Socket socket) { try { if (socket != null && !socket.isClosed()) { socket.close(); } } catch (IOException e) { Log.w(TAG, "关闭Socket失败", e); } if (socket == clientSocket) { clientSocket = null; synchronized (this) { socketWriter = null; } } } } 使用时播放完录音还是闪退 播放完成我不想让app程序闪退
07-03
package com.example.demoapplication; // 定义包名 // 导入Android系统权限相关类 import android.Manifest; import android.content.pm.PackageManager; // 导入音频处理相关的类 import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; // 导入Android基础组件和工具类 import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.speech.tts.TextToSpeech; import android.util.Base64; import android.util.Log; import android.widget.Button; import android.widget.Toast; // 导入Android支持库相关类 import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; // 导入JSON处理库 import org.json.JSONException; import org.json.JSONObject; // 导入IO和网络相关类 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; // 导入数据结构和并发工具类 import java.util.LinkedList; import java.util.Locale; import java.util.Queue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; // 主活动类,继承自AppCompatActivity并实现TextToSpeech的初始化监听器 public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { private static final String TAG = "AudioRecorder"; // 日志标签 private Button startRecordButton, stopRecordButton; // 录音控制按钮 private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton; // 播放控制按钮 private AudioRecord audioRecord; // 音频录制对象 // 音频配置常量 private static final int SAMPLE_RATE = 16000; // 采样率 private static final int BUFFER_SIZE; // 缓冲区大小 // 静态代码块计算缓冲区大小 static { int minBufferSize = AudioRecord.getMinBufferSize( // 获取最小缓冲区大小 SAMPLE_RATE, // 采样率 AudioFormat.CHANNEL_IN_MONO, // 单声道输入 AudioFormat.ENCODING_PCM_16BIT // PCM 16位编码 ); BUFFER_SIZE = Math.max(minBufferSize, 4096); // 取最小缓冲区和4096中的较大值 } // 线程和状态管理 private ScheduledExecutorService scheduler; // 调度执行服务 private AtomicBoolean isRecording = new AtomicBoolean(false); // 原子布尔值表示是否正在录音 private static final int PERMISSION_REQUEST_CODE = 1; // 权限请求码 private final ExecutorService executorService = Executors.newCachedThreadPool(); // 执行服务 // 网络通信相关变量 private ServerSocket serverSocket; // 服务器Socket private volatile boolean isServerRunning = true; // 服务器运行状态 private volatile Socket clientSocket; // 客户端Socket private volatile BufferedWriter socketWriter; // Socket写入器 // TTS和播放控制 private TextToSpeech ttsEngine; // 文语转换引擎 private boolean isTtsInitialized = false; // TTS初始化状态 private AudioTrack audioTrack; // 音频播放轨道 // 录音队列和播放状态 private final Queue<byte[]> recordingQueue = new LinkedList<>(); // 录音数据队列 private final Queue<byte[]> pausedQueue = new LinkedList<>(); // 暂停时的数据队列 private final Queue<byte[]> playbackQueue = new LinkedList<>(); // 播放队列 private boolean isPlaying = false; // 是否正在播放 private boolean isPaused = false; // 是否已暂停 // 主线程Handler用于更新UI private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x11: // 客户端连接 Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show(); break; case 0x12: // 开始录音 Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show(); sendControlPacket("startRecorder"); // 发送开始录音控制包 playTts("开始录音"); // 播报语音提示 break; case 0x14: // 停止录音 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); sendControlPacket("stopRecorder"); // 发送停止录音控制包 playTts("停止录音"); // 播报语音提示 break; case 0x16: // 错误 Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show(); break; case 0x17: // 播放完成 Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); isPlaying = false; // 更新播放状态 updatePlayButtonsState(); // 更新按钮状态 processNextPlaybackItem(); // 处理下一个播放项 break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 设置布局文件 // 初始化TTS引擎 ttsEngine = new TextToSpeech(this, this); initViews(); // 初始化视图组件 setupClickListeners(); // 设置点击事件监听器 checkPermissions(); // 检查权限 startServer(30000); // 启动服务器 } // 初始化视图组件 private void initViews() { startRecordButton = findViewById(R.id.startRecordButton); // 绑定开始录音按钮 stopRecordButton = findViewById(R.id.stopRecordButton); // 绑定停止录音按钮 playSoundButton = findViewById(R.id.playSoundButton); // 绑定播放按钮 pauseSoundButton = findViewById(R.id.pauseSoundButton); // 绑定暂停按钮 stopSoundButton = findViewById(R.id.stopSoundButton); // 绑定停止播放按钮 resumeSoundButton = findViewById(R.id.resumeSoundButton); // 绑定继续播放按钮 clearSoundsButton = findViewById(R.id.clearSoundsButton); // 绑定清除录音按钮 // 初始按钮状态设置 stopRecordButton.setEnabled(false); pauseSoundButton.setEnabled(false); stopSoundButton.setEnabled(false); resumeSoundButton.setEnabled(false); } // 设置按钮点击事件监听器 private void setupClickListeners() { startRecordButton.setOnClickListener(v -> startRecording()); // 开始录音点击事件 stopRecordButton.setOnClickListener(v -> stopRecording()); // 停止录音点击事件 playSoundButton.setOnClickListener(v -> addToPlaybackQueue()); // 添加到播放队列点击事件 pauseSoundButton.setOnClickListener(v -> pausePlayback()); // 暂停播放点击事件 stopSoundButton.setOnClickListener(v -> stopPlayback()); // 停止播放点击事件 resumeSoundButton.setOnClickListener(v -> resumePlayback()); // 继续播放点击事件 clearSoundsButton.setOnClickListener(v -> clearAllRecordings()); // 清除录音点击事件 } // ==================== 录音功能实现 ==================== // 开始录音方法 private void startRecording() { // 检查录音权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { sendErrorMessage("没有录音权限"); return; } // 如果已经在录音,释放资源 if (isRecording.get()) { releaseAudioResources(); // 确保所有队列都清空后再重新初始化 synchronized (recordingQueue) { recordingQueue.clear(); } synchronized (playbackQueue) { playbackQueue.clear(); } pausedQueue.clear(); } try { // 初始化录音器 audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, // 麦克风作为音频源 SAMPLE_RATE, // 采样率 AudioFormat.CHANNEL_IN_MONO, // 单声道输入 AudioFormat.ENCODING_PCM_16BIT, // PCM 16位编码 BUFFER_SIZE // 缓冲区大小 ); // 检查录音器是否初始化成功 if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { throw new IllegalStateException("AudioRecord 初始化失败"); } // 开始录音 audioRecord.startRecording(); isRecording.set(true); // 更新录音状态 // 清空之前的录音数据(确保同步) synchronized (recordingQueue) { recordingQueue.clear(); } pausedQueue.clear(); // 更新UI状态 startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); updatePlayButtonsState(); // 启动录音数据采集线程 if (scheduler != null) { scheduler.shutdownNow(); // 关闭之前的调度器 } scheduler = Executors.newSingleThreadScheduledExecutor(); // 创建新的单线程调度器 scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS); // 每100毫秒采集一次音频数据 // 发送开始录音通知 handler.sendEmptyMessage(0x12); // 发送消息给Handler sendControlPacket("startRecorder"); // 发送开始录音控制包 playTts("开始录音"); // 播报语音提示 } catch (Exception e) { Log.e(TAG, "录音启动失败", e); // 记录错误日志 sendErrorMessage("录音启动失败: " + e.getMessage()); // 发送错误信息 releaseAudioResources(); // 释放音频资源 } } // 停止录音方法 private void stopRecording() { if (!isRecording.get()) return; // 如果不在录音直接返回 isRecording.set(false); // 更新录音状态 releaseAudioResources(); // 释放音频资源 // 更新UI状态 stopRecordButton.setEnabled(false); startRecordButton.setEnabled(true); updatePlayButtonsState(); // 发送停止录音通知 handler.sendEmptyMessage(0x14); // 发送消息给Handler sendControlPacket("stopRecorder"); // 发送停止录音控制包 playTts("停止录音"); // 播报语音提示 } // 采集音频数据并保存到队列 private void captureAudioData() { if (!isRecording.get() || audioRecord == null) return; // 如果不在录音或录音器为空直接返回 byte[] buffer = new byte[BUFFER_SIZE]; // 创建缓冲区 try { int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); // 读取音频数据 if (bytesRead > 0) { // 将录制的音频数据保存到队列 synchronized (recordingQueue) { // 同步操作 recordingQueue.offer(buffer.clone()); // 添加拷贝到队列中 } } } catch (Exception e) { Log.e(TAG, "音频采集失败", e); // 记录错误日志 } } // ==================== 录音功能结束 ==================== // ==================== 播放功能实现 ==================== // 添加到播放队列 private void addToPlaybackQueue() { if (recordingQueue.isEmpty()) { // 如果队列为空 Toast.makeText(this, "没有可播放的录音", Toast.LENGTH_SHORT).show(); // 显示提示 return; } // 创建录音数据副本 Queue<byte[]> recordingCopy = new LinkedList<>(); synchronized (recordingQueue) { for (byte[] data : recordingQueue) { recordingCopy.offer(data.clone()); } } // 添加到播放队列 synchronized (playbackQueue) { playbackQueue.addAll(recordingCopy); } // 如果当前没有播放,立即开始播放 if (!isPlaying && !isPaused) { executorService.execute(this::playRecordingQueue); } else { Toast.makeText(this, "已添加到播放队列", Toast.LENGTH_SHORT).show(); } } // 播放录音队列 private void playRecordingQueue() { isPlaying = true; // 更新播放状态 isPaused = false; // 更新暂停状态 updatePlayButtonsState(); // 更新按钮状态 // 配置音频播放器 int bufferSize = AudioTrack.getMinBufferSize( // 获取最小缓冲区大小 SAMPLE_RATE, // 采样率 AudioFormat.CHANNEL_OUT_MONO, // 单声道输出 AudioFormat.ENCODING_PCM_16BIT // PCM 16位编码 ); // 创建音频播放器 audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, // 音乐流类型 SAMPLE_RATE, // 采样率 AudioFormat.CHANNEL_OUT_MONO, // 单声道输出 AudioFormat.ENCODING_PCM_16BIT, // PCM 16位编码 bufferSize, // 缓冲区大小 AudioTrack.MODE_STREAM // 流模式 ); audioTrack.play(); // 开始播放 // 播放队列中的所有录音数据 while (isPlaying && !playbackQueue.isEmpty()) { // 当正在播放且队列不为空时循环 if (isPaused) { // 如果处于暂停状态 // 暂停状态,等待恢复 try { Thread.sleep(100); // 等待100毫秒 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 中断线程 } continue; // 继续循环 } byte[] audioData; // 音频数据 synchronized (playbackQueue) { // 同步操作 audioData = playbackQueue.poll(); // 从队列取出数据 } if (audioData != null) { // 如果有数据 if (audioTrack != null) { // 确保audioTrack未被释放 audioTrack.write(audioData, 0, audioData.length); // 写入音频数据 } } } // 播放完成 stopPlayback(); // 停止播放 handler.sendEmptyMessage(0x17); // 发送播放完成消息 } // 处理下一个播放项 private void processNextPlaybackItem() { if (!playbackQueue.isEmpty()) { executorService.execute(this::playRecordingQueue); } } // 暂停播放 private void pausePlayback() { if (!isPlaying || isPaused) return; // 如果不在播放或已经暂停直接返回 isPaused = true; // 更新暂停状态 // 保存当前播放位置 synchronized (playbackQueue) { // 同步操作 pausedQueue.clear(); // 清空暂停队列 pausedQueue.addAll(playbackQueue); // 将播放队列添加到暂停队列 playbackQueue.clear(); // 清空播放队列 } updatePlayButtonsState(); // 更新按钮状态 Toast.makeText(this, "播放已暂停", Toast.LENGTH_SHORT).show(); // 显示提示 } // 继续播放 private void resumePlayback() { if (!isPlaying || !isPaused) return; // 如果不在播放或未暂停直接返回 isPaused = false; // 更新暂停状态 // 恢复播放位置 synchronized (playbackQueue) { // 同步操作 playbackQueue.clear(); // 清空播放队列 playbackQueue.addAll(pausedQueue); // 将暂停队列添加到播放队列 pausedQueue.clear(); // 清空暂停队列 } updatePlayButtonsState(); // 更新按钮状态 Toast.makeText(this, "继续播放", Toast.LENGTH_SHORT).show(); // 显示提示 // 启动播放线程 executorService.execute(this::playRecordingQueue); // 在线程池中执行播放任务 } // 停止播放 private void stopPlayback() { isPlaying = false; // 更新播放状态 isPaused = false; // 更新暂停状态 if (audioTrack != null) { // 如果音频轨道存在 try { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { // 如果正在播放 audioTrack.stop(); // 停止播放 } audioTrack.release(); // 释放资源 } catch (Exception e) { Log.e(TAG, "释放AudioTrack失败", e); // 记录错误日志 } audioTrack = null; // 置空引用 } synchronized (playbackQueue) { playbackQueue.clear(); // 清空播放队列 pausedQueue.clear(); // 清空暂停队列 } updatePlayButtonsState(); // 更新按钮状态 Toast.makeText(this, "播放已停止", Toast.LENGTH_SHORT).show(); // 显示提示 } // 清除所有录音 private void clearAllRecordings() { stopPlayback(); // 停止播放 synchronized (recordingQueue) { // 同步操作 recordingQueue.clear(); // 清空录音队列 pausedQueue.clear(); // 清空暂停队列 } synchronized (playbackQueue) { playbackQueue.clear(); // 清空播放队列 } updatePlayButtonsState(); // 更新按钮状态 Toast.makeText(this, "所有录音已清除", Toast.LENGTH_SHORT).show(); // 显示提示 } // ==================== 播放功能结束 ==================== // ==================== 辅助方法 ==================== // 更新播放按钮状态 private void updatePlayButtonsState() { runOnUiThread(() -> { // 在主线程执行 boolean hasRecordings = !recordingQueue.isEmpty() || !pausedQueue.isEmpty(); // 是否有录音 boolean isPlayingState = isPlaying && !isPaused; // 是否正在播放 playSoundButton.setEnabled(hasRecordings && !isPlayingState); // 设置播放按钮状态 pauseSoundButton.setEnabled(isPlayingState); // 设置暂停按钮状态 stopSoundButton.setEnabled(isPlaying); // 设置停止按钮状态 resumeSoundButton.setEnabled(isPlaying && isPaused); // 设置继续按钮状态 clearSoundsButton.setEnabled(hasRecordings); // 设置清除按钮状态 }); } // 播放TTS语音 private void playTts(String text) { if (isTtsInitialized) { // 如果TTS已初始化 ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null); // 播报文本 } } // 释放音频资源 private void releaseAudioResources() { if (audioRecord != null) { // 如果录音器存在 try { if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { // 如果正在录音 audioRecord.stop(); // 停止录音 } } catch (IllegalStateException e) { Log.e(TAG, "停止录音失败", e); // 记录错误日志 } audioRecord.release(); // 释放资源 audioRecord = null; // 置空引用 } if (scheduler != null) { // 如果调度器存在 scheduler.shutdownNow(); // 关闭调度器 scheduler = null; // 置空引用 } } // 发送控制包 private void sendControlPacket(String type) { if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) { // 如果连接无效 return; // 直接返回 } try { JSONObject packet = new JSONObject(); // 创建JSON对象 packet.put("type", type); // 添加类型字段 synchronized (this) { // 同步操作 if (socketWriter != null) { // 如果写入器存在 socketWriter.write(packet.toString()); // 写入JSON字符串 socketWriter.write("\n\n"); // 写入两个换行符 socketWriter.flush(); // 刷新缓冲区 } } } catch (Exception e) { Log.e(TAG, "发送控制指令失败", e); // 记录错误日志 } } // 发送错误消息 private void sendErrorMessage(String message) { handler.obtainMessage(0x16, message).sendToTarget(); // 发送错误消息 } // 检查权限 private void checkPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) // 检查录音权限 != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, // 请求权限 new String[]{Manifest.permission.RECORD_AUDIO}, // 权限数组 PERMISSION_REQUEST_CODE); // 请求码 } } // 启动服务器 private void startServer(int port) { executorService.execute(() -> { // 在线程池中执行 try { serverSocket = new ServerSocket(port); // 创建服务器Socket Log.i(TAG, "服务器启动: " + port); // 记录日志 while (isServerRunning) { // 当服务器运行时循环 try { Socket socket = serverSocket.accept(); // 接受连接 clientSocket = socket; // 保存客户端Socket synchronized (this) { // 同步操作 socketWriter = new BufferedWriter( // 创建缓冲写入器 new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); // 使用UTF-8编码 } handler.sendEmptyMessage(0x11); // 发送客户端连接消息 executorService.execute(() -> startCommunication(socket)); // 启动通信线程 } catch (IOException e) { if (isServerRunning) Log.e(TAG, "接受连接失败", e); // 记录错误日志 } } } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); // 记录错误日志 runOnUiThread(() -> Toast.makeText(this, // 显示提示 "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show()); } finally { closeServerSocket(); // 关闭服务器Socket } }); } // 关闭服务器Socket(空实现) private void closeServerSocket() { } // 开始通信(空实现) private void startCommunication(Socket socket) { } // 其他网络通信相关方法保持不变... // 为了简洁省略了部分网络通信代码,实际使用时需要完整实现 // TTS初始化回调 @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { // 如果初始化成功 int result = ttsEngine.setLanguage(Locale.CHINESE); // 设置中文语言 if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { // 如果不支持 Log.e(TAG, "TTS语言不支持中文"); // 记录日志 } else { isTtsInitialized = true; // 更新初始化状态 } } } // 销毁活动时调用 @Override protected void onDestroy() { super.onDestroy(); // 调用父类方法 isServerRunning = false; // 停止服务器 if (ttsEngine != null) { // 如果TTS引擎存在 ttsEngine.stop(); // 停止TTS ttsEngine.shutdown(); // 关闭TTS } closeServerSocket(); // 关闭服务器Socket closeSocket(clientSocket); // 关闭客户端Socket executorService.shutdownNow(); // 关闭线程池 releaseAudioResources(); // 释放音频资源 stopPlayback(); // 停止播放 } // 关闭Socket(空实现) private void closeSocket(Socket clientSocket) { } } 解决播放完录音app就会闪退
07-03
package com.example.demoapplication; // 定义包名 // 导入Android音频相关的类 import android.media.AudioTrack; import android.Manifest; import android.content.pm.PackageManager; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.MediaRecorder; // 导入Android系统组件相关类 import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; // 导入语音合成相关类 import android.speech.tts.TextToSpeech; // 导入Android工具类 import android.util.Base64; import android.util.Log; // 导入Android UI组件 import android.widget.Button; import android.widget.Toast; // 导入Android兼容库组件 import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; // 导入JSON处理库 import org.json.JSONException; import org.json.JSONObject; // 导入IO流相关类 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; // 导入网络通信相关类 import java.net.ServerSocket; import java.net.Socket; // 导入数据结构和并发工具 import java.util.LinkedList; import java.util.Locale; import java.util.Queue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * 主活动类,实现音频录制、播放、网络通信和TTS功能 */ public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { private static final String TAG = "AudioRecorder"; // 日志标签 // 按钮成员变量 private Button startRecordButton, stopRecordButton; private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton; // 音频录制对象 private AudioRecord audioRecord; // 音频采样率常量 private static final int SAMPLE_RATE = 16000; // 缓冲区大小常量 private static final int BUFFER_SIZE; // 静态代码块初始化缓冲区大小 static { // 获取最小缓冲区大小 int minBufferSize = AudioRecord.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT ); // 取较大值作为实际使用的缓冲区大小 BUFFER_SIZE = Math.max(minBufferSize, 4096); } // 调度执行器 private ScheduledExecutorService scheduler; // 录音状态标志 private AtomicBoolean isRecording = new AtomicBoolean(false); // 权限请求码 private static final int PERMISSION_REQUEST_CODE = 1; // 线程池服务 private final ExecutorService executorService = Executors.newCachedThreadPool(); // 服务器套接字 private ServerSocket serverSocket; // 服务器运行状态 private volatile boolean isServerRunning = true; // 客户端套接字 private volatile Socket clientSocket; // 套接字写入器 private volatile BufferedWriter socketWriter; // 文语转换引擎 private TextToSpeech ttsEngine; // TTS初始化状态 private boolean isTtsInitialized = false; // 播放队列相关变量 private final Queue<byte[]> playbackQueue = new LinkedList<>(); private final Queue<byte[]> pausedQueue = new LinkedList<>(); private final AtomicBoolean isPlaying = new AtomicBoolean(false); private final AtomicBoolean isPaused = new AtomicBoolean(false); private volatile boolean isPlaybackThreadActive = false; // ... existing code ... // 对象锁用于同步访问 private final Object audioTrackLock = new Object(); private final Object playbackQueueLock = new Object(); private final Object recordingQueueLock = new Object(); // 新增的成员变量 - 音频播放器 private AudioTrack audioTrack; // 新增的成员变量 // ... existing code ... // 录制音频数据队列 private final Queue<byte[]> recordingQueue = new LinkedList<>(); // 主线程消息处理器 private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x11: // 客户端连接 Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show(); break; case 0x12: // 开始录音 Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("startRecorder", null); playTts("开始录音"); break; case 0x14: // 停止录音 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("stopRecorder", null); playTts("停止录音"); break; case 0x16: // 错误消息 Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show(); break; case 0x17: // 播放完成 Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); isPlaying.set(false); isPlaybackThreadActive = false; updatePlayButtonsState(); break; case 0x18: // 添加到播放队列 Toast.makeText(MainActivity.this, "已添加到播放队列", Toast.LENGTH_SHORT).show(); break; case 0x19: // 更新播放按钮状态 updatePlayButtonsState(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 调用父类onCreate方法 setContentView(R.layout.activity_main); // 设置布局文件 // 初始化TTS引擎 ttsEngine = new TextToSpeech(this, this); // 初始化视图组件 initViews(); // 设置点击监听器 setupClickListeners(); // 检查权限 checkPermissions(); // 启动服务器(端口30000) startServer(30000); // 启动套接字监听 startSocketListener(); } // 初始化视图组件 private void initViews() { // 绑定按钮控件 startRecordButton = findViewById(R.id.startRecordButton); stopRecordButton = findViewById(R.id.stopRecordButton); playSoundButton = findViewById(R.id.playSoundButton); pauseSoundButton = findViewById(R.id.pauseSoundButton); stopSoundButton = findViewById(R.id.stopSoundButton); resumeSoundButton = findViewById(R.id.resumeSoundButton); clearSoundsButton = findViewById(R.id.clearSoundsButton); // 初始化按钮状态 stopRecordButton.setEnabled(false); pauseSoundButton.setEnabled(false); stopSoundButton.setEnabled(false); resumeSoundButton.setEnabled(false); } // 设置点击事件监听器 private void setupClickListeners() { // 录音控制 startRecordButton.setOnClickListener(v -> startRecording()); stopRecordButton.setOnClickListener(v -> stopRecording()); // 播放控制 playSoundButton.setOnClickListener(v -> playNextInQueue()); pauseSoundButton.setOnClickListener(v -> pausePlayback()); stopSoundButton.setOnClickListener(v -> stopPlayback()); resumeSoundButton.setOnClickListener(v -> resumePlayback()); clearSoundsButton.setOnClickListener(v -> { clearPlaybackQueue(); handler.sendEmptyMessage(0x19); }); } // ==================== 录音功能 ==================== // 开始录音 private void startRecording() { // 检查录音权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { sendErrorMessage("没有录音权限"); return; } // 如果已经在录音则先释放资源 if (isRecording.get()) { releaseAudioResources(); } try { // 创建AudioRecord对象 audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE ); // 检查初始化状态 if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { throw new IllegalStateException("AudioRecord 初始化失败"); } // 开始录音 audioRecord.startRecording(); isRecording.set(true); startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); updatePlayButtonsState(); // 关闭之前的调度器 if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdownNow(); } // 创建新的调度器 scheduler = Executors.newSingleThreadScheduledExecutor(); // 每100毫秒采集一次音频数据 scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS); handler.sendEmptyMessage(0x12); } catch (Exception e) { Log.e(TAG, "录音启动失败", e); sendErrorMessage("录音启动失败: " + e.getMessage()); releaseAudioResources(); } } // 停止录音 private void stopRecording() { if (!isRecording.get()) return; isRecording.set(false); releaseAudioResources(); stopRecordButton.setEnabled(false); startRecordButton.setEnabled(true); updatePlayButtonsState(); handler.sendEmptyMessage(0x14); } // 采集音频数据 private void captureAudioData() { if (!isRecording.get() || audioRecord == null) return; byte[] buffer = new byte[BUFFER_SIZE]; try { int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); if (bytesRead > 0) { synchronized (recordingQueueLock) { recordingQueue.offer(buffer.clone()); } String base64Data = Base64.encodeToString(buffer, Base64.DEFAULT); sendJsonPacket("recording", base64Data); } } catch (Exception e) { Log.e(TAG, "音频采集失败", e); } } // ==================== 录音功能结束 ==================== // ==================== 播放队列管理 ==================== // 将声音数据添加到播放队列 private void addSoundToQueue(byte[] soundData) { synchronized (playbackQueueLock) { playbackQueue.offer(soundData); } handler.sendEmptyMessage(0x18); // 如果没有在播放且没有激活的播放线程,则开始播放 if (!isPlaying.get() && !isPlaybackThreadActive) { handler.post(this::playNextInQueue); } } // 播放下一个队列中的音频 private void playNextInQueue() { if (isPlaybackThreadActive) { return; } isPlaybackThreadActive = true; new Thread(() -> { try { while (!playbackQueue.isEmpty() && !isPaused.get()) { byte[] soundData; synchronized (playbackQueueLock) { soundData = playbackQueue.poll(); } if (soundData != null) { playSoundDirectly(soundData); } } if (!isPaused.get()) { isPlaying.set(false); isPlaybackThreadActive = false; handler.sendEmptyMessage(0x17); } } catch (Exception e) { Log.e(TAG, "播放队列处理失败", e); sendErrorMessage("播放失败: " + e.getMessage()); } }).start(); } // 直接播放音频 private void playSoundDirectly(byte[] soundData) { if (soundData == null || soundData.length == 0) { return; } try { isPlaying.set(true); isPaused.set(false); synchronized (audioTrackLock) { int bufferSize = AudioTrack.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT ); if (audioTrack != null) { audioTrack.release(); } audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM ); if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { throw new IllegalStateException("AudioTrack 初始化失败"); } audioTrack.play(); ByteArrayInputStream inputStream = new ByteArrayInputStream(soundData); byte[] buffer = new byte[bufferSize]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1 && !isPaused.get()) { audioTrack.write(buffer, 0, bytesRead); } audioTrack.stop(); audioTrack.release(); audioTrack = null; if (!isPaused.get()) { runOnUiThread(this::updatePlayButtonsState); } } } catch (Exception e) { Log.e(TAG, "音频播放失败", e); sendErrorMessage("播放失败: " + e.getMessage()); } } // 暂停播放 private void pausePlayback() { if (!isPlaying.get() || isPaused.get()) { return; } isPaused.set(true); synchronized (audioTrackLock) { if (audioTrack != null) { audioTrack.pause(); } } // 将当前播放的数据移到暂停队列 byte[] currentData = null; synchronized (playbackQueueLock) { if (!playbackQueue.isEmpty()) { currentData = playbackQueue.poll(); } } if (currentData != null) { synchronized (playbackQueueLock) { pausedQueue.offer(currentData); } } updatePlayButtonsState(); } // 恢复播放 private void resumePlayback() { if (!isPaused.get()) { return; } isPaused.set(false); // 将暂停的数据移回播放队列 byte[] pausedData = null; synchronized (playbackQueueLock) { if (!pausedQueue.isEmpty()) { pausedData = pausedQueue.poll(); } } if (pausedData != null) { synchronized (playbackQueueLock) { playbackQueue.offer(pausedData); } } synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) { audioTrack.play(); } } if (!isPlaybackThreadActive) { new Thread(() -> { isPlaybackThreadActive = true; try { while (!playbackQueue.isEmpty() && !isPaused.get()) { byte[] soundData; synchronized (playbackQueueLock) { soundData = playbackQueue.poll(); } if (soundData != null) { playSoundDirectly(soundData); } } if (!isPaused.get()) { isPlaying.set(false); isPlaybackThreadActive = false; handler.sendEmptyMessage(0x17); } } catch (Exception e) { Log.e(TAG, "恢复播放失败", e); sendErrorMessage("恢复播放失败: " + e.getMessage()); } }).start(); } updatePlayButtonsState(); } // 停止播放 private void stopPlayback() { if (!isPlaying.get() && !isPaused.get()) { return; } isPlaying.set(false); isPaused.set(false); synchronized (playbackQueueLock) { playbackQueue.clear(); if (!pausedQueue.isEmpty()) { pausedQueue.clear(); } } synchronized (audioTrackLock) { if (audioTrack != null) { try { audioTrack.stop(); audioTrack.release(); } catch (Exception e) { Log.e(TAG, "停止音频播放失败", e); } finally { audioTrack = null; } } } updatePlayButtonsState(); handler.sendEmptyMessage(0x19); } // 清空播放队列 private void clearPlaybackQueue() { synchronized (playbackQueueLock) { playbackQueue.clear(); pausedQueue.clear(); } } // ==================== 播放队列管理结束 ==================== // ==================== 辅助方法 ==================== // 更新播放按钮状态 private void updatePlayButtonsState() { runOnUiThread(() -> { boolean hasRecordings = !playbackQueue.isEmpty() || !pausedQueue.isEmpty(); boolean isPlayingState = isPlaying.get() && !isPaused.get(); boolean isPausedState = isPaused.get(); playSoundButton.setEnabled(hasRecordings && !isPlayingState); pauseSoundButton.setEnabled(isPlayingState); stopSoundButton.setEnabled(isPlaying.get() || isPausedState); resumeSoundButton.setEnabled(isPausedState); clearSoundsButton.setEnabled(hasRecordings); }); } // 播放文本转语音 private void playTts(String text) { if (isTtsInitialized) { ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null); } } // 释放音频资源 private void releaseAudioResources() { if (audioRecord != null) { try { if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); } audioRecord.release(); } catch (IllegalStateException e) { Log.e(TAG, "停止录音失败", e); } finally { audioRecord = null; } } if (scheduler != null) { try { scheduler.shutdownNow(); if (!scheduler.awaitTermination(500, TimeUnit.MILLISECONDS)) { Log.w(TAG, "录音线程池未正常关闭"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { scheduler = null; } } } // 发送JSON数据包 private void sendJsonPacket(String type, Object data) { if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) { return; } try { JSONObject packet = new JSONObject(); packet.put("type", type); if (data != null) { packet.put("data", data); } synchronized (this) { if (socketWriter != null) { socketWriter.write(packet.toString()); socketWriter.write("\n\n"); socketWriter.flush(); } } } catch (Exception e) { Log.e(TAG, "发送数据包失败: " + type, e); } } // 发送错误消息 private void sendErrorMessage(String message) { handler.obtainMessage(0x16, message).sendToTarget(); } // 检查权限 private void checkPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CODE ); } } // 启动服务器 private void startServer(int port) { executorService.execute(() -> { try { serverSocket = new ServerSocket(port); Log.i(TAG, "服务器启动: " + port); while (isServerRunning) { try { Socket socket = serverSocket.accept(); clientSocket = socket; synchronized (this) { socketWriter = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8") ); } handler.sendEmptyMessage(0x11); } catch (IOException e) { if (isServerRunning) Log.e(TAG, "接受连接失败", e); } } } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); runOnUiThread(() -> Toast.makeText( this, "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG ).show()); } finally { closeServerSocket(); } }); } // 启动套接字监听 private void startSocketListener() { executorService.execute(() -> { while (true) { if (clientSocket != null && !clientSocket.isClosed()) { try { BufferedReader reader = new BufferedReader( new InputStreamReader(clientSocket.getInputStream(), "UTF-8") ); StringBuilder packetBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { if (line.isEmpty()) { if (packetBuilder.length() > 0) { String packet = packetBuilder.toString(); Log.d(TAG, "收到数据包: " + packet); try { JSONObject command = new JSONObject(packet); String type = command.getString("type"); switch (type) { case "playSound": String base64Sound = command.optString("data"); if (base64Sound != null && !base64Sound.isEmpty()) { byte[] soundData = Base64.decode(base64Sound, Base64.DEFAULT); addSoundToQueue(soundData); handler.sendEmptyMessage(0x18); } break; case "pauseSound": pausePlayback(); break; case "stopSound": stopPlayback(); break; case "resumeSound": resumePlayback(); break; case "clearSounds": clearPlaybackQueue(); handler.sendEmptyMessage(0x19); break; } } catch (JSONException e) { Log.e(TAG, "JSON解析失败", e); } packetBuilder.setLength(0); } } else { packetBuilder.append(line); } } } catch (IOException e) { Log.e(TAG, "Socket读取失败", e); } } else { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }); } // 关闭服务器套接字 private void closeServerSocket() { try { if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } } catch (IOException e) { Log.w(TAG, "关闭ServerSocket失败", e); } } // TTS初始化回调 @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { int result = ttsEngine.setLanguage(Locale.CHINESE); if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e(TAG, "TTS语言不支持中文"); } else { isTtsInitialized = true; } } } // 销毁活动时清理资源 @Override protected void onDestroy() { super.onDestroy(); isServerRunning = false; if (ttsEngine != null) { ttsEngine.stop(); ttsEngine.shutdown(); } closeServerSocket(); closeSocket(clientSocket); stopRecording(); executorService.shutdown(); try { if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } releaseAudioResources(); } // 关闭套接字 private void closeSocket(Socket socket) { try { if (socket != null && !socket.isClosed()) { socket.close(); } } catch (IOException e) { Log.w(TAG, "关闭Socket失败", e); } if (socket == clientSocket) { clientSocket = null; synchronized (this) { socketWriter = null; } } } } 我需要修改当前代码常按开始录音就会录音,松开按钮就会结束录音,类似于微信那种功能包括修改xml文件
最新发布
07-22
package com.example.demoapplication; import android.Manifest; // 权限相关 import android.content.pm.PackageManager; // 包管理相关 import android.media.AudioFormat; // 音频格式定义 import android.media.AudioManager; // 音频管理 import android.media.AudioRecord; // 音频录制 import android.media.AudioTrack; // 音频播放 import android.media.MediaRecorder; // 媒体录制 import android.os.Bundle; // 数据存储 import android.os.Handler; // 消息处理 import android.os.Looper; // 主线程消息循环 import android.os.Message; // 消息对象 import android.speech.tts.TextToSpeech; // 文字转语音 import android.util.Base64; // Base64编码解码 import android.util.Log; // 日志工具 import android.widget.Button; // 按钮控件 import android.widget.Toast; // 短时提示 import androidx.annotation.NonNull; // 非空注解 import androidx.appcompat.app.AppCompatActivity; // 主活动基类 import androidx.core.app.ActivityCompat; // 权限兼容处理 import androidx.core.content.ContextCompat; // 上下文工具 import org.json.JSONException; // JSON异常 import org.json.JSONObject; // JSON对象 import java.io.BufferedReader; import java.io.BufferedWriter; // 缓冲写入 import java.io.ByteArrayInputStream; import java.io.IOException; // IO异常 import java.io.InputStreamReader; import java.io.OutputStreamWriter; // 输出流写入 import java.net.ServerSocket; // 服务器Socket import java.net.Socket; // 客户端Socket import java.util.LinkedList; // 链表结构 import java.util.Locale; // 地区信息 import java.util.Queue; // 队列结构 import java.util.concurrent.ExecutorService; // 线程池服务 import java.util.concurrent.Executors; // 线程池工厂 import java.util.concurrent.ScheduledExecutorService; // 定时任务执行器 import java.util.concurrent.TimeUnit; // 时间单位 import java.util.concurrent.atomic.AtomicBoolean; // 原子布尔值 /** * 主活动类,实现录音、播放、网络通信和TTS功能 */ public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener { // 日志标签 private static final String TAG = "AudioRecorder"; // UI控件 private Button startRecordButton, stopRecordButton; private Button playSoundButton, pauseSoundButton, stopSoundButton, resumeSoundButton, clearSoundsButton; private AudioRecord audioRecord; // 音频录制对象 // 音频配置常量 private static final int SAMPLE_RATE = 16000; // 采样率 private static final int BUFFER_SIZE; // 缓冲区大小 // 静态代码块计算缓冲区大小 static { int minBufferSize = AudioRecord.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT ); BUFFER_SIZE = Math.max(minBufferSize, 4096); } // 线程和状态管理 private ScheduledExecutorService scheduler; // 定时任务执行器 private AtomicBoolean isRecording = new AtomicBoolean(false); // 录音状态 private static final int PERMISSION_REQUEST_CODE = 1; // 权限请求码 private final ExecutorService executorService = Executors.newCachedThreadPool(); // 线程池 // 网络通信相关变量 private ServerSocket serverSocket; // 服务器Socket private volatile boolean isServerRunning = true; // 服务器运行状态 private volatile Socket clientSocket; // 客户端Socket private volatile BufferedWriter socketWriter; // Socket写入流 // TTS和音频播放控制 private TextToSpeech ttsEngine; // 文字转语音引擎 private boolean isTtsInitialized = false; // TTS初始化状态 private AudioTrack audioTrack; // 音频播放轨道 // 队列用于存储录音数据 private final Queue<byte[]> recordingQueue = new LinkedList<>(); // 正常录音队列 private final Queue<byte[]> pausedQueue = new LinkedList<>(); // 暂停队列 private final Queue<byte[]> playbackQueue = new LinkedList<>(); // 播放队列 // 原子变量确保线程安全的状态管理 private final AtomicBoolean isPlaying = new AtomicBoolean(false); // 播放状态 private final AtomicBoolean isPaused = new AtomicBoolean(false); // 暂停状态 private volatile boolean isPlaybackThreadActive = false; // 播放线程活跃状态 // 锁对象用于同步访问共享资源 private final Object audioTrackLock = new Object(); // 音频轨道锁 private final Object playbackQueueLock = new Object(); // 播放队列锁 private final Object recordingQueueLock = new Object(); // 录音队列锁 // 主线程Handler用于更新UI private final Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case 0x11: // 客户端连接 Toast.makeText(MainActivity.this, "客户端已连接", Toast.LENGTH_SHORT).show(); break; case 0x12: // 开始录音 Toast.makeText(MainActivity.this, "开始录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("startRecorder", null); playTts("开始录音"); break; case 0x14: // 停止录音 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); sendJsonPacket("stopRecorder", null); playTts("停止录音"); break; case 0x16: // 错误 Toast.makeText(MainActivity.this, "错误: " + msg.obj, Toast.LENGTH_LONG).show(); break; case 0x17: // 播放完成 Toast.makeText(MainActivity.this, "播放完成", Toast.LENGTH_SHORT).show(); isPlaying.set(false); isPlaybackThreadActive = false; updatePlayButtonsState(); break; case 0x18: // 播放队列已添加 Toast.makeText(MainActivity.this, "已添加到播放队列", Toast.LENGTH_SHORT).show(); break; case 0x19: // 播放状态更新 updatePlayButtonsState(); break; case 0x20: // 播放暂停 sendJsonPacket("pauseSound", null); playTts("播放暂停"); break; case 0x21: // 播放停止 sendJsonPacket("stopSound", null); playTts("播放停止"); break; case 0x22: // 播放恢复 sendJsonPacket("resumeSound", null); playTts("继续播放"); break; case 0x23: // 清空声音 sendJsonPacket("clearSounds", null); playTts("清空所有录音"); break; case 0x24: // 接收到新的播放指令 String base64Data = (String) msg.obj; try { byte[] decodedData = Base64.decode(base64Data, Base64.DEFAULT); addBase64ToPlaybackQueue(decodedData); } catch (IllegalArgumentException e) { Log.e(TAG, "Base64解码失败", e); sendErrorMessage("无效的Base64数据"); } break; } } }; /** * Activity创建方法 * @param savedInstanceState 保存的状态 */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化TTS引擎 ttsEngine = new TextToSpeech(this, this); initViews(); setupClickListeners(); checkPermissions(); startServer(30000); startSocketListener(); // 启动Socket监听 } /** * 初始化视图组件 */ private void initViews() { // 绑定按钮 startRecordButton = findViewById(R.id.startRecordButton); stopRecordButton = findViewById(R.id.stopRecordButton); playSoundButton = findViewById(R.id.playSoundButton); pauseSoundButton = findViewById(R.id.pauseSoundButton); stopSoundButton = findViewById(R.id.stopSoundButton); resumeSoundButton = findViewById(R.id.resumeSoundButton); clearSoundsButton = findViewById(R.id.clearSoundsButton); // 初始按钮状态设置 stopRecordButton.setEnabled(false); pauseSoundButton.setEnabled(false); stopSoundButton.setEnabled(false); resumeSoundButton.setEnabled(false); } /** * 设置按钮点击监听器 */ private void setupClickListeners() { startRecordButton.setOnClickListener(v -> startRecording()); stopRecordButton.setOnClickListener(v -> stopRecording()); playSoundButton.setOnClickListener(v -> addToPlaybackQueue()); pauseSoundButton.setOnClickListener(v -> { pausePlayback(); handler.sendEmptyMessage(0x20); }); stopSoundButton.setOnClickListener(v -> { stopPlayback(); handler.sendEmptyMessage(0x21); }); resumeSoundButton.setOnClickListener(v -> { if (isPaused.get() && !playbackQueue.isEmpty()) { resumePlayback(); handler.sendEmptyMessage(0x22); } }); clearSoundsButton.setOnClickListener(v -> { clearAllRecordings(); handler.sendEmptyMessage(0x23); }); } // ==================== 录音功能实现 ==================== /** * 开始录音 */ private void startRecording() { // 检查录音权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { sendErrorMessage("没有录音权限"); return; } // 如果已经在录音,先释放资源 if (isRecording.get()) { releaseAudioResources(); } try { // 初始化录音器 audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE ); if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { throw new IllegalStateException("AudioRecord 初始化失败"); } // 开始录音 audioRecord.startRecording(); isRecording.set(true); // 更新UI状态 startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); updatePlayButtonsState(); // 启动录音数据采集线程 if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdownNow(); } scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(this::captureAudioData, 0, 100, TimeUnit.MILLISECONDS); // 发送开始录音通知 handler.sendEmptyMessage(0x12); } catch (Exception e) { Log.e(TAG, "录音启动失败", e); sendErrorMessage("录音启动失败: " + e.getMessage()); releaseAudioResources(); } } /** * 停止录音 */ private void stopRecording() { if (!isRecording.get()) return; isRecording.set(false); releaseAudioResources(); // 更新UI状态 stopRecordButton.setEnabled(false); startRecordButton.setEnabled(true); updatePlayButtonsState(); // 发送停止录音通知 handler.sendEmptyMessage(0x14); } /** * 采集音频数据并保存到队列 */ private void captureAudioData() { if (!isRecording.get() || audioRecord == null) return; byte[] buffer = new byte[BUFFER_SIZE]; try { int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); if (bytesRead > 0) { // 将录制的音频数据保存到队列 synchronized (recordingQueueLock) { recordingQueue.offer(buffer.clone()); } // 发送录音数据包 String base64Data = Base64.encodeToString(buffer, Base64.DEFAULT); sendJsonPacket("recording", base64Data); } } catch (Exception e) { Log.e(TAG, "音频采集失败", e); } } // ==================== 录音功能结束 ==================== // ==================== 播放功能实现 ==================== /** * 添加当前录音到播放队列 */ private void addToPlaybackQueue() { if (recordingQueue.isEmpty()) { Toast.makeText(this, "没有可播放的录音", Toast.LENGTH_SHORT).show(); return; } // 创建录音数据副本 Queue<byte[]> recordingCopy = new LinkedList<>(); synchronized (recordingQueueLock) { for (byte[] data : recordingQueue) { recordingCopy.offer(data.clone()); } } // 添加到播放队列 synchronized (playbackQueueLock) { playbackQueue.addAll(recordingCopy); } // 如果当前没有播放,立即开始播放 if (!isPlaybackThreadActive && !isPlaying.get()) { executorService.execute(this::playRecordingQueue); } else { handler.sendEmptyMessage(0x18); } } /** * 将Base64编码的音频数据添加到播放队列 * @param decodedData 解码后的音频数据 */ private void addBase64ToPlaybackQueue(byte[] decodedData) { if (decodedData == null || decodedData.length == 0) { Log.w(TAG, "无效的音频数据"); return; } ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedData); byte[] buffer; int bytesRead; // 使用固定大小的缓冲区读取数据 buffer = new byte[4096]; // 使用固定的缓冲区大小 try { while ((bytesRead = inputStream.read(buffer)) != -1) { if (bytesRead > 0) { byte[] dataChunk = new byte[bytesRead]; System.arraycopy(buffer, 0, dataChunk, 0, bytesRead); synchronized (playbackQueueLock) { playbackQueue.offer(dataChunk); } } } } catch (IOException e) { Log.e(TAG, "读取音频数据失败", e); } finally { try { inputStream.close(); } catch (IOException e) { Log.e(TAG, "关闭输入流失败", e); } } // 如果当前没有播放,立即开始播放 if (!isPlaybackThreadActive && !isPlaying.get()) { executorService.execute(this::playRecordingQueue); } else { handler.sendEmptyMessage(0x18); // 提示已添加到播放队列 } } /** * 播放录音队列 */ private void playRecordingQueue() { isPlaybackThreadActive = true; isPlaying.set(true); isPaused.set(false); handler.sendEmptyMessage(0x19); // 更新按钮状态 // 配置音频播放器 int bufferSize = AudioTrack.getMinBufferSize( SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT ); // 创建新的AudioTrack synchronized (audioTrackLock) { if (audioTrack != null) { try { audioTrack.stop(); audioTrack.release(); } catch (Exception e) { Log.e(TAG, "释放AudioTrack失败", e); } } try { audioTrack = new AudioTrack( AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM ); audioTrack.play(); } catch (IllegalStateException e) { Log.e(TAG, "创建AudioTrack失败", e); stopPlayback(); return; } } // 播放队列中的所有录音数据 while (isPlaying.get() && !playbackQueue.isEmpty()) { if (isPaused.get()) { // 暂停状态,等待恢复 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } continue; } byte[] audioData; synchronized (playbackQueueLock) { audioData = playbackQueue.poll(); } if (audioData != null) { synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getState() == AudioTrack.STATE_INITIALIZED) { try { audioTrack.write(audioData, 0, audioData.length); } catch (IllegalStateException e) { Log.e(TAG, "音频写入失败: " + e.getMessage()); break; } } else { Log.w(TAG, "AudioTrack不可用,停止播放"); break; } } } } // 确保播放完成时正确释放资源 try { synchronized (audioTrackLock) { if (audioTrack != null) { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.stop(); } audioTrack.release(); audioTrack = null; } } } catch (Exception e) { Log.e(TAG, "播放完成后释放资源失败", e); } // 播放完成 stopPlayback(); handler.sendEmptyMessage(0x17); } /** * 暂停播放 */ private void pausePlayback() { if (!isPlaying.get() || isPaused.get()) return; isPaused.set(true); // 保存当前播放位置 synchronized (playbackQueueLock) { pausedQueue.clear(); pausedQueue.addAll(playbackQueue); playbackQueue.clear(); } // 暂停音频播放 synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { try { audioTrack.pause(); } catch (IllegalStateException e) { Log.e(TAG, "暂停播放失败: " + e.getMessage()); } } } handler.sendEmptyMessage(0x19); // 更新按钮状态 runOnUiThread(() -> Toast.makeText(MainActivity.this, "播放已暂停", Toast.LENGTH_SHORT).show() ); } /** * 继续播放 */ private void resumePlayback() { if (!isPaused.get() || pausedQueue.isEmpty()) { return; } isPaused.set(false); isPlaying.set(true); // 恢复播放位置 synchronized (playbackQueueLock) { playbackQueue.clear(); playbackQueue.addAll(pausedQueue); pausedQueue.clear(); } // 恢复音频播放 synchronized (audioTrackLock) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) { try { audioTrack.play(); } catch (IllegalStateException e) { Log.e(TAG, "恢复播放失败: " + e.getMessage()); } } } handler.sendEmptyMessage(0x19); // 更新按钮状态 runOnUiThread(() -> Toast.makeText(MainActivity.this, "继续播放", Toast.LENGTH_SHORT).show() ); } /** * 停止播放 */ private void stopPlayback() { isPlaying.set(false); isPaused.set(false); synchronized (audioTrackLock) { if (audioTrack != null) { try { if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING || audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PAUSED) { audioTrack.stop(); } audioTrack.release(); } catch (IllegalStateException e) { Log.e(TAG, "停止播放失败: " + e.getMessage()); } finally { audioTrack = null; } } } synchronized (playbackQueueLock) { playbackQueue.clear(); } pausedQueue.clear(); runOnUiThread(() -> { handler.sendEmptyMessage(0x19); // 更新按钮状态 Toast.makeText(MainActivity.this, "播放已停止", Toast.LENGTH_SHORT).show(); } ); } private void clearAllRecordings() { stopPlayback(); synchronized (recordingQueueLock) { recordingQueue.clear(); } pausedQueue.clear(); synchronized (playbackQueueLock) { playbackQueue.clear(); } handler.sendEmptyMessage(0x19); // 更新按钮状态 runOnUiThread(() -> Toast.makeText(MainActivity.this, "所有录音已清除", Toast.LENGTH_SHORT).show() ); } // ==================== 播放功能结束 ==================== // ==================== 辅助方法 ==================== /** * 更新播放按钮状态 */ private void updatePlayButtonsState() { runOnUiThread(() -> { boolean hasRecordings = !recordingQueue.isEmpty() || !pausedQueue.isEmpty(); boolean isPlayingState = isPlaying.get() && !isPaused.get(); playSoundButton.setEnabled(hasRecordings && !isPlayingState); pauseSoundButton.setEnabled(isPlayingState); stopSoundButton.setEnabled(isPlaying.get() || isPaused.get()); resumeSoundButton.setEnabled(!playbackQueue.isEmpty() && isPaused.get()); clearSoundsButton.setEnabled(hasRecordings); }); } /** * 播放TTS语音 * @param text 要播放的文本 */ private void playTts(String text) { if (isTtsInitialized) { ttsEngine.speak(text, TextToSpeech.QUEUE_FLUSH, null); } } /** * 释放音频资源 */ private void releaseAudioResources() { if (audioRecord != null) { try { if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) { audioRecord.stop(); } audioRecord.release(); } catch (IllegalStateException e) { Log.e(TAG, "停止录音失败", e); } finally { audioRecord = null; } } if (scheduler != null) { try { scheduler.shutdownNow(); if (!scheduler.awaitTermination(500, TimeUnit.MILLISECONDS)) { Log.w(TAG, "录音线程池未正常关闭"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { scheduler = null; } } } /** * 发送JSON格式的数据包 * @param type 数据包类型 * @param data 数据内容(可以为null) */ private void sendJsonPacket(String type, Object data) { if (clientSocket == null || clientSocket.isClosed() || socketWriter == null) { return; } try { JSONObject packet = new JSONObject(); packet.put("type", type); if (data != null) { packet.put("data", data); } synchronized (this) { if (socketWriter != null) { socketWriter.write(packet.toString()); socketWriter.write("\n\n"); socketWriter.flush(); } } } catch (Exception e) { Log.e(TAG, "发送数据包失败: " + type, e); } } /** * 发送错误消息 * @param message 错误信息 */ private void sendErrorMessage(String message) { handler.obtainMessage(0x16, message).sendToTarget(); } /** * 检查权限 */ private void checkPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CODE); } } /** * 启动服务器 * @param port 监听端口 */ private void startServer(int port) { executorService.execute(() -> { try { serverSocket = new ServerSocket(port); Log.i(TAG, "服务器启动: " + port); while (isServerRunning) { try { Socket socket = serverSocket.accept(); clientSocket = socket; synchronized (this) { socketWriter = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); } handler.sendEmptyMessage(0x11); } catch (IOException e) { if (isServerRunning) Log.e(TAG, "接受连接失败", e); } } } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); runOnUiThread(() -> Toast.makeText(this, "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show()); } finally { closeServerSocket(); } }); } /** * 启动Socket监听 */ private void startSocketListener() { executorService.execute(() -> { while (true) { if (clientSocket != null && !clientSocket.isClosed()) { try { BufferedReader reader = new BufferedReader( new InputStreamReader(clientSocket.getInputStream(), "UTF-8")); StringBuilder packetBuilder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { if (line.isEmpty()) { // 收到两个换行符,表示一个数据包结束 if (packetBuilder.length() > 0) { String packet = packetBuilder.toString(); Log.d(TAG, "收到数据包: " + packet); try { JSONObject command = new JSONObject(packet); String type = command.getString("type"); switch (type) { case "playSound": String base64Data = command.getString("data"); Message msg = handler.obtainMessage(0x24, base64Data); handler.sendMessage(msg); break; case "pauseSound": handler.sendEmptyMessage(0x20); break; case "stopSound": handler.sendEmptyMessage(0x21); break; case "resumeSound": handler.sendEmptyMessage(0x22); break; case "clearSounds": handler.sendEmptyMessage(0x23); break; default: Log.w(TAG, "未知指令类型: " + type); } } catch (JSONException e) { Log.e(TAG, "JSON解析失败", e); } // 重置包构建器 packetBuilder.setLength(0); } } else { // 添加数据到当前包 packetBuilder.append(line); } } } catch (IOException e) { Log.e(TAG, "Socket读取失败", e); } } else { try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }); } /** * 关闭服务器Socket */ private void closeServerSocket() { try { if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } } catch (IOException e) { Log.w(TAG, "关闭ServerSocket失败", e); } } /** * TTS初始化回调 */ @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { int result = ttsEngine.setLanguage(Locale.CHINESE); if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) { Log.e(TAG, "TTS语言不支持中文"); } else { isTtsInitialized = true; } } } /** * 活动销毁时调用 */ @Override protected void onDestroy() { super.onDestroy(); isServerRunning = false; if (ttsEngine != null) { ttsEngine.stop(); ttsEngine.shutdown(); } closeServerSocket(); closeSocket(clientSocket); // 停止所有录音和播放 stopRecording(); stopPlayback(); // 优雅关闭线程池 executorService.shutdown(); try { if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); Thread.currentThread().interrupt(); } // 确保所有资源释放 releaseAudioResources(); } /** * 关闭Socket * @param socket 要关闭的Socket */ private void closeSocket(Socket socket) { try { if (socket != null && !socket.isClosed()) { socket.close(); } } catch (IOException e) { Log.w(TAG, "关闭Socket失败", e); } if (socket == clientSocket) { clientSocket = null; synchronized (this) { socketWriter = null; } } } } 保留按钮,播放语音,暂停播放,停止播放,继续播放,清空全部声音这些功能先注释失效
07-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值