Android蓝牙通信的两种方式
添加权限
<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) { }
}
}