RxJava Android Bluetooth 经典蓝牙 SDK 项目开发

Android 经典蓝牙项目开发:

分三部分

一: btcore (服务端和客户端的核心代码)
二: 服务端使用实例
三: 客户端使用实例


btcore 分服务端和客户端

服务端:
1: 启动服务监听客户端请求
2: 处理客户端数据请求并返回数据
客户端:
1: 连接服务端 (扫描)
2: 数据处理(数据组转、队列管理、超时处理、并发)
3: 重连


使用蓝牙前需要判断蓝牙是否可用、开关是否开启、是否授权
/**
 * 蓝牙是否可用
 */
fun bluetoothEnable() : Boolean {
    return bluetoothAdapter != null
}

/**
 * 蓝牙是否打开
 */
fun bluetoothIsOpen() : Boolean {
    return bluetoothAdapter.isEnabled ?: false
}

/**
 * 检查权限
 */
fun havePermission() : Boolean {
    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
        ActivityCompat.checkSelfPermission(content, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
    } else {
        ActivityCompat.checkSelfPermission(content, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
    }
}
服务端设置可被扫描连接 (0表示持续可扫描)
// 属性
private lateinit var searchDeviceLauncher: ActivityResultLauncher<Intent>

// onCreate 中初始化
searchDeviceLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
    bluetoothManager!!.startAdvertiser()
}

// 需要启动时执行
private fun startDiscoverable() {
    var requestDiscoverIntent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
    requestDiscoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0)
    searchDeviceLauncher.launch(requestDiscoverIntent)
}
一: 服务端核心类
1: AcceptThread 等待客户端连接线程(阻塞式)
// 核心代码
override fun run() {
    super.run()
    var shouldLoop : Boolean = true

    while (shouldLoop) {
        btSocket = try {
            Log.i("AcceptThread", " 服务正在等待监听..... ")
            serverSocket?.accept()
        } catch (e: Exception) {
            shouldLoop = false
            null
        }
        btSocket?.also {
            serverSocket?.close()
            shouldLoop = false
            // 通知有客户端设备连接
            acceptSubject.onNext(it)

            Log.i("BluetoothSocket === : ", "$it")
        }
    }
}
2: CommunicationThread 通讯句柄类(客户端也需要), 建立连接后的 BluetoothSocket 可以打开 输入输出两个数据流,读和写数据
// 核心代码

init {
    try {
        inputStream = DataInputStream(socket.inputStream)
        outputStream = DataOutputStream(socket.outputStream)

        Log.i(TAG, "会话建立完成")

    } catch (e: IOException) {
        Log.e(TAG, "获取输出输入流失败")
    }
}

override fun run() {
    super.run()
    var numBytes: Int
    while (true) {
        val mmBuffer: ByteArray = ByteArray(2000)
        // 阻塞 收数据
        if (inputStream != null && socket.isConnected) {
            numBytes = try {
                Log.i(TAG, "run: reading")
                inputStream!!.read(mmBuffer)
            } catch (e: Exception) {
                Log.e(TAG, "读取数据异常 $e")
                readStateSubject.onNext(true)
                break
            }
            if (numBytes > 0) {
                val resultByteArray: ByteArray = ByteArray(numBytes)
                System.arraycopy(mmBuffer, 0, resultByteArray, 0, numBytes)
                dataSubject.onNext(Pair<Int, ByteArray>(numBytes, resultByteArray))
            }
        }
    }
}

/**
 * 写数据
 */
fun write(bytes: ByteArray) : Boolean {
    if (!socket.isConnected) {
        return false
    }
    return try {
        outputStream?.write(bytes)
        outputStream?.flush()
        true
    } catch (e: IOException) {
        false
    }
}
3: BluetoothServerManger 管理连接、数据处理、状态监听
// 核心代码

 /**
 * 作为服务端启动监听(广播)
 */
fun startAdvertiser() {
    if (acceptThread == null) {
        acceptThread = AcceptThread(uuid = BluetoothServerManger.serverUUID)
        acceptThread!!.acceptSubject
            .subscribeBy(
                onNext = {
                    bluetoothSocket = it
                    startServerCommunication(it)
                }
            )
        acceptThread!!.start()
    }
}

/**
 * 有客户端接入,建立连接
 */
