在Android上通过BLE API发送大文件 [英] Sending Large File via BLE API on android

查看:191
本文介绍了在Android上通过BLE API发送大文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经创建了用于通过蓝牙LE发送大型ByteArray的BLE发送器类 发送过程的逻辑如下:

I have created BLE sender class for the sending large ByteArray via Bluetooth LE The logic of the send process following:

  1. 写入描述符以启用有关发送数据的特征的通知
  2. 通过以下方式通知外围设备有关数据发送过程的信息 写入相应的特征(数据大小:块大小: 块数)
  3. 等待外围设备通知块0发送数据发送特征
  4. 收到通知后,开始按20个字节的块(BLE限制)开始发送第一个块1000字节,其中每个块包含块编号和18字节的数据,发送1000字节后,发送已发送数据的校验和块.
  5. 外围设备通过校验和验证数据,并为下一个块通知描述符
  1. Write descriptor to enable notification on characteristics that sends data
  2. Notify peripheral about data sending process via writing to corresponding characteristics (Size of data: chunk size: number of chunks)
  3. Wait for peripheral to notify for chunk 0 to send on data sending characteristics
  4. On notification received start sending the first chunk 1000 byte by blocks of 20 bytes (BLE restriction) where each block contains block number and 18 bytes of data, after 1000 bytes sent, send block of checksum for the data sent
  5. Peripheral verify the data by the checksum and notify descriptor for the next chunk

我的问题是:有没有更好的方法? 我发现多次写入特征需要至少20毫秒的延迟.有什么办法可以避免这种情况?

My Question is: is there any better approach? I have found that writing characteristics multiple times requires some delay of at least 20 milliseconds. Is there any way to avoid this?

更改了实现方式,而不是20毫秒,我正在等待回调 onCharacteristicWrite 作为 建议 Emil .并且还更改了准备方法以减少18字节块发送之间的计算时间:

Changed the implementation instead of 20 millis, I'm waiting for a callback onCharacteristicWrite as Emil advised. and Also changed the prepare method to decrease calculation time between 18bytes blocks sends:

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)
                    }
                }

            }
        }


    }

推荐答案

等待20毫秒是怎么回事?使用特征写入来抽取数据的首选方法是先使用写无响应"(

What's this thing about waiting 20 ms? The preferred way to pump data using characteristic writes is to first use "Write Without Response" (https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#WRITE_TYPE_NO_RESPONSE), then perform a Write, then wait for the onCharacteristicWrite callback and then immediately perform the next Write. You need to wait for the onCharacteristicWrite callback since the API doesn't allow you to have multiple pending commands/requests at a time.

这篇关于在Android上通过BLE API发送大文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