• Android 蓝牙开发( 四 )


    前言

    上一篇文章给大家分享了Kotlin版的Android蓝牙的基础知识和基础用法,不过上一篇都是一些零散碎片化的程序,,这一篇给大家分享Android蓝牙开发实战项目Kotlin+Compose的初步使用

    效果演示 : 

    Android Compose 蓝牙开发

    Android蓝牙实战开发步骤

    1.新建Android项目添加蓝牙权限

    下图所示:MyBluetoothDemo为刚刚创建的Android空项目,我们现在清单文件中把我们需要用到的权限声明一下,其中定位权限还需要做动态申请

    2.封装BluetoothAdapter类

    BluetoothAdapter类提供了常用的蓝牙API,我这里创建了一个BlueToothController类,小编这里是先将这些API封装到了一个BlueToothController类中,方便后续使用和操作

    1. package com.example.bluetoothcompose
    2. import android.annotation.SuppressLint
    3. import android.app.Activity
    4. import android.bluetooth.BluetoothAdapter
    5. import android.bluetooth.BluetoothDevice
    6. import android.bluetooth.BluetoothSocket
    7. import android.content.Context
    8. import android.content.Intent
    9. object BlueToothController {
    10. val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
    11. /**
    12. * 检查设备是否支持蓝牙
    13. */
    14. fun isBluetoothSupport(): Boolean {
    15. return mBluetoothAdapter !=null
    16. }
    17. /**
    18. * 检查该设备蓝牙是否开启
    19. */
    20. @SuppressLint("MissingPermission")
    21. fun isBluetoothEnabled(): Boolean {
    22. return mBluetoothAdapter.enable()
    23. }
    24. /**
    25. * 打开蓝牙
    26. */
    27. @SuppressLint("MissingPermission")
    28. fun turnOnBlueTooth(activity: Activity, requestCode: Int) {
    29. val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
    30. activity.startActivityForResult(intent, requestCode)
    31. }
    32. /**
    33. * 打开蓝牙可见性
    34. */
    35. @SuppressLint("MissingPermission")
    36. fun enableVisibily(context: Context) {
    37. val intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
    38. intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
    39. context.startActivity(intent)
    40. }
    41. /**
    42. * 停止查找设备
    43. */
    44. @SuppressLint("MissingPermission")
    45. fun cancelFindDevice() {
    46. mBluetoothAdapter.cancelDiscovery()
    47. }
    48. /**
    49. * 判断当前设备是否在查找蓝牙设备
    50. */
    51. @SuppressLint("MissingPermission")
    52. fun isStartDiscovering(): Boolean {
    53. return mBluetoothAdapter.isDiscovering
    54. }
    55. /**
    56. * 判断当前设备是否未在查找蓝牙设备
    57. */
    58. @SuppressLint("MissingPermission")
    59. fun isCancelDiscovering(): Boolean {
    60. return !mBluetoothAdapter.isDiscovering
    61. }
    62. /**
    63. * 查找设备
    64. */
    65. @SuppressLint("MissingPermission")
    66. fun findDevice() {
    67. mBluetoothAdapter.startDiscovery()
    68. }
    69. /**
    70. * 获取已绑定设备
    71. */
    72. @SuppressLint("MissingPermission")
    73. fun getBondedDeviceList(): List? {
    74. return ArrayList(mBluetoothAdapter.bondedDevices)
    75. }
    76. /**
    77. * 判断蓝牙是否连接
    78. */
    79. @SuppressLint("MissingPermission")
    80. fun isConnectBlue(bluetoothSocket: BluetoothSocket?): Boolean {
    81. return bluetoothSocket != null && bluetoothSocket.isConnected
    82. }
    83. }

    3. 编写Compose UI页面

    这里的UI样式,在后面我给出了完整版的,大家可以去复制一下

    MainScreen:这是我们MainActivity的UI,放置了一个Column(竖向布局)和Menu

    1. @Composable
    2. fun MainScreen() {
    3. var expanded = remember {
    4. mutableStateOf(false)
    5. }
    6. Column(
    7. modifier = Modifier.fillMaxSize()
    8. )
    9. {
    10. Row(
    11. modifier = Modifier
    12. .fillMaxWidth()
    13. .background(Blue)
    14. .padding(vertical = 12.dp)
    15. .height(35.dp),
    16. verticalAlignment = Alignment.CenterVertically,
    17. horizontalArrangement = Arrangement.Start
    18. ) {
    19. Text(
    20. text = "可用设备",
    21. modifier = Modifier
    22. .weight(1f)
    23. .offset(10.dp)
    24. )
    25. if(isRefresh.value){
    26. CircularProgressIndicator(
    27. modifier = Modifier.size(25.dp),
    28. color = White
    29. )
    30. }
    31. Box() {
    32. Icon(
    33. painter = painterResource(id = R.drawable.ic_setting),
    34. contentDescription = null,
    35. modifier = Modifier
    36. .width(50.dp)
    37. .fillMaxHeight()
    38. .clickable {
    39. expanded.value = true
    40. },
    41. )
    42. if(expanded.value){
    43. DropdownMenu(
    44. expanded = expanded.value,
    45. onDismissRequest = {
    46. expanded.value = false
    47. }) {
    48. data.forEachIndexed{ index: Int, s: String ->
    49. DropdownMenuItem(onClick = {
    50. when (index) {
    51. 0 -> {
    52. if(BlueToothController.isBluetoothSupport()){
    53. Toast.makeText(this@MainActivity,"本机支持蓝牙功能",Toast.LENGTH_SHORT).show()
    54. }else{
    55. Toast.makeText(this@MainActivity,"本机暂不支持蓝牙功能",Toast.LENGTH_SHORT).show()
    56. }
    57. }
    58. 1 -> {
    59. if(BlueToothController.isBluetoothEnabled()){
    60. Toast.makeText(this@MainActivity,"用户允许开启蓝牙",Toast.LENGTH_SHORT).show()
    61. }else{
    62. Toast.makeText(this@MainActivity,"用户拒绝开启蓝牙",Toast.LENGTH_SHORT).show()
    63. }
    64. }
    65. 2 -> {
    66. selected.value = 3
    67. Log.d(TAG,"查看已绑定设备")
    68. if(BlueToothController.isStartDiscovering()){
    69. BlueToothController.cancelFindDevice()
    70. }
    71. deviceList.clear()
    72. for (device in BlueToothController.getBondedDeviceList()!!){
    73. deviceList.add(device!!)
    74. }
    75. }
    76. 3 -> {
    77. if(BlueToothController.isStartDiscovering()){
    78. Log.d(TAG,"停止查找")
    79. BlueToothController.cancelFindDevice()
    80. deviceList!!.clear()
    81. }
    82. selected.value = 4
    83. BlueToothController.findDevice()
    84. Log.d(TAG,"开始查找")
    85. }
    86. }
    87. Log.d(TAG,selected.value.toString())
    88. expanded.value = false
    89. }) {
    90. Text(text = s)
    91. }
    92. }
    93. }
    94. }
    95. }
    96. }
    97. DeviceListView()
    98. }
    99. if(openDialog.value){
    100. AlterDialog()
    101. }
    102. }

    AlterDialog:    用来显示弹窗

    1. @Composable
    2. fun AlterDialog() {
    3. AlertDialog(
    4. onDismissRequest = { openDialog.value = false },
    5. title = { Text(text = text.value) },
    6. text = {
    7. Text(
    8. text = "0c 11 09 41 23 00 01 03 FF"
    9. )
    10. }, confirmButton = {
    11. TextButton(onClick = {
    12. openDialog.value = false
    13. sendMessage()
    14. }) {
    15. Text(text = "发送")
    16. }
    17. }, dismissButton = {
    18. TextButton(onClick = { openDialog.value = false }) {
    19. Text(text = "取消")
    20. }
    21. })
    22. }

    DeviceListView: 这是一个列表控件,相当于RecycleView  

    1. @Composable
    2. fun DeviceListView(){
    3. LazyColumn(
    4. Modifier
    5. .fillMaxSize(),
    6. contentPadding = PaddingValues(5.dp,1.dp),
    7. verticalArrangement = Arrangement.spacedBy(5.dp)
    8. ){
    9. items(deviceList!!.size){ index->
    10. ListItem(index, deviceList[index])
    11. }
    12. }
    13. }

    ListItem:这是每个LazyColumn中每个列表的UI样式

    1. @Composable
    2. fun ListItem(index: Int, blueToothDevice: BluetoothDevice){
    3. Card(
    4. shape = RoundedCornerShape(4.dp),
    5. elevation = 2.dp
    6. ) {
    7. Row(
    8. modifier = Modifier
    9. .height(50.dp)
    10. .fillMaxWidth()
    11. .clickable {
    12. openDialog.value = true
    13. if (blueToothDevice.name == null) {
    14. text.value = "N/A"
    15. } else {
    16. text.value = blueToothDevice.name
    17. }
    18. //Gatt协议连接蓝牙
    19. var bluetoothGatt =
    20. blueToothDevice.connectGatt(this@MainActivity, true, mGattCallback)
    21. bluetoothGatt.connect()
    22. Log.d(TAG, "点击了第$index 个item")
    23. },
    24. verticalAlignment = Alignment.CenterVertically,
    25. ) {
    26. Image(
    27. painter = painterResource(R.drawable.ic_blue),
    28. contentDescription = null,
    29. modifier = Modifier
    30. .fillMaxHeight()
    31. .padding(all = 5.dp)
    32. )
    33. Column(
    34. modifier = Modifier.fillMaxWidth()
    35. ) {
    36. if(blueToothDevice.name==null){
    37. Text(
    38. text = "N/A",
    39. fontWeight = FontWeight.Bold
    40. )
    41. }else{
    42. Text(
    43. text = blueToothDevice.name,
    44. fontWeight = FontWeight.Bold
    45. )
    46. }
    47. Text(
    48. text = blueToothDevice.address,
    49. )
    50. }
    51. }
    52. }
    53. }

    4. 蓝牙搜索,配对,连接,通信

    小编这里为了让大家方便,便将搜索,配对,连接都写在了MainActivity中了,Compose UI也在这里了,大家可以复制直接去运行

    1. package com.example.bluetoothcompose
    2. import android.Manifest.permission.ACCESS_COARSE_LOCATION
    3. import android.Manifest.permission.ACCESS_FINE_LOCATION
    4. import android.annotation.SuppressLint
    5. import android.bluetooth.*
    6. import android.content.BroadcastReceiver
    7. import android.content.Context
    8. import android.content.Intent
    9. import android.content.IntentFilter
    10. import android.os.Bundle
    11. import android.util.Log
    12. import android.widget.Toast
    13. import androidx.activity.ComponentActivity
    14. import androidx.activity.compose.setContent
    15. import androidx.compose.foundation.Image
    16. import androidx.compose.foundation.background
    17. import androidx.compose.foundation.clickable
    18. import androidx.compose.foundation.layout.*
    19. import androidx.compose.foundation.lazy.LazyColumn
    20. import androidx.compose.foundation.shape.RoundedCornerShape
    21. import androidx.compose.material.*
    22. import androidx.compose.runtime.*
    23. import androidx.compose.ui.Alignment
    24. import androidx.compose.ui.Modifier
    25. import androidx.compose.ui.res.painterResource
    26. import androidx.compose.ui.text.font.FontWeight
    27. import androidx.compose.ui.tooling.preview.Preview
    28. import androidx.compose.ui.unit.dp
    29. import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
    30. import com.example.bluetoothcompose.ui.theme.Blue
    31. import com.example.bluetoothcompose.ui.theme.BlueToothComposeTheme
    32. import com.example.bluetoothcompose.ui.theme.White
    33. import java.util.*
    34. class MainActivity : ComponentActivity() {
    35. private val TAG = "yf"
    36. private var deviceList = mutableStateListOf()
    37. private var data = mutableListOf(
    38. "检查设备是否支持蓝牙",
    39. "检查设备是否开启蓝牙",
    40. "查看已配过的蓝牙设备",
    41. "查找蓝牙设备"
    42. )
    43. var selected = mutableStateOf(0)
    44. var openDialog = mutableStateOf(false)
    45. var text = mutableStateOf("")
    46. var mGatt: BluetoothGatt? = null
    47. var mWriter: BluetoothGattCharacteristic? = null
    48. private var isRefresh = mutableStateOf(false)
    49. override fun onCreate(savedInstanceState: Bundle?) {
    50. super.onCreate(savedInstanceState)
    51. setContent {
    52. BlueToothComposeTheme {
    53. // A surface container using the 'background' color from the theme
    54. Surface(
    55. modifier = Modifier.fillMaxSize(),
    56. color = MaterialTheme.colors.background
    57. ) {
    58. MainScreen()
    59. }
    60. }
    61. }
    62. }
    63. override fun onStart() {
    64. super.onStart()
    65. isPermission()
    66. registerBluetoothReceiver()
    67. }
    68. //处理找到蓝牙设备和搜索完成的广播消息
    69. var receiver: BroadcastReceiver = object : BroadcastReceiver() {
    70. @SuppressLint("MissingPermission")
    71. override fun onReceive(context: Context, intent: Intent) {
    72. val action = intent.action
    73. //开始查找设备
    74. when {
    75. BluetoothAdapter.ACTION_DISCOVERY_STARTED == action -> {
    76. //开始搜索
    77. if(deviceList!=null){
    78. deviceList!!.clear()
    79. }
    80. isRefresh.value = true
    81. }
    82. BluetoothDevice.ACTION_FOUND == action -> {
    83. //搜到蓝牙设备
    84. val device =
    85. intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
    86. //把搜索到的设备添加到已找到列表中,显示它的信息
    87. deviceList?.add(device!!)
    88. Log.d(TAG,"找到了: ${deviceList.size}")
    89. }
    90. BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action -> {
    91. //搜索完毕
    92. isRefresh.value = false
    93. when (selected.value) {
    94. 3 -> {
    95. }
    96. 4 -> {
    97. Toast.makeText(this@MainActivity,"选择要配对的蓝牙设备",Toast.LENGTH_SHORT).show()
    98. }
    99. }
    100. }
    101. BluetoothDevice.ACTION_BOND_STATE_CHANGED == action -> {
    102. val device =
    103. intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
    104. if (device == null) {
    105. Toast.makeText(this@MainActivity,"无设备",Toast.LENGTH_SHORT).show()
    106. return
    107. }
    108. val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0)
    109. when (state) {
    110. BluetoothDevice.BOND_BONDED -> {
    111. Toast.makeText(this@MainActivity,"已配对",Toast.LENGTH_SHORT).show()
    112. }
    113. BluetoothDevice.BOND_BONDING -> {
    114. Toast.makeText(this@MainActivity,"正在配对",Toast.LENGTH_SHORT).show()
    115. }
    116. BluetoothDevice.BOND_NONE -> {
    117. Toast.makeText(this@MainActivity,"未配对",Toast.LENGTH_SHORT).show()
    118. }
    119. }
    120. }
    121. }
    122. }
    123. }
    124. //动态获取位置权限
    125. @SuppressLint("WrongConstant")
    126. private fun isPermission() {
    127. if (checkSelfPermission(ACCESS_COARSE_LOCATION) !== PERMISSION_GRANTED
    128. || checkSelfPermission(ACCESS_FINE_LOCATION) !== PERMISSION_GRANTED
    129. ) {
    130. requestPermissions(
    131. arrayOf(
    132. ACCESS_COARSE_LOCATION,
    133. ACCESS_FINE_LOCATION
    134. ), 200
    135. )
    136. }
    137. }
    138. private fun registerBluetoothReceiver() {
    139. //filter注册广播接收器
    140. val filter = IntentFilter()
    141. //蓝牙当前状态
    142. filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
    143. //开始扫描蓝牙设备广播
    144. filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
    145. //找到蓝牙设备广播
    146. filter.addAction(BluetoothDevice.ACTION_FOUND)
    147. //扫描蓝牙设备结束广播
    148. filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
    149. //蓝牙设备配对状态改变广播
    150. filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
    151. //设备扫描模式改变广播
    152. filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
    153. registerReceiver(receiver, filter)
    154. }
    155. @SuppressLint("MissingPermission")
    156. @Composable
    157. fun MainScreen() {
    158. var expanded = remember {
    159. mutableStateOf(false)
    160. }
    161. Column(
    162. modifier = Modifier.fillMaxSize()
    163. )
    164. {
    165. Row(
    166. modifier = Modifier
    167. .fillMaxWidth()
    168. .background(Blue)
    169. .padding(vertical = 12.dp)
    170. .height(35.dp),
    171. verticalAlignment = Alignment.CenterVertically,
    172. horizontalArrangement = Arrangement.Start
    173. ) {
    174. Text(
    175. text = "可用设备",
    176. modifier = Modifier
    177. .weight(1f)
    178. .offset(10.dp)
    179. )
    180. if(isRefresh.value){
    181. CircularProgressIndicator(
    182. modifier = Modifier.size(25.dp),
    183. color = White
    184. )
    185. }
    186. Box() {
    187. Icon(
    188. painter = painterResource(id = R.drawable.ic_setting),
    189. contentDescription = null,
    190. modifier = Modifier
    191. .width(50.dp)
    192. .fillMaxHeight()
    193. .clickable {
    194. expanded.value = true
    195. },
    196. )
    197. if(expanded.value){
    198. DropdownMenu(
    199. expanded = expanded.value,
    200. onDismissRequest = {
    201. expanded.value = false
    202. }) {
    203. data.forEachIndexed{ index: Int, s: String ->
    204. DropdownMenuItem(onClick = {
    205. when (index) {
    206. 0 -> {
    207. if(BlueToothController.isBluetoothSupport()){
    208. Toast.makeText(this@MainActivity,"本机支持蓝牙功能",Toast.LENGTH_SHORT).show()
    209. }else{
    210. Toast.makeText(this@MainActivity,"本机暂不支持蓝牙功能",Toast.LENGTH_SHORT).show()
    211. }
    212. }
    213. 1 -> {
    214. if(BlueToothController.isBluetoothEnabled()){
    215. Toast.makeText(this@MainActivity,"用户允许开启蓝牙",Toast.LENGTH_SHORT).show()
    216. }else{
    217. Toast.makeText(this@MainActivity,"用户拒绝开启蓝牙",Toast.LENGTH_SHORT).show()
    218. }
    219. }
    220. 2 -> {
    221. selected.value = 3
    222. Log.d(TAG,"查看已绑定设备")
    223. if(BlueToothController.isStartDiscovering()){
    224. BlueToothController.cancelFindDevice()
    225. }
    226. deviceList.clear()
    227. for (device in BlueToothController.getBondedDeviceList()!!){
    228. deviceList.add(device!!)
    229. }
    230. }
    231. 3 -> {
    232. if(BlueToothController.isStartDiscovering()){
    233. Log.d(TAG,"停止查找")
    234. BlueToothController.cancelFindDevice()
    235. deviceList!!.clear()
    236. }
    237. selected.value = 4
    238. BlueToothController.findDevice()
    239. Log.d(TAG,"开始查找")
    240. }
    241. }
    242. Log.d(TAG,selected.value.toString())
    243. expanded.value = false
    244. }) {
    245. Text(text = s)
    246. }
    247. }
    248. }
    249. }
    250. }
    251. }
    252. DeviceListView()
    253. }
    254. if(openDialog.value){
    255. AlterDialog()
    256. }
    257. }
    258. @Preview(
    259. showBackground = true,
    260. group = "Group1",
    261. )
    262. @Composable
    263. fun DefaultPreview() {
    264. MainScreen()
    265. }
    266. @SuppressLint("MissingPermission")
    267. @Composable
    268. fun DeviceListView(){
    269. LazyColumn(
    270. Modifier
    271. .fillMaxSize(),
    272. contentPadding = PaddingValues(5.dp,1.dp),
    273. verticalArrangement = Arrangement.spacedBy(5.dp)
    274. ){
    275. items(deviceList!!.size){ index->
    276. ListItem(index, deviceList[index])
    277. }
    278. }
    279. }
    280. @SuppressLint("MissingPermission")
    281. @Composable
    282. fun ListItem(index: Int, blueToothDevice: BluetoothDevice){
    283. Card(
    284. shape = RoundedCornerShape(4.dp),
    285. elevation = 2.dp
    286. ) {
    287. Row(
    288. modifier = Modifier
    289. .height(50.dp)
    290. .fillMaxWidth()
    291. .clickable {
    292. openDialog.value = true
    293. if (blueToothDevice.name == null) {
    294. text.value = "N/A"
    295. } else {
    296. text.value = blueToothDevice.name
    297. }
    298. //Gatt协议连接蓝牙
    299. var bluetoothGatt =
    300. blueToothDevice.connectGatt(this@MainActivity, true, mGattCallback)
    301. bluetoothGatt.connect()
    302. Log.d(TAG, "点击了第$index 个item")
    303. },
    304. verticalAlignment = Alignment.CenterVertically,
    305. ) {
    306. Image(
    307. painter = painterResource(R.drawable.ic_blue),
    308. contentDescription = null,
    309. modifier = Modifier
    310. .fillMaxHeight()
    311. .padding(all = 5.dp)
    312. )
    313. Column(
    314. modifier = Modifier.fillMaxWidth()
    315. ) {
    316. if(blueToothDevice.name==null){
    317. Text(
    318. text = "N/A",
    319. fontWeight = FontWeight.Bold
    320. )
    321. }else{
    322. Text(
    323. text = blueToothDevice.name,
    324. fontWeight = FontWeight.Bold
    325. )
    326. }
    327. Text(
    328. text = blueToothDevice.address,
    329. )
    330. }
    331. }
    332. }
    333. }
    334. @SuppressLint("MissingPermission")
    335. @Composable
    336. fun AlterDialog() {
    337. AlertDialog(
    338. onDismissRequest = { openDialog.value = false },
    339. title = { Text(text = text.value) },
    340. text = {
    341. Text(
    342. text = "0c 11 09 41 23 00 01 03 FF"
    343. )
    344. }, confirmButton = {
    345. TextButton(onClick = {
    346. openDialog.value = false
    347. sendMessage()
    348. }) {
    349. Text(text = "发送")
    350. }
    351. }, dismissButton = {
    352. TextButton(onClick = { openDialog.value = false }) {
    353. Text(text = "取消")
    354. }
    355. })
    356. }
    357. private val mGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
    358. @SuppressLint("MissingPermission")
    359. override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
    360. //连接成功
    361. if (newState == BluetoothProfile.STATE_CONNECTED) {
    362. //进行服务发现
    363. gatt.discoverServices()
    364. Log.d(TAG, "连接成功")
    365. } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
    366. //连接断开,处理断开逻辑
    367. Log.d(TAG, "连接断开")
    368. }
    369. }
    370. @SuppressLint("MissingPermission")
    371. override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
    372. Log.d(TAG, "onServicesDiscovered : $status ==>> $gatt")
    373. //发现服务成功,处理服务和特征值
    374. if (status == BluetoothGatt.GATT_SUCCESS) {
    375. //发送消息
    376. mGatt = gatt
    377. val service =
    378. gatt.getService(UUID.fromString("0000180a-0000-1000-8000-00805F9B34FB"))
    379. mWriter =
    380. service.getCharacteristic(UUID.fromString("00002ad9-0000-1000-8000-00805F9B34FB"))
    381. //打开消息通知
    382. mGatt!!.setCharacteristicNotification(mWriter, true)
    383. val descriptor: BluetoothGattDescriptor =
    384. mWriter!!.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
    385. descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
    386. mGatt!!.writeDescriptor(descriptor)
    387. } else {
    388. Log.d(TAG, "发现服务失败")
    389. }
    390. }
    391. override fun onCharacteristicRead(
    392. gatt: BluetoothGatt,
    393. characteristic: BluetoothGattCharacteristic,
    394. status: Int
    395. ) {
    396. Log.e(TAG, "onCharacteristicRead $status")
    397. //读取特征成功,处理特征值
    398. if (status == BluetoothGatt.GATT_SUCCESS) {
    399. }
    400. }
    401. override fun onCharacteristicWrite(
    402. gatt: BluetoothGatt,
    403. characteristic: BluetoothGattCharacteristic,
    404. status: Int
    405. ) {
    406. Log.e(TAG, "onCharacteristicWrite $status")
    407. //写入特征成功
    408. if (status == BluetoothGatt.GATT_SUCCESS) {
    409. Log.d(TAG, "发送成功")
    410. } else {
    411. Log.d(TAG, "发送失败")
    412. }
    413. }
    414. override fun onCharacteristicChanged(
    415. gatt: BluetoothGatt,
    416. characteristic: BluetoothGattCharacteristic
    417. ) {
    418. //接收到数据
    419. val data = characteristic.value
    420. //处理接收到的数据
    421. Log.d(TAG, "Received data: " + bytesToHexFun2(data))
    422. }
    423. override fun onDescriptorRead(
    424. gatt: BluetoothGatt,
    425. descriptor: BluetoothGattDescriptor,
    426. status: Int
    427. ) {
    428. super.onDescriptorRead(gatt, descriptor, status)
    429. }
    430. override fun onDescriptorWrite(
    431. gatt: BluetoothGatt,
    432. descriptor: BluetoothGattDescriptor,
    433. status: Int
    434. ) {
    435. super.onDescriptorWrite(gatt, descriptor, status)
    436. }
    437. override fun onReliableWriteCompleted(gatt: BluetoothGatt, status: Int) {
    438. super.onReliableWriteCompleted(gatt, status)
    439. }
    440. override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
    441. super.onReadRemoteRssi(gatt, rssi, status)
    442. }
    443. override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
    444. super.onMtuChanged(gatt, mtu, status)
    445. }
    446. override fun onServiceChanged(gatt: BluetoothGatt) {
    447. super.onServiceChanged(gatt)
    448. }
    449. override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
    450. super.onPhyUpdate(gatt, txPhy, rxPhy, status)
    451. }
    452. override fun onPhyRead(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
    453. super.onPhyRead(gatt, txPhy, rxPhy, status)
    454. }
    455. }
    456. private fun bytesToHexFun2(bytes: ByteArray): String? {
    457. var result = 0
    458. for (i in bytes.indices) {
    459. result += bytes[i]
    460. }
    461. return byte2Hex((result.inv() and 0xFF).toByte())
    462. }
    463. fun byte2Hex(inByte: Byte?): String //1字节转2个Hex字符
    464. {
    465. return String.format("%02x", inByte).toUpperCase()
    466. }
    467. @SuppressLint("MissingPermission")
    468. fun sendMessage(){
    469. if (null == mWriter) {
    470. Log.e("yf123", "ble:发送失败:null == writer !!!!")
    471. } else {
    472. mWriter!!.value = byteArrayOf(
    473. 0x0c.toByte(),
    474. 0x11.toByte(),
    475. 0x09.toByte(),
    476. 0x41.toByte(),
    477. 0x23.toByte(),
    478. 0x00.toByte(),
    479. 0x01.toByte(),
    480. 0x03.toByte(),
    481. 0xFF.toByte()
    482. )
    483. mGatt!!.writeCharacteristic(mWriter)
    484. }
    485. }
    486. }

    到此为止,我们的程序就到这里了,蓝牙搜索,配对,连接,通信便已经成功实现了,大家可以把代码copy一下拿去运行,具体效果演示图在文章最上方,大家还想了解更多关于Android蓝牙开发的可以继续看我下一篇给大家的分享

  • 相关阅读:
    docker部署redis
    发展新能源汽车加快充换电基础设施建设实施方案-安科瑞黄安南
    java游戏制作-拼图游戏
    面试题整理
    玩转Vue3之shallowRef和shallowReactive
    二维码怎么分解成链接?线上快速解码教学
    PreScan快速入门到精通第三十讲基于PreScan进行摄像头传感器仿真
    复试(服务器测试实习生)、一面
    每日挠头算法题(十五)螺旋矩阵II
    订水商城实战教程08-轮播图
  • 原文地址:https://blog.csdn.net/Ai1114/article/details/132294425