public class MainActivity extends AppCompatActivity {
private Button startRecordButton;
private Button stopRecordButton;
private Button uploadButton;
private MediaRecorder mediaRecorder;
private String audioFilePath;
private static final int PERMISSION_REQUEST_CODE = 1;
private final ExecutorService executorService = Executors.newCachedThreadPool();
// TCP服务器套接字,用于监听客户端连接
private ServerSocket serverSocket;
// 控制服务器运行状态的标志变量
private volatile boolean isServerRunning = true;
// 当前客户端连接的Socket
private Socket clientSocket;
// 主线程的消息处理器,用于更新UI
private 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();
uploadButton.setEnabled(true);
startRecordButton.setEnabled(true);
break;
case 0x13:
// 上传失败,显示错误信息并启用上传按钮
Toast.makeText(MainActivity.this, "上传失败: " + msg.obj, Toast.LENGTH_SHORT).show();
uploadButton.setEnabled(true);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置主界面布局为 activity_main.xml
setContentView(R.layout.activity_main);
// 初始化按钮控件
initViews();
// 检查并申请录音权限
checkPermissions();
// 设置按钮点击事件监听器
setupClickListeners();
// 启动TCP服务器,监听端口30000
startServer(30000);
}
/**
* 初始化界面中的按钮控件,并设置初始状态。
* 开始录音按钮默认可用,停止录音和上传按钮默认不可用。
*/
private void initViews() {
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
uploadButton = findViewById(R.id.uploadButton);
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(false);
}
/**
* 检查并请求录音权限
* 如果应用尚未获得 RECORD_AUDIO 权限,则通过 ActivityCompat.requestPermissions 方法发起请求
* 请求码 PERMISSION_REQUEST_CODE 用于在 onRequestPermissionsResult 中识别该请求
*/
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 setupClickListeners() {
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
uploadButton.setOnClickListener(v -> uploadRecording());
}
/**
* 开始录音操作
* 配置MediaRecorder参数,准备并启动录音
*/
private void startRecording() {
// 生成音频文件的存储路径
audioFilePath = getExternalCacheDir().getAbsolutePath() + "/audio_record.mp3";
// 初始化 MediaRecorder 并配置录音参数
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频来源为麦克风
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置输出格式为 MPEG-4
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); // 使用 AAC 编码器
mediaRecorder.setOutputFile(audioFilePath); // 设置输出文件路径
try {
// 准备并开始录音
mediaRecorder.prepare();
mediaRecorder.start();
// 更新按钮状态
startRecordButton.setEnabled(false); // 开始录音按钮禁用
stopRecordButton.setEnabled(true); // 停止录音按钮启用
uploadButton.setEnabled(false); // 上传按钮禁用
// 提示用户录音已开始
Toast.makeText(this, "开始录音", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// 捕获异常并提示录音失败
e.printStackTrace();
Toast.makeText(this, "录音失败", Toast.LENGTH_SHORT).show();
}
}
/**
* 停止录音操作
* 当mediaRecorder不为空时,停止录音并释放资源
* 录音停止后,禁用停止录音按钮,启用上传按钮
* 最后,通过Toast显示录音已停止,并提供音频文件路径
*/
private void stopRecording() {
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(true);
Toast.makeText(this, "录音已停止,文件路径:" + audioFilePath, Toast.LENGTH_LONG).show();
}
}
/**
* 上传录音文件到已连接的客户端
* 首先检查文件是否存在以及是否有客户端连接
* 然后通过Socket发送文件名、大小和内容
*/
private void uploadRecording() {
File audioFile = new File(audioFilePath);
if (!audioFile.exists()) {
Toast.makeText(this, "录音文件不存在", Toast.LENGTH_SHORT).show();
return;
}
if (clientSocket == null || clientSocket.isClosed()) {
Toast.makeText(this, "未检测到客户端连接", Toast.LENGTH_SHORT).show();
return;
}
uploadButton.setEnabled(false);
executorService.execute(() -> {
try {
DataOutputStream dataOut = new DataOutputStream(clientSocket.getOutputStream());
byte[] fileNameBytes = audioFile.getName().getBytes("UTF-8");
dataOut.writeInt(fileNameBytes.length);
dataOut.write(fileNameBytes);
dataOut.writeLong(audioFile.length());
FileInputStream fileInputStream = new FileInputStream(audioFile);
byte[] buffer = new byte[4096];
int read;
while ((read = fileInputStream.read(buffer)) != -1) {
dataOut.write(buffer, 0, read);
}
dataOut.flush();
fileInputStream.close();
handler.sendEmptyMessage(0x12);
} catch (IOException e) {
e.printStackTrace();
Message msg = handler.obtainMessage(0x13, e.getMessage());
handler.sendMessage(msg);
}
});
}
/**
* 启动 TCP 服务器,监听指定端口,等待客户端连接。
* 当有客户端连接时,会更新 UI 提示“客户端已连接”,并将文件上传任务交由线程池处理。
*
* @param port 要监听的端口号
*/
private void startServer(int port) {
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port); // 创建一个ServerSocket,监听指定端口
while (isServerRunning) {
Socket socket = serverSocket.accept(); // 接收客户端连接
clientSocket = socket;
handler.sendEmptyMessage(0x11); // 发送客户端连接消息
// 启动新线程处理客户端上传的文件
executorService.execute(() -> handleClientUpload(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 处理客户端上传的文件。
* 接收客户端发送的文件名、文件大小和文件内容,并将其保存到设备缓存目录中。
*
* @param socket 客户端连接的 Socket
*/
private void handleClientUpload(Socket socket) {
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (true) {
int nameLen;
try {
nameLen = in.readInt(); // 读取文件名长度
} catch (IOException e) {
break; // 客户端断开连接,退出循环
}
if (nameLen <= 0) break;
byte[] nameBytes = new byte[nameLen];
in.readFully(nameBytes); // 读取完整的文件名
String fileName = new String(nameBytes, "UTF-8"); // 解码文件名
long fileSize = in.readLong(); // 读取文件大小
File file = new File(getExternalCacheDir(), fileName); // 创建目标文件
try (FileOutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[4096];
long remaining = fileSize;
while (remaining > 0) {
int read = in.read(buffer, 0, (int) Math.min(buffer.length, remaining));
if (read == -1) throw new IOException("连接中断");
out.write(buffer, 0, read); // 写入文件数据
remaining -= read;
}
out.flush();
}
handler.sendEmptyMessage(0x12); // 发送文件接收完成的消息
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close(); // 关闭客户端连接
} catch (IOException ignored) {}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
isServerRunning = false;
try {
if (serverSocket != null) serverSocket.close(); // 关闭服务器套接字
} catch (IOException e) {
e.printStackTrace();
}
executorService.shutdownNow(); // 关闭线程池
if (mediaRecorder != null) {
mediaRecorder.release(); // 释放录音资源
mediaRecorder = null;
}
try {
if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close(); // 关闭客户端套接字
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "权限已授予", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "录音权限被拒绝", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}public class MainActivity extends AppCompatActivity {
private Button startRecordButton;
private Button stopRecordButton;
private Button uploadButton;
private MediaRecorder mediaRecorder;
private String audioFilePath;
private static final int PERMISSION_REQUEST_CODE = 1;
private final ExecutorService executorService = Executors.newCachedThreadPool();
// TCP服务器套接字,用于监听客户端连接
private ServerSocket serverSocket;
// 控制服务器运行状态的标志变量
private volatile boolean isServerRunning = true;
// 当前客户端连接的Socket
private Socket clientSocket;
// 主线程的消息处理器,用于更新UI
private 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();
uploadButton.setEnabled(true);
startRecordButton.setEnabled(true);
break;
case 0x13:
// 上传失败,显示错误信息并启用上传按钮
Toast.makeText(MainActivity.this, "上传失败: " + msg.obj, Toast.LENGTH_SHORT).show();
uploadButton.setEnabled(true);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置主界面布局为 activity_main.xml
setContentView(R.layout.activity_main);
// 初始化按钮控件
initViews();
// 检查并申请录音权限
checkPermissions();
// 设置按钮点击事件监听器
setupClickListeners();
// 启动TCP服务器,监听端口30000
startServer(30000);
}
/**
* 初始化界面中的按钮控件,并设置初始状态。
* 开始录音按钮默认可用,停止录音和上传按钮默认不可用。
*/
private void initViews() {
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
uploadButton = findViewById(R.id.uploadButton);
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(false);
}
/**
* 检查并请求录音权限
* 如果应用尚未获得 RECORD_AUDIO 权限,则通过 ActivityCompat.requestPermissions 方法发起请求
* 请求码 PERMISSION_REQUEST_CODE 用于在 onRequestPermissionsResult 中识别该请求
*/
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 setupClickListeners() {
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
uploadButton.setOnClickListener(v -> uploadRecording());
}
/**
* 开始录音操作
* 配置MediaRecorder参数,准备并启动录音
*/
private void startRecording() {
// 生成音频文件的存储路径
audioFilePath = getExternalCacheDir().getAbsolutePath() + "/audio_record.mp3";
// 初始化 MediaRecorder 并配置录音参数
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频来源为麦克风
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置输出格式为 MPEG-4
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); // 使用 AAC 编码器
mediaRecorder.setOutputFile(audioFilePath); // 设置输出文件路径
try {
// 准备并开始录音
mediaRecorder.prepare();
mediaRecorder.start();
// 更新按钮状态
startRecordButton.setEnabled(false); // 开始录音按钮禁用
stopRecordButton.setEnabled(true); // 停止录音按钮启用
uploadButton.setEnabled(false); // 上传按钮禁用
// 提示用户录音已开始
Toast.makeText(this, "开始录音", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// 捕获异常并提示录音失败
e.printStackTrace();
Toast.makeText(this, "录音失败", Toast.LENGTH_SHORT).show();
}
}
/**
* 停止录音操作
* 当mediaRecorder不为空时,停止录音并释放资源
* 录音停止后,禁用停止录音按钮,启用上传按钮
* 最后,通过Toast显示录音已停止,并提供音频文件路径
*/
private void stopRecording() {
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(true);
Toast.makeText(this, "录音已停止,文件路径:" + audioFilePath, Toast.LENGTH_LONG).show();
}
}
/**
* 上传录音文件到已连接的客户端
* 首先检查文件是否存在以及是否有客户端连接
* 然后通过Socket发送文件名、大小和内容
*/
private void uploadRecording() {
File audioFile = new File(audioFilePath);
if (!audioFile.exists()) {
Toast.makeText(this, "录音文件不存在", Toast.LENGTH_SHORT).show();
return;
}
if (clientSocket == null || clientSocket.isClosed()) {
Toast.makeText(this, "未检测到客户端连接", Toast.LENGTH_SHORT).show();
return;
}
uploadButton.setEnabled(false);
executorService.execute(() -> {
try {
DataOutputStream dataOut = new DataOutputStream(clientSocket.getOutputStream());
byte[] fileNameBytes = audioFile.getName().getBytes("UTF-8");
dataOut.writeInt(fileNameBytes.length);
dataOut.write(fileNameBytes);
dataOut.writeLong(audioFile.length());
FileInputStream fileInputStream = new FileInputStream(audioFile);
byte[] buffer = new byte[4096];
int read;
while ((read = fileInputStream.read(buffer)) != -1) {
dataOut.write(buffer, 0, read);
}
dataOut.flush();
fileInputStream.close();
handler.sendEmptyMessage(0x12);
} catch (IOException e) {
e.printStackTrace();
Message msg = handler.obtainMessage(0x13, e.getMessage());
handler.sendMessage(msg);
}
});
}
/**
* 启动 TCP 服务器,监听指定端口,等待客户端连接。
* 当有客户端连接时,会更新 UI 提示“客户端已连接”,并将文件上传任务交由线程池处理。
*
* @param port 要监听的端口号
*/
private void startServer(int port) {
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port); // 创建一个ServerSocket,监听指定端口
while (isServerRunning) {
Socket socket = serverSocket.accept(); // 接收客户端连接
clientSocket = socket;
handler.sendEmptyMessage(0x11); // 发送客户端连接消息
// 启动新线程处理客户端上传的文件
executorService.execute(() -> handleClientUpload(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 处理客户端上传的文件。
* 接收客户端发送的文件名、文件大小和文件内容,并将其保存到设备缓存目录中。
*
* @param socket 客户端连接的 Socket
*/
private void handleClientUpload(Socket socket) {
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (true) {
int nameLen;
try {
nameLen = in.readInt(); // 读取文件名长度
} catch (IOException e) {
break; // 客户端断开连接,退出循环
}
if (nameLen <= 0) break;
byte[] nameBytes = new byte[nameLen];
in.readFully(nameBytes); // 读取完整的文件名
String fileName = new String(nameBytes, "UTF-8"); // 解码文件名
long fileSize = in.readLong(); // 读取文件大小
File file = new File(getExternalCacheDir(), fileName); // 创建目标文件
try (FileOutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[4096];
long remaining = fileSize;
while (remaining > 0) {
int read = in.read(buffer, 0, (int) Math.min(buffer.length, remaining));
if (read == -1) throw new IOException("连接中断");
out.write(buffer, 0, read); // 写入文件数据
remaining -= read;
}
out.flush();
}
handler.sendEmptyMessage(0x12); // 发送文件接收完成的消息
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close(); // 关闭客户端连接
} catch (IOException ignored) {}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
isServerRunning = false;
try {
if (serverSocket != null) serverSocket.close(); // 关闭服务器套接字
} catch (IOException e) {
e.printStackTrace();
}
executorService.shutdownNow(); // 关闭线程池
if (mediaRecorder != null) {
mediaRecorder.release(); // 释放录音资源
mediaRecorder = null;
}
try {
if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close(); // 关闭客户端套接字
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "权限已授予", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "录音权限被拒绝", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}public class MainActivity extends AppCompatActivity {
private Button startRecordButton;
private Button stopRecordButton;
private Button uploadButton;
private MediaRecorder mediaRecorder;
private String audioFilePath;
private static final int PERMISSION_REQUEST_CODE = 1;
private final ExecutorService executorService = Executors.newCachedThreadPool();
// TCP服务器套接字,用于监听客户端连接
private ServerSocket serverSocket;
// 控制服务器运行状态的标志变量
private volatile boolean isServerRunning = true;
// 当前客户端连接的Socket
private Socket clientSocket;
// 主线程的消息处理器,用于更新UI
private 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();
uploadButton.setEnabled(true);
startRecordButton.setEnabled(true);
break;
case 0x13:
// 上传失败,显示错误信息并启用上传按钮
Toast.makeText(MainActivity.this, "上传失败: " + msg.obj, Toast.LENGTH_SHORT).show();
uploadButton.setEnabled(true);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置主界面布局为 activity_main.xml
setContentView(R.layout.activity_main);
// 初始化按钮控件
initViews();
// 检查并申请录音权限
checkPermissions();
// 设置按钮点击事件监听器
setupClickListeners();
// 启动TCP服务器,监听端口30000
startServer(30000);
}
/**
* 初始化界面中的按钮控件,并设置初始状态。
* 开始录音按钮默认可用,停止录音和上传按钮默认不可用。
*/
private void initViews() {
startRecordButton = findViewById(R.id.startRecordButton);
stopRecordButton = findViewById(R.id.stopRecordButton);
uploadButton = findViewById(R.id.uploadButton);
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(false);
}
/**
* 检查并请求录音权限
* 如果应用尚未获得 RECORD_AUDIO 权限,则通过 ActivityCompat.requestPermissions 方法发起请求
* 请求码 PERMISSION_REQUEST_CODE 用于在 onRequestPermissionsResult 中识别该请求
*/
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 setupClickListeners() {
startRecordButton.setOnClickListener(v -> startRecording());
stopRecordButton.setOnClickListener(v -> stopRecording());
uploadButton.setOnClickListener(v -> uploadRecording());
}
/**
* 开始录音操作
* 配置MediaRecorder参数,准备并启动录音
*/
private void startRecording() {
// 生成音频文件的存储路径
audioFilePath = getExternalCacheDir().getAbsolutePath() + "/audio_record.mp3";
// 初始化 MediaRecorder 并配置录音参数
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频来源为麦克风
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置输出格式为 MPEG-4
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); // 使用 AAC 编码器
mediaRecorder.setOutputFile(audioFilePath); // 设置输出文件路径
try {
// 准备并开始录音
mediaRecorder.prepare();
mediaRecorder.start();
// 更新按钮状态
startRecordButton.setEnabled(false); // 开始录音按钮禁用
stopRecordButton.setEnabled(true); // 停止录音按钮启用
uploadButton.setEnabled(false); // 上传按钮禁用
// 提示用户录音已开始
Toast.makeText(this, "开始录音", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// 捕获异常并提示录音失败
e.printStackTrace();
Toast.makeText(this, "录音失败", Toast.LENGTH_SHORT).show();
}
}
/**
* 停止录音操作
* 当mediaRecorder不为空时,停止录音并释放资源
* 录音停止后,禁用停止录音按钮,启用上传按钮
* 最后,通过Toast显示录音已停止,并提供音频文件路径
*/
private void stopRecording() {
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
stopRecordButton.setEnabled(false);
uploadButton.setEnabled(true);
Toast.makeText(this, "录音已停止,文件路径:" + audioFilePath, Toast.LENGTH_LONG).show();
}
}
/**
* 上传录音文件到已连接的客户端
* 首先检查文件是否存在以及是否有客户端连接
* 然后通过Socket发送文件名、大小和内容
*/
private void uploadRecording() {
File audioFile = new File(audioFilePath);
if (!audioFile.exists()) {
Toast.makeText(this, "录音文件不存在", Toast.LENGTH_SHORT).show();
return;
}
if (clientSocket == null || clientSocket.isClosed()) {
Toast.makeText(this, "未检测到客户端连接", Toast.LENGTH_SHORT).show();
return;
}
uploadButton.setEnabled(false);
executorService.execute(() -> {
try {
DataOutputStream dataOut = new DataOutputStream(clientSocket.getOutputStream());
byte[] fileNameBytes = audioFile.getName().getBytes("UTF-8");
dataOut.writeInt(fileNameBytes.length);
dataOut.write(fileNameBytes);
dataOut.writeLong(audioFile.length());
FileInputStream fileInputStream = new FileInputStream(audioFile);
byte[] buffer = new byte[4096];
int read;
while ((read = fileInputStream.read(buffer)) != -1) {
dataOut.write(buffer, 0, read);
}
dataOut.flush();
fileInputStream.close();
handler.sendEmptyMessage(0x12);
} catch (IOException e) {
e.printStackTrace();
Message msg = handler.obtainMessage(0x13, e.getMessage());
handler.sendMessage(msg);
}
});
}
/**
* 启动 TCP 服务器,监听指定端口,等待客户端连接。
* 当有客户端连接时,会更新 UI 提示“客户端已连接”,并将文件上传任务交由线程池处理。
*
* @param port 要监听的端口号
*/
private void startServer(int port) {
executorService.execute(() -> {
try {
serverSocket = new ServerSocket(port); // 创建一个ServerSocket,监听指定端口
while (isServerRunning) {
Socket socket = serverSocket.accept(); // 接收客户端连接
clientSocket = socket;
handler.sendEmptyMessage(0x11); // 发送客户端连接消息
// 启动新线程处理客户端上传的文件
executorService.execute(() -> handleClientUpload(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 处理客户端上传的文件。
* 接收客户端发送的文件名、文件大小和文件内容,并将其保存到设备缓存目录中。
*
* @param socket 客户端连接的 Socket
*/
private void handleClientUpload(Socket socket) {
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (true) {
int nameLen;
try {
nameLen = in.readInt(); // 读取文件名长度
} catch (IOException e) {
break; // 客户端断开连接,退出循环
}
if (nameLen <= 0) break;
byte[] nameBytes = new byte[nameLen];
in.readFully(nameBytes); // 读取完整的文件名
String fileName = new String(nameBytes, "UTF-8"); // 解码文件名
long fileSize = in.readLong(); // 读取文件大小
File file = new File(getExternalCacheDir(), fileName); // 创建目标文件
try (FileOutputStream out = new FileOutputStream(file)) {
byte[] buffer = new byte[4096];
long remaining = fileSize;
while (remaining > 0) {
int read = in.read(buffer, 0, (int) Math.min(buffer.length, remaining));
if (read == -1) throw new IOException("连接中断");
out.write(buffer, 0, read); // 写入文件数据
remaining -= read;
}
out.flush();
}
handler.sendEmptyMessage(0x12); // 发送文件接收完成的消息
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close(); // 关闭客户端连接
} catch (IOException ignored) {}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
isServerRunning = false;
try {
if (serverSocket != null) serverSocket.close(); // 关闭服务器套接字
} catch (IOException e) {
e.printStackTrace();
}
executorService.shutdownNow(); // 关闭线程池
if (mediaRecorder != null) {
mediaRecorder.release(); // 释放录音资源
mediaRecorder = null;
}
try {
if (clientSocket != null && !clientSocket.isClosed()) clientSocket.close(); // 关闭客户端套接字
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "权限已授予", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "录音权限被拒绝", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}修改实现每录160毫秒发送一次录音数据,并且保持连续发送的功能,通过usb无网传输
最新发布