在 android 中通过 BLE 传输图像

Transfer Image throug BLE in android

我正在使用以下代码传输 1 MB 的图像。 如果在每个数据包之间实施线程延迟,图像将成功传输。 如果未设置线程延迟,则所有数据包都从 BluetoothGattServer 发送,但 BluetoothGattCallback 不会接收所有数据包。

任何人都可以指导在没有线程延迟的情况下发送数据包

在每个数据包之间实现线程

private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
                                   byte[] CHARACTERS) {
    boolean isComplete = false;
    runOnUiThread(() -> {
        tv_status.setText("Sending Data...!!");
        startTime = SystemClock.uptimeMillis();
        customHandler.postDelayed(updateTimerThread, 0);
    });

    // Check the data length is large how many times with Default Data (BLE)

    int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
    totalPackets = times;
    Log.i("", "CHARACTERS.length() " + CHARACTERS.length);

    byte[] packetNoByte;
    byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
    for (int time = 0; time <= times; time++) {

        final int remainingTime = time;
        if (!hasDisconnected) {
            this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mRelativeLayout.setVisibility(View.VISIBLE);
                    if (totalPackets != 0) {
                        showProgress(totalPackets, remainingTime);
                    }
                }
            });
        } else {
            runOnUiThread(() -> {
                mProgressBar.setProgress(0);
                tv_progress.setText(0 + "%");
                tv_timer.setText("00:00:00");
                tv_imageSize.setText("");
                tv_status.setText("");
                Toast.makeText(PeripheralRoleActivity.this, "Something went wrong, Please Try again", Toast.LENGTH_SHORT).show();
                customHandler.removeCallbacks(updateTimerThread);
            });
            return;
        }
        int a;
        int b;

        /**
         * @param THREAD_SLEEP_TIME_FOR_NOTIFICATION
         * this delay is placed to give a small pause while sending the data packe
         * */
        try {
            Thread.sleep(Constants.THREAD_SLEEP_TIME_FOR_NOTIFICATION);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sentPacket = sentPacket + 1;
        byte[] packetArray = Utils.getUtilsClass().toByteArray(sentPacket);
        packetNoByte = Arrays.copyOf(packetArray, packetArray.length);

        if (time == times) {
            Log.i("", "LAST PACKET ");
            int character_length = CHARACTERS.length
                    - DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
            byte[] sending_last_hex = new byte[character_length];
            a = (sending_continue_hex.length) * time;
            b = a + character_length;
            if(b-a ==0){
                return;
            }
            sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);

            byte[] last_packet =
                    new byte[packetNoByte.length + character_length];
            System.arraycopy(packetNoByte, 0, last_packet,
                    0, packetNoByte.length);
            System.arraycopy(sending_last_hex, 0, last_packet,
                    packetNoByte.length, sending_last_hex.length);


            Log.d("Sending packets", Arrays.toString(last_packet));
            // Set value for characteristic
            characteristic.setValue(last_packet);
            notifyCharacteristicChanged();
            isComplete = true;
            customHandler.removeCallbacks(updateTimerThread);
            currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
            Log.d("Collection", "End Time: " + currentDateTimeString);
            Utils.getUtilsClass().sendNotification(getApplicationContext(), "Data Transfer", "Transfer Complete");


        } else {

            Log.i("", "CONTINUE PACKET ");

            a = ((sending_continue_hex.length) * time);
            b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;

            sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);


            byte[] sending_continue_packet =
                    new byte[packetNoByte.length + sending_continue_hex.length];
            System.arraycopy(packetNoByte, 0, sending_continue_packet,
                    0, packetNoByte.length);
            System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
                    packetNoByte.length, sending_continue_hex.length);


            Log.d("data transfer a", String.valueOf(a));
            Log.d("data transfer b", String.valueOf(b));
            Log.d("data trans bytes", String.valueOf(sending_continue_hex.length));
            if(output == null){
                output = new ByteArrayOutputStream();
            }
            try {
                if {
                    characteristic.setValue(sending_continue_packet);
                    Log.d("Sending packets", Arrays.toString(sending_continue_packet));
                    notifyCharacteristicChanged();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        Log.d("Data byte", "times " + time);

        if (isComplete) {
            characteristic.setValue("Completed");
            notifyCharacteristicChanged();
        }
        runOnUiThread(() -> tv_status.setText("Data sent!!"));
    }
}

更新代码

//下面的函数用于将图片byte[]打成包,存入arraylist

private void breakPackets(byte[] CHARACTERS) {
        // Check the data length is large how many times with Default Data (BLE)

        int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
        totalPackets = times;
        packetList = new ArrayList<>();
        sendingPacket = 0;
        Log.i("", "CHARACTERS.length() " + CHARACTERS.length);


        byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
        for (int time = 0; time <= times; time++) {


            int a;
            int b;

            if (time == times) {
                Log.i("", "LAST PACKET ");
                int character_length = CHARACTERS.length
                        - DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
                byte[] sending_last_hex = new byte[character_length];
                a = (sending_continue_hex.length) * time;
                b = a + character_length;

                sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
                //packetList is an ArrayList<byte[]>
                packetList.add(sending_last_hex);
                startSendingPackets(sendingPacket);
            } else {
                a = (sending_continue_hex.length) * time;
                b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
                sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);

                packetList.add(sending_continue_hex);
            }
            Log.d("Data byte", "times " + time);

        }
    }

    //the following function is used to set the byte[] from the arraylist to the characteristics and then notify the characteristics
     private void startSendingPackets(int packet) {
        isCommand = false;
        mSampleCharacteristic.setValue(packetList.get(packet));
        notifyCharacteristicChanged();
        Log.i("packeting", "Sending  ------------> " + packet);
    }


    /*************************************************/



     @Override
        public void onNotificationSent(BluetoothDevice device, int status) {
            super.onNotificationSent(device, status);
            //check if status is success
            if (status == BluetoothGatt.GATT_SUCCESS) {
            //if status is not successful isExecutable is false and the else loop is executed to resend the same packet that has failed
                if (isExecutable) {
//                    Log.i("packeting", "Sent  ------------> " + sendingPacket);
                    sendingPacket = sendingPacket + 1;
                    int size = packetList.size();
                    if (sendingPacket <= size-1) {

                        startSendingPackets(sendingPacket);

                        Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
                    } else {
                        sendCommand("Completed");
                    }
                } else {
                    startSendingPackets(sendingPacket);
                    isExecutable = true;
                    Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
                }
            }else{
            //if status is not successful
                isExecutable = false;

                Log.d(MainActivity.TAG, "Notification sent. fail Status: " + status );
            }


        }

可在 https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback.html#onNotificationSent(android.bluetooth.BluetoothDevice,%20int):

的文档中阅读

When multiple notifications are to be sent, an application must wait for this callback to be received before sending additional notifications.

这意味着在您调用notifyCharacteristicChanged之后,您不能再次调用notifyCharacteristicChanged,直到收到回调onNotificationSent。因此,您需要删除 for 循环并重构代码以遵循 API 规则。

这样做的原因是为了获得流量控制。如果您只是以比 BLE link 的吞吐量更快的速度推送新数据包,则内部缓冲区会变满并且会发生数据包丢失。这就是延迟似乎有效的原因,但这不是一个可靠的解决方案,因此您应该等待 onNotificationSent 回调,因为这意味着 BLE 堆栈已准备好接受新数据包。