, Когда вы используете «Запись без ответа», вы будете качать данные так быстро, как это будет принято контроллером. Для 2 - нет никаких ограничений, кроме памяти (вы должны обрабатывать уведомления по крайней мере так же быстро, как они получены).

дал класс отправителя BLE для отправки большого ByteArray через Bluetooth LE. Логика процесса отправки следующая:

Написать дескриптор, чтобы включить уведомление о характеристиках, которые отправляют данныеУведомить периферийное устройство о процессе отправки данных путем записи соответствующих характеристик (размер данных: размер чанка: количество чанков)Дождитесь, пока периферийное устройство уведомит блок 0 об отправке данных о характеристиках отправки данныхПри получении уведомления начните отправку первого блока 1000 байтов блоками по 20 байтов (ограничение BLE), где каждый блок содержит номер блока и 18 байтов данных, после отправки 1000 байтов отправьте блок контрольной суммы для отправленных данныхПериферийная проверка данных по контрольной сумме и уведомление дескриптора для следующего чанка

Мой вопрос: есть ли лучший подход? Я обнаружил, что для записи характеристик несколько раз требуется некоторая задержка не менее 20 миллисекунд. Есть ли способ избежать этого?

Изменена реализация вместо 20 миллисЯ жду обратного звонкаonCharacteristicWrite какЭмиль рекомендуется. Также изменен метод подготовки, чтобы уменьшить время вычисления между 18-байтовыми блоками:

class BluetoothLEDataSender(
            val characteristicForSending: BluetoothGattCharacteristic,
            val characteristicForNotifyDataSend: BluetoothGattCharacteristic,
            private val config: BluetoothLESenderConfiguration = BluetoothLESenderConfiguration(),
            val  bluetoothLeService: WeakReference<BluetoothLeService>) : HandlerThread("BluetoothLEDataSender") {

    data class BluetoothLESenderConfiguration(val sendingIntervalMillis: Long = 20L, val chunkSize: Int = 1000, val retryForFailureInSeconds: Long = 3)
       private val  toaster by lazy { Toast.makeText(bluetoothLeService.get()!!,"",Toast.LENGTH_SHORT) }

    companion object {

        val ACTION_DATA_SEND_FINISHED = "somatix.com.bleplays.ACTION_DATA_SEND_FINISHED"
        val ACTION_DATA_SEND_FAILED = "somatix.com.bleplays.ACTION_DATA_SEND_FAILED"
    }

    lateinit var  dataToSend: List<BlocksQueue>
    val messageHandler by lazy { SenderHandler()}

    var currentIndex = 0

    public fun notifyDataState(receivedChecksum: String) {
        val msg = Message()
        msg.arg1 = receivedChecksum.toInt()
        messageHandler.sendMessage(msg)
    }
    inner class BlocksQueue(val initialCapacity:Int):ArrayBlockingQueue<ByteArray>(initialCapacity)
   inner class  BlockSendingTask:Runnable{
      override fun run() {
        executeOnUiThread({ toaster.setText("Executing block: $currentIndex")
        toaster.show()})
        sendNext()
      }
   }

        public fun sendMessage(messageByteArray: ByteArray) {
            start()
             dataToSend = prepareSending(messageByteArray)
            bluetoothLeService.get()?.setEnableNotification(characteristicForSending,true)
            val descriptor = characteristicForSending.getDescriptor(DESCRIPTOR_CONFIG_UUID)
            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            bluetoothLeService.get()?.writeDescriptor(descriptor)
            characteristicForNotifyDataSend.value = "${messageByteArray.size}:${config.chunkSize}:${dataToSend.size}".toByteArray()
            toaster.setText(String(characteristicForNotifyDataSend.value))
            toaster.show()
            messageHandler.postDelayed({bluetoothLeService.get()?.writeCharacteristic(characteristicForNotifyDataSend)}, config.sendingIntervalMillis)

        }


     private fun prepareSending(messageByteArray: ByteArray): ArrayList<BlocksQueue> {
       with(config)
        {
            var chunksNumber = messageByteArray.size / config.chunkSize
            chunksNumber = if (messageByteArray.size == chunksNumber * config.chunkSize) chunksNumber else chunksNumber + 1
            val chunksArray = ArrayList<BlocksQueue>()


           (0 until chunksNumber).mapTo(chunksArray) {
              val start = it * chunkSize
              val end = if ((start + chunkSize) > messageByteArray.size) messageByteArray.size else start + chunkSize
              val sliceArray = messageByteArray.sliceArray(start until end)
              listOfCheckSums.add(sliceArray.checkSum())
              var capacity = sliceArray.size / 18
              capacity = if(sliceArray.size - capacity*18 == 0) capacity else capacity + 1
              //Add place for checksum
              val queue = BlocksQueue(capacity+1)
              for(i in 0 until  capacity){
                val  start1 = i *18
                val end1 = if((start1 + 18)<sliceArray.size) start1 +18 else sliceArray.size
                queue.add(sliceArray.sliceArray(start1 until end1))
            }
            queue.add(sliceArray.checkSum().toByteArray())
            queue
         }
        return chunksArray
    }
}


    fun  sendNext(){
        val currentChunk = dataToSend.get(currentIndex)
        val peek = currentChunk.poll()
        if(peek != null)
        {
            if(currentChunk.initialCapacity > currentBlock+1)
            {
                val indexByteArray = if(currentBlock>9) "$currentBlock".toByteArray() else "0${currentBlock}".toByteArray()
               characteristicForSending.value = indexByteArray + peek
            }
            else{
               characteristicForSending.value = peek
            }

   bluetoothLeService.get()?.writeCharacteristic(characteristicForSending)
              currentBlock++
            }
            else
            {
                Log.i(TAG, "Finished chunk $currentIndex")
                currentBlock = 0
             }

         }


        private val TAG= "BluetoothLeService"

        @SuppressLint("HandlerLeak")
        inner class SenderHandler:Handler(looper){
            private var failureCheck:FailureCheck? = null
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                    currentIndex = msg.arg1
                    if(currentIndex < dataToSend.size)
                    {
                        if (currentIndex!= 0 &&  failureCheck != null)
                        {
                            removeCallbacks(failureCheck)
                        }
                        failureCheck = FailureCheck(currentIndex)
                        post(BlockSendingTask())
                        postDelayed(failureCheck,TimeUnit.MILLISECONDS.convert(config.retryForFailureInSeconds,TimeUnit.SECONDS))


                     }
                    else {
                        if (currentIndex!= 0 &&  failureCheck != null)
                        {
                            removeCallbacks(failureCheck)
                        }
                        val intent= Intent(ACTION_DATA_SEND_FINISHED)
                        bluetoothLeService.get()?.sendBroadcast(intent)
                    }

            }
            private inner class FailureCheck(val index:Int):Runnable{
                override fun run() {
                    if (index==currentIndex){
                        val intent= Intent(ACTION_DATA_SEND_FAILED)
                        bluetoothLeService.get()?.sendBroadcast(intent)
                    }
                }

            }
        }


    }

Ответы на вопрос(2)

Ваш ответ на вопрос