Android平台基于RFCOMM套接字实现蓝牙传输文本/音视频文件问题
Android RFCOMM蓝牙套接字实现文本/图片/音频传输完整指南
看你现在在搞Android蓝牙RFCOMM的跨设备传输,代码里已经搭了按钮点击的基础框架,我来帮你把这个功能补全,涵盖文本、图片、音频这几种常见类型的传输,顺便把权限适配和细节处理都捋清楚,避免踩坑。
一、先把权限配置到位
Android不同版本的蓝牙和文件权限差异很大,先在AndroidManifest.xml里把该加的权限加上:
<!-- 蓝牙核心权限 --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <!-- 文件读写权限(适配全版本) --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="33" />
然后你的checkBTPermissions()和file_permission()方法可以这么写,完美适配Android 12+的权限要求:
private void checkBTPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { String[] btPermissions = { Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN }; if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, btPermissions, 100); } } else { String[] oldBtPermissions = { Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN }; if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, oldBtPermissions, 100); } } } private void file_permission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } } else { String[] filePermissions = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, filePermissions, 200); } } }
二、完善RFCOMM连接与传输逻辑
首先假设你已经有了mBluetoothConnection这个蓝牙连接管理类,核心是把write()方法写健壮,因为不管是文本还是文件,本质都是传输字节流:
public class BluetoothConnection { private OutputStream mOutputStream; // 初始化时传入已建立的RFCOMM套接字,获取输出流 public BluetoothConnection(BluetoothSocket socket) { try { mOutputStream = socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); } } public void write(byte[] bytes) { try { if (mOutputStream != null) { // 先发送数据长度,让接收端知道要接收多少字节,避免粘包 mOutputStream.write(intToBytes(bytes.length)); mOutputStream.write(bytes); mOutputStream.flush(); } } catch (IOException e) { e.printStackTrace(); // 这里可以加个Toast提示发送失败 } } // 把int转成4字节数组,用于传输长度标识 private byte[] intToBytes(int value) { return new byte[]{ (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; } }
接下来分类型处理传输:
1. 传输文本
直接把输入框的文本转成UTF-8字节数组发送就行:
send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { checkBTPermissions(); file_permission(); // 传输文本逻辑 String text = etSend.getText().toString().trim(); if (!text.isEmpty() && mBluetoothConnection != null) { byte[] textBytes = text.getBytes(StandardCharsets.UTF_8); mBluetoothConnection.write(textBytes); Toast.makeText(MainActivity.this, "文本已发送", Toast.LENGTH_SHORT).show(); } } });
2. 传输图片/音频文件
文件传输的核心是把文件读成字节流,这里给个基础实现,如果是大文件(比如几百M的音频),建议改成分段读取发送,避免OOM:
// 通用文件发送方法 private void sendFile(String filePath) { File targetFile = new File(filePath); if (!targetFile.exists() || !targetFile.isFile()) { Toast.makeText(this, "文件不存在或不是合法文件", Toast.LENGTH_SHORT).show(); return; } try { FileInputStream fis = new FileInputStream(targetFile); byte[] fileBytes = new byte[(int) targetFile.length()]; fis.read(fileBytes); fis.close(); if (mBluetoothConnection != null) { mBluetoothConnection.write(fileBytes); Toast.makeText(this, "文件发送成功", Toast.LENGTH_SHORT).show(); } } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "文件读取失败", Toast.LENGTH_SHORT).show(); } }
然后在按钮点击里调用即可(比如你要传/sdcard/bluetooth/tom.mp3):
send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { checkBTPermissions(); file_permission(); // 传输音频/图片文件 String filePath = "/sdcard/bluetooth/tom.mp3"; // 换成你的文件路径 sendFile(filePath); } });
三、接收端的对应处理(重要!不然发了收不到)
接收端需要先读取4字节的长度标识,再读取对应长度的数据,避免粘包:
public class BluetoothReceiver { private InputStream mInputStream; public BluetoothReceiver(BluetoothSocket socket) { try { mInputStream = socket.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } public byte[] read() throws IOException { // 先读取4字节的数据长度 byte[] lengthBytes = new byte[4]; mInputStream.read(lengthBytes); int dataLength = bytesToInt(lengthBytes); // 循环读取完整数据 byte[] dataBytes = new byte[dataLength]; int totalRead = 0; while (totalRead < dataLength) { int readCount = mInputStream.read(dataBytes, totalRead, dataLength - totalRead); if (readCount == -1) break; // 连接断开 totalRead += readCount; } return dataBytes; } // 把4字节数组转回int private int bytesToInt(byte[] bytes) { return ((bytes[0] & 0xFF) << 24) | ((bytes[1] & 0xFF) << 16) | ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF); } }
然后根据接收类型处理:
// 接收文本 byte[] receivedBytes = bluetoothReceiver.read(); String receivedText = new String(receivedBytes, StandardCharsets.UTF_8); // 可以把receivedText显示到UI上 // 接收文件 byte[] receivedFileBytes = bluetoothReceiver.read(); File saveFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "received.jpg"); FileOutputStream fos = new FileOutputStream(saveFile); fos.write(receivedFileBytes); fos.close();
四、避坑小贴士
- 大文件分段传输:如果文件超过100M,一定要改成每次读1024或4096字节循环发送,不然会直接OOM
- RFCOMM套接字创建:服务器端要用
BluetoothAdapter.listenUsingRfcommWithServiceRecord来监听,客户端用device.createRfcommSocketToServiceRecord来连接 - 文件路径优化:尽量用
Context.getExternalFilesDir()获取合法的外部存储路径,不要硬编码/sdcard,适配Android 10+的分区存储 - 权限回调处理:别忘了在
onRequestPermissionsResult里处理权限申请结果,确保权限通过后再执行传输逻辑
内容的提问来源于stack exchange,提问作者pranjal khanduri
相关产品推荐
相关产品推荐

