You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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;

问题分析与修复

核心错误点

  1. 数据格式不匹配:发送端通过copyPixelsToBuffer获取的是Bitmap原始像素数据(无文件头),但BitmapFactory.decodeByteArray需要完整的图片文件字节流(如JPEG/PNG格式,包含文件头),直接解码像素数据必然返回Null。
  2. 分片接收未处理:蓝牙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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 01:31:15