Android蓝牙通信

本文详细介绍了在Android平台上进行蓝牙通信的步骤,包括添加权限、获取蓝牙设备列表、扫描设备、5.0以上与5.0以下的不同扫描方式、蓝牙设备的配对、取消配对和删除配对信息。此外,还展示了作为中央设备如何实现BluetoothGatt连接,发送数据以及建立双向服务通信的过程。内容涵盖了蓝牙连接状态监听、服务发现和数据传输的回调方法。
摘要由CSDN通过智能技术生成

添加权限

   <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

获取蓝牙设备列表

通过广播方式获取蓝牙设备

val broadcastReceiver:BroadcastReceiver =object: BroadcastReceiver(){
    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        //获取设备
        val device  =intent.getParcelableExtra<BluetoothDevice>
                            (BluetoothDevice.EXTRA_DEVICE)
        //获取信号强度
        int rssi = intent.extras.getShort(BluetoothDevice.EXTRA_RSSI).toInt()
        if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)){
            //todo 开始扫描
        }else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){
            //结束扫描            
        }else if(action.equals(BluetoothDevice.ACTION_FOUND)){
            //找到蓝牙设备
        }
    }
}

private fun startScan(){
    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled){
        adapter.starterDisconvery()
    }
}

private fun stopScan(){
    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled){
        adapter.cancelDiscovery()
    }
}

5.0以下代码方式获取蓝牙设备

private fun startScan(){
    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled){
        adapter.startLeScan(mScanCallback)
    }
}

private fun stopScan(){
      BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled){
        adapter.stopLeScan(mScanCallback )
    }
}

private val mScanCallback = BluetoothAdapter.LeScanCallback {
    device,rssi,scanRecord ->
    //device 蓝牙设备信息
    //rssi 信号强度
    //scanRecord  远程蓝牙设备官博数据
}

5.0以上代码获取蓝牙设备

private fun starScan(){
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled){
        val setting    ScanSettings.Builder()
        //扫描模式设置
        .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
        .setMatchMode(ScanSettings.MATCH_MODE_STICKY)
        .build()
        adapter.startScan(null,setting ,mScanCallback )
    }
}

private val mLeScanCallback = object : ScanCallback(){
    override fun onBatchScanResults(results: MutableList<ScanResult>?) {
    super.onBatchScanResults(results)
    results?.forEach { result ->
        Log.e(
            TAG, "${result.device?.name},${result.device?.address},${result.rssi},"
        )
    }
}

    override fun onScanFailed(errorCode: Int) {
        super.onScanFailed(errorCode)
        Toast.makeText(requireContext(), getText(R.string.scan_falied), Toast.LENGTH_LONG)
            .show()
    }
    
    override fun onScanResult(callbackType: Int, result: ScanResult?) {
        super.onScanResult(callbackType, result)
        if (result != null) {
            Log.e(TAG, "${result.device?.name},${result.device?.address},${result.rssi},")
            //所有结果定义在ScanResult中
            // result.device
            // result.rssi
            // result.scanRecord
        }
    }
}

private fun stopScan(){
      BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter()
    if(adapter.isEnabled)
    mBLEScanner?.stopScan(mLeScanCallback)
}

蓝牙配对

配对

private fun pair(device: BluetoothDevice) {
    val method = device.javaClass.getMethod("createBond")
    method.invoke(device)
}

取消配对

//取消配对
private fun cancelPair(device:BluetoothDevcie):Boolean?{
    val method = device.javaClass.getMethod("cancelBondProcess")
    return    method.invoke(device)
}

删除配对信息

private fun deletePair(device:BluetoothDevice){
     val method = device.javaClass.getMethod("removeBond")
    return    method.invoke(device)
}

监听配对结果

//监听蓝牙配对广播
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        //蓝牙配对广播
        if (intent.action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
            val device =
                intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
            when (device?.bondState) {
                //配对失败
                BluetoothDevice.BOND_NONE -> {
                    alertDialog!!.setMessage(getString(R.string.pair_cancel))
                    alertDialog!!.getButton(DialogInterface.BUTTON_POSITIVE).isEnabled = true
                }
                //配对中...
                BluetoothDevice.BOND_BONDING -> {
                    alertDialog!!.setMessage(getString(R.string.pairing))
                    alertDialog!!.getButton(DialogInterface.BUTTON_POSITIVE).isEnabled = false
                }
                // 配对成功
                BluetoothDevice.BOND_BONDED -> {
                    alertDialog?.setMessage(getString(R.string.pair_success))
                    alertDialog?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = true
                    homeViewModel.updateDeviceStatus(device.address)
                }
            }
    }
 }

作为中央BluetoothGatt的实现

低功耗蓝牙的基本概念:
在BLE协议中,有两个角色,周边(Periphery)和中央(Central)。周边是数据的提供者,中央是数据的使用和处理者。在Android SDK里面,Android4.3以后手机可以作为中央使用;Android5.0以后手机才可以作为周边使用,即此时的手机可以作为BLE设备(如可穿戴设备、手环、智能锁、心率测量仪等)来为中央提供数据。
一个中央可以同时连接多个周边,但一个周边某一时刻只能连接一个中央。

