arraycopy vs ByteArray io class

arraycopy vs ByteArray io class

 

In Beijing on 2007-7-15

 

IO功能开发中,常常需要涉及对字节数组的处理。Java语言提供有多种方式可供使用,在这里介绍其中的两种方式,System.arraycopy方法和使用ByteArray 相关IO类,并对它们的使用做一个基本的对比,提供给大家一个简单的参考使用。

java语言中处理字节数组,我们常常会用到System类的arraycopy方法,此方法提供了java系统级别的数组复制功能,性能要高于我们自行复制数组的方式。但作为一个比较底层的方法,它没有提供缓存的功能,并且需要频繁的调用5个入参来作处理。因此对于数组的处理即显繁琐,又在性能方面略有不足。

java提供的io类中,有两个ByteArrayInputStreamByteArrayOutputStream,把对数组的操作封装为io功能来作处理,即减少了调用方面的难度,同时也在其内部实现采用缓存的方式。相比与第一种方法,每一次复制均需要重新开辟一个新的内存空间,则更加有效。

 

下面是对两种代码方式的一个比较,分布使用两种方式操作一次常见的数组复制操作。

 

使用arraycopy实现代码:

       //读取数据包长度

       byte[] buf = new byte[LEN_DATA_LEN];

       in.read(buf);

       int dateLen = Integer.parseInt(new String(buf));

   

       //读取数据包

       byte[] bufData = new byte[dateLen];

       in.read(bufData);

   

       byte[] bufAll = new byte[buf.length + bufData.length];

       System.arraycopy(buf, 0, bufAll, 0, buf.length);

       System.arraycopy(bufData, 0, bufAll, buf.length,                                 bufData.length);

 

使用io类实现代码:

       //读取数据包长度

       byte[] buf = new byte[LEN_DATA_LEN];

       in.read(buf);

       int dateLen = Integer.parseInt(new String(buf));

   

       //读取数据包内容

       byte[] bufData = new byte[dateLen];

       in.read(bufData);

       

       //创建输出字节流对象

       ByteArrayOutputStream outBuf = new ByteArrayOutputStream();

       outBuf.write(buf);

       outBuf.write(bufData);  

       byte[] bufAll = outBuf.toByteArray();

 

对于上面两种方式感觉如何?也许对于简单的程序来说,并没有太大的差异。但是如果在你的代码中存在大量的数组操作,也许修改成使用io类,避免到处使用一堆参数的arraycopy方法更加直观,并且如果你的操作方式中如果存在循环处理,则采用第二中方式,会充分利用缓存功能,使得程序更加有效。

