首页 > 解决方案 > 在android中通过BLE传输图像

问题描述

我正在使用以下代码传输 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!!"));
    }
}

更新代码

//以下函数用于将图像字节[]分成数据包并将其存储在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 );
            }


        }

标签: androidimagebluetooth-lowenergytransfer

解决方案


可以在https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback.html#onNotificationSent(android.bluetooth.BluetoothDevice,%20int)的文档中阅读:

当要发送多个通知时,应用程序必须等待收到此回调,然后才能发送其他通知。

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

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


推荐阅读