建立连接

//建立连接
fun connectGatt(var address:String) {
    val device = bluetoothAdapter.getRemoteDevice(address)
    if (bluetoothAdapter.isEnabled) {
        device.connectGatt(requireContext(), false, bluetoothGattCallback)
    }
}

//蓝牙连接实体类 回调都是在子线程中,不可做更新 UI 操作
private val bluetoothGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
        override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status)
            Log.e(TAG, "onPhyUpdate")
        }

        override fun onPhyRead(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
            super.onPhyRead(gatt, txPhy, rxPhy, status)
            Log.e(TAG, "onPhyRead")
        }
        
        //监听连接状态
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            Log.e(TAG, "$status , $newState")
            //连接状态
            if (status == BluetoothGatt.GATT_SUCCESS) {
                when (newState) {
                    BluetoothProfile.STATE_DISCONNECTED -> {
                        gatt.close()
                        //连接状态,断开连接后的操作
                    }
                    BluetoothProfile.STATE_CONNECTED ->                 
                        //发现服务
                        gatt.discoverServices()
                }
            } else {
                val msg = mHandler.obtainMessage(SHOW_TIPS)
                mHandler.sendMessage(msg)
                mHandler.sendEmptyMessage(CLOSE_LOADING)
            }
            
            //当 status === 133时需要清楚缓存,断开连接,然后重新开始连接
            super.onConnectionStateChange(gatt, status, newState)
        }
        //蓝牙连接上后,查看对方可用的服务信息
        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            super.onServicesDiscovered(gatt, status)
            mGatt = gatt
             //todo 查找服务列表
             //根据服务UUID 查找对应服务信息
             // 通过UUID查询指定服务下的 功能列表 characteristic
val gattService = gatt.getService(UUID.fromString(mServiceUUID))
if (gattService == null) {
    Log.e(
        TAG,
        "onServicesDiscovered-->" + "获取服务指定uuid:" + mServiceUUID + "的BluetoothGattService为空,请联系外设设备开发商确认uuid是否正确"
    )
}
    //根据指定特征值uuid获取指定的特征值二
    //根据指定特征值uuid获取指定的特征值二
    mWriteGattCharacteristic1 =
        gattService.getCharacteristic(UUID.fromString(mWriteCharacteristicUUID1))
    mWriteGattCharacteristic2 =
        gattService.getCharacteristic(UUID.fromString(mWriteCharacteristicUUID2))
                          // 发送数据
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic?
        ) {
            super.onCharacteristicChanged(gatt, characteristic)
            Log.d(
                TAG,
                "onCharacteristicChanged-->" + characteristic!!.uuid + "," + characteristic.value.toString()
            )
            //过滤,判断是否是目标特征值
        }

        //发送数据后的回调,可以在此检测发送的数据包是否有异常
        override fun onCharacteristicWrite(
            gatt: BluetoothGatt?,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            super.onCharacteristicWrite(gatt, characteristic, status)
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(
                    TAG,
                    "onCharacteristicWrite:发送数据成功:" + BleHelp.binaryToHexString(characteristic.value)
                )
            } else Log.e(TAG, "onCharacteristicWrite:发送数据失败")
        }
    }

发送数据

// 每次发送数据大小不能超过20字节
private fun sendData(gatt: BluetoothGatt) {
    Thread {
        Log.e(TAG, "发送数据开始")
        val data = DataUtils.getData()
        //写入控制开始
        mWriteGattCharacteristic1.value = byteArrayOf(0x00)
        gatt.writeCharacteristic(mWriteGattCharacteristic1)
        for (cmd in data) {
            Log.e(TAG, BleHelp.binaryToHexString(cmd))
            sendData2(cmd, gatt)
            Thread.sleep(200)
        }

        //写入控制结束
        mWriteGattCharacteristic1.value = byteArrayOf(0x01)
        gatt.writeCharacteristic(mWriteGattCharacteristic1)
        Log.e(TAG, "发送数据完成")
    }.start()
}

双向服务通信

为了在两台设备上创建一个连接,你必须实现服务器端和客户端两头的机制,因为一个设备必须打开一个服务器socket,而另一个设备初始化创建(使用服务器设备的MAC地址来初始化一个连接)。当他们在相同的RFCOMM通道上有一个已连接的 BluetoothSocket 时,服务器和客户被认为是互相连接了。

注意:如果两台设备之前没有配对过,那么Android框架将会自动显示一个请求配对的通知或对话框,正如图3中显示的那样。因此,当尝试连接设备时,你的应用不需要考虑设备是否配对过。你的RFCOMM连接尝试将会阻塞,知道用户成功配对,或者用户拒绝失败时,或者配对失败,或者超时。

建立服务端

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;
 
    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }
 
    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }
 
    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

建立客户端

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
 
    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;
 
        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }
 
    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();
 
        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }
 
        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }
 
    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

参考文档

【1】手机作为周边BluetoothGattServer的实现
【2】Android中文社区(Bluetooth)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值