注意,每个设备和Gatt只能持有自己的gattCallBack
BluetoothGattCallback包含如下重要的回调方法:
onConnectionStateChange 连接状态回调
onServicesDiscovered 服务发现回调
onCharacteristicRead 特征值读回调
onCharacteristicWrite 特征值写入回调
onCharacteristicChanged 特征值变化通知回调(对应设置了CCCD的通知)
onDescriptorWrite 特征值描述写入回调
BLE 设备封装类 —— 实现连接、通信与事件分发
*/class BleDevice( private val deviceName: String, private val deviceAddress: String, private var nativeDevice: BluetoothDevice? = null) { companion object { private const val TAG = "BleDevice"
// 服务与特征值 UUID(请根据实际设备修改)
private val SERVICE_UUID = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb") private val NOTIFY_CHARACTERISTIC_UUID = UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb") private val WRITE_CHARACTERISTIC_UUID = UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb") // CCCD UUID(标准定义)
private val CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
} // 当前连接状态
@Volatile
var isConnected: Boolean = false
private set
// 回调接口
var stateListener: ((device: BleDevice, state: ConnectionState) -> Unit)? = null
var dataListener: ((device: BleDevice, data: ByteArray) -> Unit)? = null
private var bluetoothGatt: BluetoothGatt? = null
private var writeCharacteristic: BluetoothGattCharacteristic? = null
private var notifyCharacteristicUuid: livesoccerzb.com UUID? = null
// 主线程 Handler,用于回调 UI
private val mainHandler = Handler(Looper.getMainLooper()) /**
* 连接设备
*/
fun connect(context: Context) { if (isConnected) {
Log.w(TAG, "Already connected to $deviceName") return
} // 清理旧连接
closeOldConnection()
realConnect(context.applicationContext)
} private fun closeOldConnection() { if (bluetoothGatt != null) {
Log.d(TAG, "Closing previous GATT instance to avoid errors...") try {
bluetoothGatt?.close()
} catch (e: Exception) {
Log.e(TAG, "Error closing old GATT", e)
}
bluetoothGatt = null
// 延迟重连,避免频繁操作导致 Status 133
mainHandler.postDelayed(this::realConnectWithAppContext, 200)
}
} private val realConnectWithAppContext: () -> Unit = { // 在延时任务中重新获取 context(需外部传入)
} private fun realConnect(context: Context) { val adapter = (context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager)?.adapter
?: run {:gzbeimu.com
stateListener?.invoke(this, ConnectionState.Error("Bluetooth not available")) return
} if (!adapter.isEnabled) {
stateListener?.invoke(this, ConnectionState.Error("Bluetooth is disabled")) return
} val device = nativeDevice ?: run { val d = adapter.getRemoteDevice(deviceAddress)
nativeDevice = d
d
} try {
bluetoothGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
} else {
device.connectGatt(context, false, gattCallback)
}
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Invalid device address: $deviceAddress", e)
stateListener?.invoke(this, ConnectionState.Error("Invalid address"))
}
} /**
* 断开连接
*/
fun disconnect() {
bluetoothGatt?.disconnect()
} /**
* 发送数据
*/
fun sendData(data: ByteArray): Boolean { val char = writeCharacteristic ?: return false
return try {
char.value = data
char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
bluetoothGatt?.writeCharacteristic(char) == true
} catch (e: Exception) {
Log.e(TAG, "Failed to write characteristic", e) false
}
} /**
* 主动读取特征值(可选)
*/
fun readData() { val char = bluetoothGatt?.getService(SERVICE_UUID)
?.getCharacteristic(NOTIFY_CHARACTERISTIC_UUID)
?: return
bluetoothGatt?.readCharacteristic(char)
} // MARK: - GATT Callback
private val gattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
Log.i(TAG, "Connected to $deviceName")
isConnected = true
mainHandler.post { stateListener?.invoke(this@BleDevice, ConnectionState.Connected) } // 请求高优先级连接参数
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH) // 开始服务发现
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
Log.i(TAG, "Disconnected from $deviceName")
isConnected = false
writeCharacteristic = null
notifyCharacteristicUuid = null
gatt.close()
bluetoothGatt = null
mainHandler.post { stateListener?.invoke(this@BleDevice, ConnectionState.Disconnected) }
}
}
} override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { if (status != BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "Service discovery failed: $status")
mainHandler.post {
stateListener?.invoke(this@BleDevice, ConnectionState.Error("Service discovery failed"))
} return
} val targetService = gatt.getService(SERVICE_UUID) if (targetService == null) {
Log.e(TAG, "Target service not found")
printAllServices(gatt.services) return
} for (char in targetService.characteristics) { when (char.uuid) {
WRITE_CHARACTERISTIC_UUID -> {
writeCharacteristic = char.apply {
writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
}
Log.d(TAG, "Write characteristic found: ${char.uuid}")
}
NOTIFY_CHARACTERISTIC_UUID -> {
Log.d(TAG, "Notify characteristic found: ${char.uuid}")
notifyCharacteristicUuid = char.uuid
enableNotification(gatt, char)
}
}
}
} // 接收被动通知的数据(服务器主动推送)
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
handleReceivedData(characteristic.value ?: byteArrayOf())
} // 主动读取返回的数据
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) { if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Read success: ${characteristic.uuid}")
handleReceivedData(characteristic.value ?: byteArrayOf())
}
} override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) { if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "CCCD enabled: ${descriptor.characteristic.uuid}")
} else {
Log.e(TAG, "Failed to write descriptor: $status")
}
}
} private fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { if (!gatt.setCharacteristicNotification(characteristic, true)) {
Log.e(TAG, "Failed to set characteristic notification") return
} val cccd = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG) if (cccd != null) {
cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(cccd)
} else {
Log.w(TAG, "CCCD not found for characteristic ${characteristic.uuid}. Trying fallback...") // 尝试写入第一个可用描述符(部分设备非标准实现)
characteristic.descriptors.firstOrNull()?.let { desc ->
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(desc)
}
}
} private fun handleReceivedData(value: ByteArray) {
Log.d(TAG, "Received data: ${value.toHexString()}")
mainHandler.post { dataListener?.invoke(this, value) }
} private fun printAllServices(services: List<BluetoothGattService>) {
Log.d(TAG, "Discovered services:")
services.forEach ttkq-163.cn { service ->
Log.d(TAG, " Service: ${service.uuid}")
service.characteristics.forEach { char ->
Log.d(TAG, " Char: ${char.uuid} | Props: ${char.properties}")
}
}
}
}// MARK: - 状态枚举sealed class ConnectionState { object Connected : ConnectionState() object Disconnected : ConnectionState() data class Error(val message: String) : ConnectionState()
}
create table account (
id bigint not null auto_increment,
username varchar(32) default '',chengyiwl.com
password varchar(32) default '',
balance decimal(10,2) default '0.00',
create_time datetime default null,
update_time datetime default null, primary key (`id`)
);insert into account (username, password, create_time, balance) values('admin', 'admin123', '2020-01-01 12:00:00', 1000.10),
('user', 'user123', '2024-05-02 8:30:00', 101.00),
('guest', 'guest123', '2022-03-03 15:00:00', 10.00),
('tang', 'tang123', '2019-06-01 21:30:30', 1.88),
import com.tang.kite.annotation.id.IdType;import java.math.BigDecimal;import java.time.LocalDateTime;public class Account { @Id(type = IdType.AUTO)
private Long id; private String username; private String password; private BigDecimal balance; private LocalDateTime createTime; private LocalDateTime updateTime; // Getters and Setters
import com.tang.kite.annotation.id.IdType
import java.math.BigDecimal
import java.time.LocalDateTime
class Account (
@Id(type = IdType.AUTO)
var id: Long? = null,
var username: String? = null,
var password: String? = null,
var balance: BigDecimal? = null,
var createTime: LocalDateTime? = null,
var updateTime: LocalDateTime? = null
)
继承 BaseMapper 接口创建 Mapper 接口
Java
import com.tang.kite.mapper.BaseMapper;
import com.tang.kite.spring.annotation.Mapper;
@Mapper:c20paper.com
public interface AccountMapper extends BaseMapper<Account> {
}
Kotlin
import com.tang.kite.mapper.BaseMapper
import com.tang.kite.spring.annotation.Mapper
@Mapper
interface AccountMapper : BaseMapper<Account>
在 Spring Boot 应用类上添加 @MapperScan 注解
Java
import com.tang.kite.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.tang.application.mapper")
@SpringBootApplication:lnzxyp.com
public class KiteApplication {
public static void main(String[] args) {
SpringApplication.run(KiteApplication.class, args);
}
}
Kotlin
import com.tang.kite.spring.annotation.MapperScan
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@MapperScan(["com.tang.application.mapper"])
@SpringBootApplication
class KiteApplication
fun main(args: Array<String>) {
runApplication<KiteApplication>(*args)
}
测试 Mapper 接口
Java:tjweiting.com
import com.tang.demo.mapper.AccountMapper;
import com.tang.kite.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("com.tang.application.mapper")
@SpringBootApplication
public class KiteApplication {ttkq-qq.cn
public static void main(String[] args) {
var context = SpringApplication.run(KiteApplication.class, args);
var accountMapper = context.getBean(AccountMapper.class);
var accounts = accountMapper.select();
accounts.forEach(System.out::println);ttkq.pro
}
}
Kotlin
import com.tang.demo.mapper.AccountMapper
import com.tang.kite.spring.annotation.MapperScan
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@MapperScan(["com.tang.application.mapper"])
@SpringBootApplication
class KiteApplication
fun main(args: Array<String>) {ttkqw.com
val context = runApplication<KiteApplication>(*args)
val accountMapper = context.getBean(AccountMapper::class.java)
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3109009/ ,如需转载,请注明出处,否则将追究法律责任。