private fun startServerCommunication(bs: BluetoothSocket) {
    serverCommThread = CommunicationThread(bs)

    serverCommThread!!.readStateSubject
        .subscribeBy(onNext = {
            reStartAdvertiser()
        }, onError = {

        })

    // 收到客户端数据
    serverCommThread!!.dataSubject
        .subscribeBy(
            onNext = {
                val valueString = it.second.toString(Charset.defaultCharset())
                var d = String(it.second)
                val string2 = String(it.second, Charsets.UTF_16)
                Log.i(BluetoothClientManager.TAG, "服务端收到数据 size: ${it.first}; data: $d $valueString  ${it.second} $string2")
                val receiverData =  it.second
                // 解析报文头为: AABB, 获取到 taskId 用于返回
                if (receiverData.size > 4 &&
                    receiverData[0] == 0xAA.toByte() &&
                    receiverData[1] == 0xBB.toByte()) {
                    tempTask = BTTask()
                    val rTaskId = (receiverData[3].toInt() and 0xFF shl 8) or (receiverData[2].toInt() and 0xFF)
                    tempTask!!.taskId = rTaskId
                }
                if (tempTask == null) {
                    return@subscribeBy
                }
                tempTask?.addBuffer(receiverData)
                // 检测数据接受完成, 返回给客户端
                if (tempTask?.checkComplete() == true) {
                    val responseByte = byteArrayOf(1, 2, 3, 4, 5)
                    responseData(tempTask!!.buildCmdContent(responseByte))
                    Log.i(BluetoothClientManager.TAG, "服务端回复了数据")
                }
            },
            onError = {
                Log.i(BluetoothClientManager.TAG, "startServerCommunication: 出错了")
            }
        )
    serverCommThread!!.start()
}

/**
 * 监听手机蓝牙开关变化,做出处理
 */
private fun registerBluetoothStateEvent() {
    val intent =  IntentFilter()
    intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
    intent.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
    intent.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)

    val stateReceiver = object : BroadcastReceiver() {
        override fun onReceive(p0: Context?, intent: Intent?) {
            if (intent == null) {
                return
            }
            val action = intent!!.action
            action?.let {
                when (it) {
                    BluetoothAdapter.ACTION_STATE_CHANGED -> {
                        when (val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)) {
                            BluetoothAdapter.STATE_OFF -> stateOff()
                            BluetoothAdapter.STATE_ON -> stateOn()  // 重启监听服务
                            else -> Log.i(BluetoothClientManager.TAG, "unSupport $state")
                        }
                    }
                    BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                        Log.i(TAG, "BluetoothDevice: ACTION_ACL_DISCONNECTED ")
                    }
                    BluetoothDevice.ACTION_ACL_CONNECTED -> {
                        Log.i(TAG, "BluetoothDevice: ACTION_ACL_CONNECTED ")
                    }
                    else -> {}
                }
            }
        }
    }
    content.registerReceiver(stateReceiver, intent)
}
二: 客户端核心类
1:ConnectThread 用于连接服务端的线程
// 核心代码

override fun run() {
    super.run()
    // 连接前,取消扫描
    BluetoothAdapter.getDefaultAdapter()?.cancelDiscovery()
    if (socket != null) {
        try {
            socket!!.connect()      // 5 秒会自动超时
            connectSubject.onNext(socket)
        } catch (e: IOException) {
            Log.i(TAG, "请求连接异常: $e")
            socket!!.close()
            connectSubject.onError(Error())
        }
    }
}
2:BluetoothClientManager 客户端管理器, 处理客户端发起连接请求、队列、重连
注册系统蓝牙开关状态:
/**
 * 注册蓝牙状态变化
 */
private fun registerBluetoothStateEvent() {
    val intent =  IntentFilter()
    intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
    intent.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
    intent.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)

    val stateReceiver = object : BroadcastReceiver() {
        override fun onReceive(p0: Context?, intent: Intent?) {
            if (intent == null) {
                return
            }
            val action = intent!!.action
            action?.let {
                when (it) {
                    BluetoothAdapter.ACTION_STATE_CHANGED -> {
                        when (val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)) {
                            BluetoothAdapter.STATE_OFF -> stateOff()
                            BluetoothAdapter.STATE_ON -> stateOn()
                            else -> Log.i(TAG, "unSupport $state")
                        }
                    }
                    BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                        Log.i(BluetoothServerManger.TAG, "onReceive:ACTION_ACL_DISCONNECTED ")
                        handleDisconnect()
                    }
                    BluetoothDevice.ACTION_ACL_CONNECTED -> {
                        Log.i(BluetoothServerManger.TAG, "onReceive:ACTION_ACL_DISCONNECTED ")
                        connected = true
                    }
                    else -> {}
                }
            }
        }
    }
    content.registerReceiver(stateReceiver, intent)
}
客户端扫描部分:
/**
 * 注册扫描事件、获取扫描结果
 */
private val receiver = object : BroadcastReceiver() {
    override fun onReceive(p0: Context?, intent: Intent?) {
        intent?.let {
            when(intent.action) {
                BluetoothDevice.ACTION_FOUND -> {
                    val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                    device?.let {
                        val deviceHardwareAddress = device.address
                        Log.i(BluetoothClientManager.TAG, "onReceive: $deviceHardwareAddress")
                        scanSubject.onNext(Pair(BluetoothDevice.ACTION_FOUND, it))
                    }
                }
                BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                    Log.i(BluetoothClientManager.TAG,"scan start")
                    scanSubject.onNext(Pair(BluetoothAdapter.ACTION_DISCOVERY_STARTED, null))
                }
                BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                    unRegisterBluetoothEvent() // 取消注册
                    Log.i(BluetoothClientManager.TAG,"scan end")
                    scanSubject.onNext(Pair(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, null))
                }
                else -> {}
            }
        }
    }
}


/**
 * 开始扫描
 */