有兴趣可以参考java中的ArrayBute的两个io类的实现,实际上是对arraycopy方法的又一次封装。

 
package com.example.demoapplication; import android.Manifest; // 录音权限相关 import android.content.pm.PackageManager; // 权限检查相关 import android.media.AudioFormat; // 音频格式定义 import android.media.AudioRecord; // 音频录制功能 import android.media.MediaRecorder; // 媒体录制配置 import android.os.Bundle; // Activity生命周期数据 import android.os.Handler; // 线程通信机制 import android.os.Looper; // 主线程消息循环 import android.os.Message; // 消息传递对象 import android.widget.Button; // UI按钮控件 import android.widget.Toast; // 短时提示信息 import androidx.annotation.NonNull; // 非空注解 import androidx.appcompat.app.AppCompatActivity; // 兼容Activity基类 import androidx.core.app.ActivityCompat; // 动态权限请求 import androidx.core.content.ContextCompat; // 权限状态查询 import java.io.BufferedReader; // 文本流读取 import java.io.BufferedWriter; // 文本流写入 import java.io.IOException; // IO异常处理 import java.io.InputStreamReader; // 字节转字符流 import java.io.OutputStreamWriter; // 字节转字符流 import java.net.ServerSocket; // 服务端监听套接字 import java.net.Socket; // 客户端连接套接字 import java.util.concurrent.ExecutorService; // 线程池管理 import java.util.concurrent.Executors; // 线程池工厂 import java.util.concurrent.ScheduledExecutorService; // 定时任务调度 import java.util.concurrent.TimeUnit; // 时间单位定义 import android.util.Base64; // Base64编码 import org.json.JSONException; import org.json.JSONObject; // JSON对象 public class MainActivity extends AppCompatActivity { private Button startRecordButton; // 开始录音按钮 private Button stopRecordButton; // 停止录音按钮 private Button uploadButton; // 上传文件按钮 private AudioRecord audioRecord; // 音频录制对象 private static final int SAMPLE_RATE = 44100; // 采样率:44.1kHz private static final int BUFFER_SIZE; // 缓冲区大小 static { // 使用更安全的缓冲区大小计算方式 int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); // 确保缓冲区大小是帧大小的整数倍(2通道*2字节) BUFFER_SIZE = ((minBufferSize / (2 * 2)) + 1) * (2 * 2); } private ScheduledExecutorService scheduler; // 录音定时器 private boolean isRecording = false; // 录音状态标志 private static final int PERMISSION_REQUEST_CODE = 1; // 权限请求码 private final ExecutorService executorService = Executors.newCachedThreadPool(); // 通用线程池 private ServerSocket serverSocket; // TCP服务端Socket private volatile boolean isServerRunning = true; // 服务运行状态 private Socket clientSocket; // 当前客户端连接 // 主线程消息处理器 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(); break; case 0x13: // 上传错误 Toast.makeText(MainActivity.this, "录音数据已发送", Toast.LENGTH_SHORT).show(); break; case 0x14: // 自定义消息类型,用于停止录音提示 Toast.makeText(MainActivity.this, "停止录音", Toast.LENGTH_SHORT).show(); break; case 0x15: // 接收到控制指令 Toast.makeText(MainActivity.this, "收到控制指令:" + msg.obj.toString(), Toast.LENGTH_SHORT).show(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); // 初始化视图组件 checkPermissions(); // 检查权限状态 setupClickListeners(); // 设置点击事件监听 startServer(30000); // 启动TCP服务器,监听30000端口 } /** * 视图初始化方法 * 绑定布局中的UI组件并设置初始状态 */ private void initViews() { startRecordButton = findViewById(R.id.startRecordButton); stopRecordButton = findViewById(R.id.stopRecordButton); uploadButton = findViewById(R.id.uploadButton); stopRecordButton.setEnabled(false); // 初始禁用停止按钮 uploadButton.setEnabled(false); // 初始禁用上传按钮 } /** * 权限检查方法 * 如果未授予录音权限则发起请求 */ 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()); // 上传录音 } /** * 开始录音方法 * 初始化AudioRecord并启动录制 */ private void startRecording() { // 添加状态检查和异常处理 if (isRecording || audioRecord != null) { return; // 防止重复启动 } 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 = true; startRecordButton.setEnabled(false); stopRecordButton.setEnabled(true); uploadButton.setEnabled(false); // 创建定时任务发送音频数据 scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(this::uploadAudioData, 0, 160, TimeUnit.MILLISECONDS); handler.sendEmptyMessage(0x12); // 发送开始录音的消息 // 添加发送 { "type": "startRecorder", "data": null } 的逻辑 if (clientSocket != null && !clientSocket.isClosed()) { try { JSONObject startPacket = new JSONObject(); startPacket.put("type", "startRecorder"); startPacket.put("data", JSONObject.NULL); // 设置 data 为 null BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); writer.write(startPacket.toString()); writer.write("\n\n"); // 双换行作为结束标识 writer.flush(); } catch (IOException | JSONException e) { e.printStackTrace(); Message msg = handler.obtainMessage(0x13, e.getMessage()); handler.sendMessage(msg); } } else { Toast.makeText(this, "客户端未连接", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "录音启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show(); releaseAudioResources(); } } /** * 停止录音方法 * 释放录音资源并清理状态 */ private void stopRecording() { isRecording = false; releaseAudioResources(); stopRecordButton.setEnabled(false); uploadButton.setEnabled(true); handler.sendEmptyMessage(0x14); // 发送停止录音的消息 // 添加发送 { "type": "stopRecor", "data": null } 的逻辑 if (clientSocket != null && !clientSocket.isClosed()) { try { JSONObject stopPacket = new JSONObject(); stopPacket.put("type", "stopRecor"); stopPacket.put("data", JSONObject.NULL); // 设置 data 为 null BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); writer.write(stopPacket.toString()); writer.write("\n\n"); // 双换行作为结束标识 writer.flush(); } catch (IOException | JSONException e) { e.printStackTrace(); Message msg = handler.obtainMessage(0x13, e.getMessage()); handler.sendMessage(msg); } } } /** * 释放音频资源 */ private void releaseAudioResources() { if (audioRecord != null) { try { audioRecord.stop(); } catch (IllegalStateException e) { // 忽略可能的非法状态异常 } try { audioRecord.release(); } finally { audioRecord = null; } } if (scheduler != null) { try { scheduler.shutdownNow(); } finally { scheduler = null; } } } /** * 实时上传音频数据 * 将当前缓冲区数据通过Socket发送给客户端,并添加Base64编码的JSON格式数据 */ private void uploadAudioData() { if (!isRecording || clientSocket == null || clientSocket.isClosed()) return; byte[] buffer = new byte[BUFFER_SIZE]; try { int bytesRead = audioRecord.read(buffer, 0, BUFFER_SIZE); if (bytesRead > 0) { // 使用固定大小缓冲区确保完整性 byte[] validData = new byte[bytesRead]; System.arraycopy(buffer, 0, validData, 0, bytesRead); // 将音频数据转换为Base64编码字符串 String base64Data = Base64.encodeToString(validData, Base64.DEFAULT); // 构建JSON格式字符串 JSONObject json = new JSONObject(); json.put("type", "recording"); json.put("data", base64Data); // 获取输出流并发送数据 BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); writer.write(json.toString()); writer.write("\n\n"); // 添加双换行作为结束标识 writer.flush(); handler.sendEmptyMessage(0x13); // 发送录音数据的消息 } } catch (IOException | JSONException e) { Message msg = handler.obtainMessage(0x13, e.getMessage()); handler.sendMessage(msg); } catch (Exception e) { e.printStackTrace(); Message msg = handler.obtainMessage(0x13, "音频读取异常: " + e.getMessage()); handler.sendMessage(msg); } } /** * 上传完整录音文件(当前未使用) * 提示该模式下为实时传输无需手动上传 */ private void uploadRecording() { // 可选:上传录音完整文件逻辑,如有需要可添加实现 Toast.makeText(this, "该模式下无需上传文件,已实时发送", Toast.LENGTH_SHORT).show(); } /** * 启动TCP服务器 * 在指定端口监听客户端连接 * * @param port 监听端口号 */ private void startServer(int port) { executorService.execute(() -> { try { serverSocket = new ServerSocket(port); while (isServerRunning) { Socket socket = serverSocket.accept(); clientSocket = socket; handler.sendEmptyMessage(0x11); // 发送客户端连接成功的消息 // 启动双向通信处理 executorService.execute(() -> startCommunication(socket)); } } catch (IOException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(MainActivity.this, "服务器启动失败: " + e.getMessage(), Toast.LENGTH_LONG).show()); } }); } /** * 启动双向通信 * 处理客户端的连接和数据交互 * * @param socket 客户端Socket连接 */ private void startCommunication(Socket socket) { try { BufferedReader reader = new BufferedReader( new InputStreamReader(socket.getInputStream(), "UTF-8")); StringBuilder packetBuilder = new StringBuilder(); int c; while ((c = reader.read()) != -1) { char ch = (char) c; packetBuilder.append(ch); // 检测到连续两个换行符,表示一个完整的数据包结束 if (packetBuilder.length() >= 2 && packetBuilder.charAt(packetBuilder.length() - 2) == '\n' && packetBuilder.charAt(packetBuilder.length() - 1) == '\n') { String packet = packetBuilder.toString().trim(); // 去除首尾空白字符 packetBuilder.setLength(0); // 清空构建器 if (!packet.isEmpty()) { try { JSONObject jsonObject = new JSONObject(packet); handleReceivedPacket(jsonObject); // 处理接收到的数据包 } catch (JSONException e) { e.printStackTrace(); } } } } } catch (IOException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(MainActivity.this, "通信中断: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } finally { try { socket.close(); } catch (IOException ignored) {} // 重置客户端socket if (socket == clientSocket) { clientSocket = null; } } } /** * 处理接收到的数据包 * 根据数据包类型执行相应操作 * * @param jsonObject 接收到的JSON数据包 */ private void handleReceivedPacket(JSONObject jsonObject) { try { String type = jsonObject.getString("type"); Object data = jsonObject.opt("data"); // 发送消息到主线程进行Toast显示 Message msg = handler.obtainMessage(0x15, type + ": " + data.toString()); handler.sendMessage(msg); // 根据不同类型执行不同操作 switch (type) { case "start_recording": if (!isRecording) { runOnUiThread(this::startRecording); } break; case "stop_recording": if (isRecording) { runOnUiThread(this::stopRecording); } break; case "ping": sendResponse("pong"); // 发送pong响应 break; // 可以添加更多类型的处理 } } catch (JSONException e) { e.printStackTrace(); } } /** * 发送响应数据包 * 使用统一的JSON格式并通过双换行结尾 * * @param responseType 响应类型 */ private void sendResponse(String responseType) { if (clientSocket == null || clientSocket.isClosed()) return; try { JSONObject response = new JSONObject(); response.put("type", responseType); response.put("data", ""); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); writer.write(response.toString()); writer.write("\n\n"); writer.flush(); } catch (JSONException | IOException e) { e.printStackTrace(); } } /** * 发送特定类型的数据包 * 用于发送控制指令或其他消息 * * @param type 数据包类型 * @param data 数据内容 */ private void sendDataPacket(String type, Object data) { if (clientSocket == null || clientSocket.isClosed()) return; try { JSONObject packet = new JSONObject(); packet.put("type", type); packet.put("data", data); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8")); writer.write(packet.toString()); writer.write("\n\n"); writer.flush(); } catch (JSONException | IOException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); isServerRunning = false; try { if (serverSocket != null) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } executorService.shutdownNow(); if (audioRecord != null) { audioRecord.release(); audioRecord = null; } if (scheduler != null) { scheduler.shutdownNow(); } 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); } } import socket import subprocess import threading import time import json import base64 import numpy as np import queue import pyaudio # 配置参数 CHUNK = 1024 * 2 # 缓冲区大小 FORMAT = pyaudio.paInt16 # 16位PCM格式 CHANNELS = 1 # 单声道 RATE = 44100 # 采样率 SERVER_PORT = 30000 # TCP监听端口 class ADBExecutor: """ADB命令执行器""" ADB_PATH = "adb" HOST_PORT = 35000 ANDROID_PORT = 30000 def __init__(self, adb_path="adb"): self.adb_path = adb_path def get_connected_device(self): """获取当前连接的第一个设备""" try: result = subprocess.check_output([self.adb_path, "devices"], text=True) lines = result.strip().split("\n")[1:] devices = [line.split("\t")[0] for line in lines if "device" in line] return devices[0] if devices else None except Exception as e: print(f"ADB命令执行错误: {e}") return None def setup_port_forwarding(self, device_id): """设置端口转发""" try: subprocess.run( [self.adb_path, "-s", device_id, "forward", f"tcp:{self.HOST_PORT}", f"tcp:{self.ANDROID_PORT}"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) print(f"端口转发已设置: {self.HOST_PORT} <-> {self.ANDROID_PORT}") return True except Exception as e: print(f"端口转发失败: {e}") return False class AdbDeviceListener: """ADB设备监听器""" def __init__(self, adb_executor, interval=2, on_device_callback=None): self.adb_executor = adb_executor self.interval = interval self.on_device_callback = on_device_callback self.running = False self.current_device = None def start(self): """启动设备监听线程""" self.running = True threading.Thread(target=self._listen_loop, daemon=True).start() print("设备监听已启动") def stop(self): """停止设备监听""" self.running = False print("设备监听已停止") def _listen_loop(self): """设备监听主循环""" while self.running: try: device = self.adb_executor.get_connected_device() # 检测设备连接变化 if device and device != self.current_device: if self.current_device: self._handle_event("disconnected", self.current_device) self.current_device = device self._handle_event("connected", device) elif not device and self.current_device: self._handle_event("disconnected", self.current_device) self.current_device = None time.sleep(self.interval) except Exception as e: print(f"设备监听错误: {e}") time.sleep(5) def _handle_event(self, event_type, device_id): """处理设备事件""" if self.on_device_callback: self.on_device_callback(event_type, device_id) print(f"[设备事件] {event_type}: {device_id}") class AudioReceiver: """音频接收器,处理JSON格式的音频数据包""" def __init__(self, host_port): self.host_port = host_port self.socket = None self.running = False self.buffer = bytearray() # 接收缓冲区 self.audio_callback = None self.command_callback = None def connect(self): """连接到设备""" try: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect(("localhost", self.host_port)) print(f"已连接到端口 {self.host_port}") return True except Exception as e: print(f"连接失败: {e}") self.socket = None return False def disconnect(self): """断开连接""" if self.socket: try: self.socket.close() except: pass self.socket = None def start_receiving(self, audio_callback=None, command_callback=None): """开始接收数据""" self.running = True self.audio_callback = audio_callback self.command_callback = command_callback threading.Thread(target=self._receive_loop, daemon=True).start() print("音频接收已启动") def stop_receiving(self): """停止接收数据""" self.running = False self.disconnect() print("音频接收已停止") def _receive_loop(self): """接收主循环""" while self.running: if not self.socket or not self._is_connected(): if not self.connect(): time.sleep(2) continue try: data = self.socket.recv(4096) if not data: print("连接断开,尝试重连...") self.disconnect() continue self.buffer.extend(data) self._parse_packets() except Exception as e: print(f"接收错误: {e}") self.disconnect() time.sleep(2) def _parse_packets(self): """解析接收到的数据包""" end_marker = b'\n\n' while True: marker_pos = self.buffer.find(end_marker) if marker_pos == -1: break # 没有完整数据包 # 提取完整数据包 packet_bytes = self.buffer[:marker_pos + len(end_marker)] self.buffer = self.buffer[marker_pos + len(end_marker):] try: # 解析JSON json_bytes = packet_bytes[:-len(end_marker)] json_str = json_bytes.decode('utf-8') packet = json.loads(json_str) # 处理不同类型的数据包 self._handle_packet(packet) except json.JSONDecodeError as e: print(f"JSON解析错误: {e}") except Exception as e: print(f"数据包处理错误: {e}") def _handle_packet(self, packet): """处理解析后的数据包""" packet_type = packet.get("type") if packet_type == "recording": # 处理录音数据 if self.audio_callback: audio_data = base64.b64decode(packet.get("data", "")) self.audio_callback(audio_data) elif packet_type in ["startRecorder", "stopRecord"]: # 处理命令 if self.command_callback: self.command_callback(packet_type) else: print(f"未知数据包类型: {packet_type}") def _is_connected(self): """检查连接状态""" if not self.socket: return False try: self.socket.sendall(b'') return True except: return False # 发送命令到安卓设备 def send_command(self, command_type, data=None): """发送命令到安卓设备""" if not self.socket or not self._is_connected(): print(f"发送失败: 未连接到设备") return False try: # 构建JSON数据包 packet = { "type": command_type, "data": base64.b64encode(data).decode('ascii') if data else None } # 转换为JSON字符串并添加结束标志 json_str = json.dumps(packet) packet_bytes = (json_str + "\n\n").encode('utf-8') # 发送数据包 self.socket.sendall(packet_bytes) print(f"已发送命令: {command_type}") return True except Exception as e: print(f"发送错误: {e}") return False def pcm_to_base64_str(pcm_data: bytes) -> str: """将PCM转为Base64编码字符串""" return base64.b64encode(pcm_data).decode('ascii') def pcm_to_utf8(pcm_data: bytearray) -> str: """将16位PCM音频数据转为UTF-8字符串""" def validate_pcm(data: bytearray) -> bool: """验证PCM数据有效性""" return len(data) % 2 == 0 # 16位PCM需为偶数长度 if not validate_pcm(pcm_data): raise ValueError("无效的PCM数据长度,16位PCM需为偶数长度") try: # 转为16位有符号整数数组(小端序) samples = np.frombuffer(pcm_data, dtype='<i2') # 标准化到0-255范围 normalized = ((samples - samples.min()) * (255 / (samples.max() - samples.min()))).astype(np.uint8) # 转换为UTF-8字符串 return bytes(normalized).decode('utf-8', errors='replace') except Exception as e: raise RuntimeError(f"转换失败: {str(e)}") def audio_data_handler(audio_data): """音频数据处理回调""" print(f"收到音频数据: {len(audio_data)} 字节") # 打印前32字节的十六进制表示 if len(audio_data) > 0: hex_preview = ' '.join(f'{b:02x}' for b in audio_data[:32]) print(f"前32字节: {hex_preview}...") # 调试用:将PCM转为UTF-8 if len(audio_data) < 1024: try: utf8_data = pcm_to_utf8(audio_data) print(f"UTF-8预览: {utf8_data[:30]}...") except: pass def command_handler(command_type): """命令处理回调""" if command_type == "startRecorder": print("[命令] 开始录音") elif command_type == "stopRecord": print("[命令] 停止录音") def on_device_callback(event, device_name): """设备事件回调""" if event == "connected": print(f"[设备已连接] {device_name}") # 设置端口转发 adb_executor = ADBExecutor() if adb_executor.setup_port_forwarding(device_name): # 连接并开始接收音频 global audio_receiver audio_receiver = AudioReceiver(ADBExecutor.HOST_PORT) if audio_receiver.connect(): audio_receiver.start_receiving( audio_callback=audio_data_handler, command_callback=command_handler ) print("已开始接收音频数据") else: print("连接失败") else: print("端口转发设置失败") elif event == "disconnected": print(f"[设备已断开连接] {device_name}") if 'audio_receiver' in globals(): audio_receiver.stop_receiving() # 主函数 def run_listener(): adb_executor = ADBExecutor() listener = AdbDeviceListener(adb_executor, interval=2, on_device_callback=on_device_callback) listener.start() try: while True: time.sleep(1) except KeyboardInterrupt: print("程序关闭,停止监听") listener.stop() if 'audio_receiver' in globals(): audio_receiver.stop_receiving() if __name__ == '__main__': run_listener() 把脚本和安卓端优化下 让安卓当客户端 python 当服务端去接收,现在是相反
06-27
### 使用 `System.arraycopy()` 方法进行数组操作 `System.arraycopy()` 是 Java 提供的一个高效数组复制方法,广泛用于数组元素的复制、移动、合并等操作。该方法由 `java.lang.System` 类提供,是静态方法,具有较高的执行效率,尤其适用于大规模数组的复制任务[^3]。 #### 方法签名 ```java public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) ``` - `src`:源数组。 - `srcPos`:源数组中的起始位置。 - `dest`:目标数组。 - `destPos`:目标数组中的起始位置。 - `length`:要复制的元素个数。 #### 使用示例 ##### 1. 复制整个数组 ```java int[] source = {1, 2, 3, 4, 5}; int[] destination = new int[source.length]; System.arraycopy(source, 0, destination, 0, source.length); // 输出复制后的数组 for (int i : destination) { System.out.print(i + " "); } // 输出:1 2 3 4 5 ``` 此方法可以实现将一个数组完整复制到另一个数组中,而不会影响原始数组的内容[^2]。 ##### 2. 复制数组的部分元素 ```java int[] source = {10, 20, 30, 40, 50}; int[] destination = new int[3]; System.arraycopy(source, 1, destination, 0, 3); // 输出复制后的数组 for (int i : destination) { System.out.print(i + " "); } // 输出:20 30 40 ``` 这种方式适用于只需要复制数组中某一段连续元素的场景,具有较高的灵活性[^1]。 ##### 3. 合并两个数组 ```java int[] array1 = {1, 2, 3}; int[] array2 = {4, 5, 6}; int[] mergedArray = new int[array1.length + array2.length]; System.arraycopy(array1, 0, mergedArray, 0, array1.length); System.arraycopy(array2, 0, mergedArray, array1.length, array2.length); // 输出合并后的数组 for (int i : mergedArray) { System.out.print(i + " "); } // 输出:1 2 3 4 5 6 ``` 通过多次调用 `arraycopy()` 方法,可以实现两个或多个数组的合并操作,适用于数据聚合场景[^1]。 ##### 4. 移动数组元素 ```java int[] array = {1, 2, 3, 4, 5}; // 将索引1到3的元素整体前移一位 System.arraycopy(array, 1, array, 0, 3); // 输出移动后的数组 for (int i : array) { System.out.print(i + " "); } // 输出:2 3 4 4 5 ``` 该方法可以实现数组内部元素的移动或覆盖,适用于实现队列、缓冲区等数据结构中的元素重排操作[^1]。 #### 性能优势 相较于使用 `for` 循环逐个复制元素,`System.arraycopy()` 是本地方法,底层由 C/C++ 实现,因此在处理大规模数组时性能更优,尤其在涉及成千上万个元素的复制任务中表现尤为明显[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值