基于Google BluetoothChat实现蓝牙图片传输,字节数组转Bitmap返回Null
问题
基于Google的BluetoothChat示例实现两台设备间图片传输,目前字符串传输正常,但图片传输时接收端将字节数组转Bitmap始终得到Null值,无法显示。相关代码如下:
发送端(BluetoothSend类)代码
send.setOnClickListener(view -> { Bitmap bitmap = BitmapFactory.decodeFile(filePath); int width = bitmap.getRowBytes(); int height = bitmap.getHeight(); int bmpSize = width * height; ByteBuffer byteBuffer = ByteBuffer.allocate(bmpSize); bitmap.copyPixelsToBuffer(byteBuffer); byte[] byteArray = byteBuffer.array(); sendUtils.write(byteArray); bitmap.recycle(); });
收发工具类ConnectedThread代码
/** * This thread runs during a connection with a remote device. * It handles all incoming and outgoing transmissions. */ private class ConnectedThread extends Thread { private final BluetoothSocket bluetoothsocket; private final InputStream inputStream; private final OutputStream outputStream; public ConnectedThread(BluetoothSocket socket) { bluetoothsocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = bluetoothsocket.getInputStream(); tmpOut = bluetoothsocket.getOutputStream(); } catch (IOException e) { Log.e("ConnectedThrd->Cons", "Socket not created."); e.printStackTrace(); } inputStream = tmpIn; outputStream = tmpOut; } public void run() { byte[] buffer = new byte[8192]; //1024 original int bytes; // Keep listening to the InputStream while connected while (true) { try { // Read from the InputStream try { bytes = inputStream.read(buffer); // Send the obtained bytes to the UI Activity handler.obtainMessage(BluetoothSend.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); handler.obtainMessage(BluetoothReceive.MESSAGE_READ, bytes, -1, buffer).sendToTarget(); } catch (NullPointerException n) { Log.e("ConnectedThrd->Run", n.getMessage()); } } catch (IOException e) { Log.e("ConnectedThrd->Run", "Connection Lost.", e); e.printStackTrace(); connectionLost(); break; } } } public void write(byte[] buffer) { try { try { outputStream.write(buffer); handler.obtainMessage(BluetoothSend.MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); handler.obtainMessage(BluetoothReceive.MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (NullPointerException n) { Log.e("ConnectedThrd->Write", "Bluetooth Socket is null: " + n.getMessage()); } } catch (IOException e) { Log.e("ConnectedThread->Write", "Empty write stream."); } } public void cancel() { try { bluetoothsocket.close(); } catch (IOException e) { Log.e("ConnectedThread->Cancel", "Failed to close socket."); } } }
接收端(BluetoothReceive类)Handler代码
case MESSAGE_READ: //Read message from sender byte[] bufferRead = (byte[]) message.obj; //bitmap decodedByte is null Bitmap decodedByte = BitmapFactory.decodeByteArray(bufferRead, 0, bufferRead.length); int height = decodedByte.getHeight(); int width = decodedByte.getWidth(); Bitmap.Config config = Bitmap.Config.valueOf(decodedByte.getConfig().name()); Bitmap bitmap_tmp = Bitmap.createBitmap(width, height, config); ByteBuffer buffer = ByteBuffer.wrap(bufferRead); bitmap_tmp.copyPixelsFromBuffer(buffer); fileView.setImageBitmap(bitmap_tmp); break;
问题分析与修复
核心错误点
- 数据格式不匹配:发送端通过
copyPixelsToBuffer获取的是Bitmap原始像素数据(无文件头),但BitmapFactory.decodeByteArray需要完整的图片文件字节流(如JPEG/PNG格式,包含文件头),直接解码像素数据必然返回Null。 - 分片接收未处理:蓝牙InputStream的
read方法是分批读取数据的,一次最多读取缓冲区大小(8192字节),当前代码仅处理单次读取的片段,未拼接成完整的图片数据。
修复步骤
1. 修改发送端:发送完整图片文件字节流
跳过Bitmap转换,直接读取图片文件的原始字节,同时先发送数据长度让接收端明确接收总量:
send.setOnClickListener(view -> { try { File file = new File(filePath); FileInputStream fis = new FileInputStream(file); byte[] byteArray = new byte[(int) file.length()]; fis.read(byteArray); fis.close(); // 先发送图片数据总长度(转成4字节数组) sendUtils.write(intToBytes(byteArray.length)); // 再发送图片字节数据 sendUtils.write(byteArray); } catch (IOException e) { e.printStackTrace(); } }); // 辅助方法:将int转为4字节数组 private byte[] intToBytes(int value) { return ByteBuffer.allocate(4).putInt(value).array(); }
2. 修改ConnectedThread:传递实际读取的字节片段
发送MESSAGE_READ时,只传递实际读取到的字节片段,而非整个缓冲区:
// run方法中修改消息发送逻辑 try { bytes = inputStream.read(buffer); // 仅给接收端发送消息,发送端无需接收自身数据 handler.obtainMessage(BluetoothReceive.MESSAGE_READ, bytes, -1, Arrays.copyOf(buffer, bytes)).sendToTarget(); } catch (NullPointerException n) { Log.e("ConnectedThrd->Run", n.getMessage()); }
3. 修改接收端:拼接分片数据并解码
在接收端类添加成员变量存储完整数据:
private byte[] completeImageData; private int totalBytesToReceive = -1; private int receivedBytes = 0;
然后更新Handler的MESSAGE_READ处理逻辑:
case MESSAGE_READ: byte[] receivedFragment = (byte[]) message.obj; if (totalBytesToReceive == -1) { // 首次接收的是数据长度,解析后初始化缓冲区 if (receivedFragment.length >=4) { totalBytesToReceive = ByteBuffer.wrap(receivedFragment).getInt(); completeImageData = new byte[totalBytesToReceive]; receivedBytes = 0; } } else { // 拼接当前片段到完整数据 System.arraycopy(receivedFragment, 0, completeImageData, receivedBytes, receivedFragment.length); receivedBytes += receivedFragment.length; // 判断是否接收完成 if (receivedBytes == totalBytesToReceive) { Bitmap decodedBitmap = BitmapFactory.decodeByteArray(completeImageData, 0, completeImageData.length); if (decodedBitmap != null) { fileView.setImageBitmap(decodedBitmap); } else { Log.e("Receive", "图片解码失败"); } // 重置状态,准备接收下一张图片 totalBytesToReceive = -1; receivedBytes = 0; } } break;
额外注意事项
- 大图片建议分块发送,避免一次性分配过大字节数组导致内存溢出。
- 可添加CRC校验机制,确保接收数据完整无误。
- 发送长度时需保证两端字节序(大端/小端)一致,避免解析错误。
内容的提问来源于stack exchange,提问作者Dennis Manicani Jr.
相关产品推荐
相关产品推荐