@SuppressLint("MissingPermission")
fun startDiscoverDevice() : Boolean {

    if (!bluetoothEnable()) {
        return false
    }

    if (!bluetoothIsOpen()) {
        return false
    }

    if (!havePermission()) {
        return false
    }
    registerBluetoothEvent()
    return bluetoothAdapter.startDiscovery()
}
连接部分:在ACTION_FOUND 过滤到设备,即可发起连接
// 核心代码

/**
* 通过对象连接
*/
@Synchronized
fun connectDevice(device: BluetoothDevice, timeout: Long) : Observable<Boolean> {
    if (connecting) {
        return Observable.error(BTException("正在连接"))
    }
    connecting = true
    // 记录连接历史,用于重连
    historyAddress = device.address

    unRegisterBluetoothEvent()
    cancelDiscovery()

    return Observable.create<Boolean> { emitter ->
        if (connected) {
            connecting = false
            Log.i(TAG, "connectDevice: 已连接 ")
            emitter.onNext(true)
            return@create
        }
         val connectThread = ConnectThread(device)
        //  connect 会阻塞 5 秒后超时, 无需自己加超时处理
        connectThread.connectSubject
            .subscribeBy(onNext = {
                connecting = false
                startClientCommunication(it)
                emitter.onNext(true)
            }, onError = {
                connecting = false
                Log.i(BluetoothClientManager.TAG, "尝试连接失败")
                emitter.onError(it)
            })
        connectThread.start()
    }
}

/**
* 客户端建立可读写通道
*/
private fun startClientCommunication(bs: BluetoothSocket) {
    clientCommThread = CommunicationThread(bs)
    clientCommThread!!.dataSubject
        .subscribeBy(
            onNext = {
                val valueString = it.second.toString(Charset.defaultCharset())
                var d = String(it.second)

                Log.i(BluetoothClientManager.TAG, "客户端收到数据 size: ${it.first}; $d  ${it.second} data: $valueString ")
                val source = it.second
                val taskId = (source[3].toInt() and 0xFF shl 8) or (source[2].toInt() and 0xFF)
                val ct =  currentTasks.first { t -> t.taskId == taskId }
                ct.addBuffer(source)

                if ( ct.checkComplete()) {
                    // 接受数据完成,next
                    val type = ct.type
                    ct.subscribe?.onNext(true)
                    ct.subscribe?.onComplete()
                    ct.complete()           // 完成 停止 超时记时
                    if (currentTasks.contains(ct))  currentTasks.remove(ct)
                    if (taskList.contains(ct))  taskList.remove(ct)
                    Log.i(BluetoothClientManager.TAG, "该任务已完成 task id: $taskId")
                    //下一个
                    trigger(type)
                }
            },
            onError = {
                Log.i(BluetoothClientManager.TAG, "爆发异常")
            }
        )
    clientCommThread!!.start()
}
数据处理:队列处理,先进先出
// 核心代码

/**
 * 数据发送
 */
@Synchronized
fun sendData(timeout: Long, message: String, type: Int = 0) : Observable<Boolean> {

    if (!bluetoothEnable()) {
        return Observable.error(BTException("蓝牙不可用"))
    }

    if (!bluetoothIsOpen()) {
        return Observable.error(BTException("蓝牙未开启"))
    }

    // 发起重连
    if (!connected) {
        if (historyAddress == null) {
            return Observable.error(BTException("蓝牙未连接"))
        }
        connectDeviceWithAddress(historyAddress!!)
    }
    return Observable.create<Boolean> { emitter ->
        // 存入队列中
        taskList.add(BTTask(timeout = timeout, sourceData = message.toByteArray(), subscribe = emitter))
        if (connected) {
            trigger(type)
        }
    }
}

/**
 * 检测是否空闲, 空闲时即可发送
 *
 * type:任务类别,只有不同的type 才会堵塞,(如所有任务都按顺序,使用同一type即可)
 */
private fun trigger(type: Int) {
    if (!connected) {
        return
    }
    // 判断是否有同类型的指令正在执行
    if (currentTasks.any { it.type == type }) {
        return
    }
    if (taskList.size <= 0) {
        return
    }
    val btTask = taskList.first { it.type == type }
    btTask?.let {
        currentTasks.add(it)
        val cmdContent = btTask.buildCmdContent(it.sourceData!!)
        btTask.begin()
        btTask.timeoutSubject.subscribeBy(
            onError = { e ->
                // 指令超时 下一条
                btTask.subscribe?.onError(e)
                btTask.subscribe?.onComplete()
                if (currentTasks.contains(btTask))  currentTasks.remove(btTask)
                if (taskList.contains(btTask))  taskList.remove(btTask)
                Log.i(BluetoothClientManager.TAG, "该任务已超时 task id: ${btTask.taskId}")
                trigger(type)
            }
        )
        val sendResult = clientCommThread!!.write(cmdContent)
        Log.i(BluetoothClientManager.TAG, "客户端返回发送结果: $sendResult ")
    }
}
完整代码:

https://github.com/tlin2011/android-bluetooth-sdk.git

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容