Android 蓝牙开发( 四 )

news2024/12/23 21:17:56

前言

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

效果演示 : 

Android Compose 蓝牙开发

Android蓝牙实战开发步骤

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

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

2.封装BluetoothAdapter类

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

package com.example.bluetoothcompose

import android.annotation.SuppressLint
import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.content.Intent

object BlueToothController {

    val mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()

    /**
     * 检查设备是否支持蓝牙
     */
    fun isBluetoothSupport(): Boolean {
        return mBluetoothAdapter !=null
    }

    /**
     * 检查该设备蓝牙是否开启
     */
    @SuppressLint("MissingPermission")
    fun isBluetoothEnabled(): Boolean {
        return mBluetoothAdapter.enable()
    }


    /**
     * 打开蓝牙
     */
    @SuppressLint("MissingPermission")
    fun turnOnBlueTooth(activity: Activity, requestCode: Int) {
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        activity.startActivityForResult(intent, requestCode)
    }


    /**
     * 打开蓝牙可见性
     */
    @SuppressLint("MissingPermission")
    fun enableVisibily(context: Context) {
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
        intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
        context.startActivity(intent)
    }


    /**
     * 停止查找设备
     */
    @SuppressLint("MissingPermission")
    fun cancelFindDevice() {
        mBluetoothAdapter.cancelDiscovery()
    }

    /**
     * 判断当前设备是否在查找蓝牙设备
     */
    @SuppressLint("MissingPermission")
    fun isStartDiscovering(): Boolean {
        return mBluetoothAdapter.isDiscovering
    }

    /**
     * 判断当前设备是否未在查找蓝牙设备
     */
    @SuppressLint("MissingPermission")
    fun isCancelDiscovering(): Boolean {
        return !mBluetoothAdapter.isDiscovering
    }


    /**
     * 查找设备
     */
    @SuppressLint("MissingPermission")
    fun findDevice() {
        mBluetoothAdapter.startDiscovery()
    }


    /**
     * 获取已绑定设备
     */
    @SuppressLint("MissingPermission")
    fun getBondedDeviceList(): List<BluetoothDevice?>? {
        return ArrayList(mBluetoothAdapter.bondedDevices)
    }

    /**
     * 判断蓝牙是否连接
     */
    @SuppressLint("MissingPermission")
    fun isConnectBlue(bluetoothSocket: BluetoothSocket?): Boolean {
        return bluetoothSocket != null && bluetoothSocket.isConnected
    }
}

3. 编写Compose UI页面

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

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

    @Composable
    fun MainScreen() {
        var expanded = remember {
            mutableStateOf(false)
        }


        Column(
            modifier = Modifier.fillMaxSize()
        )
        {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(Blue)
                    .padding(vertical = 12.dp)
                    .height(35.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.Start
            ) {
                Text(
                    text = "可用设备",
                    modifier = Modifier
                        .weight(1f)
                        .offset(10.dp)
                )

                if(isRefresh.value){
                    CircularProgressIndicator(
                        modifier = Modifier.size(25.dp),
                        color = White
                    )
                }

                Box() {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_setting),
                        contentDescription = null,
                        modifier = Modifier
                            .width(50.dp)
                            .fillMaxHeight()
                            .clickable {
                                expanded.value = true
                            },
                    )

                    if(expanded.value){
                        DropdownMenu(
                            expanded = expanded.value,
                            onDismissRequest = {
                                expanded.value = false
                            }) {
                            data.forEachIndexed{ index: Int, s: String ->
                                DropdownMenuItem(onClick = {
                                    when (index) {
                                        0 -> {
                                            if(BlueToothController.isBluetoothSupport()){
                                                Toast.makeText(this@MainActivity,"本机支持蓝牙功能",Toast.LENGTH_SHORT).show()
                                            }else{
                                                Toast.makeText(this@MainActivity,"本机暂不支持蓝牙功能",Toast.LENGTH_SHORT).show()
                                            }
                                        }
                                        1 -> {
                                            if(BlueToothController.isBluetoothEnabled()){
                                                Toast.makeText(this@MainActivity,"用户允许开启蓝牙",Toast.LENGTH_SHORT).show()
                                            }else{
                                                Toast.makeText(this@MainActivity,"用户拒绝开启蓝牙",Toast.LENGTH_SHORT).show()
                                            }
                                        }
                                        2 -> {
                                            selected.value = 3
                                            Log.d(TAG,"查看已绑定设备")
                                            if(BlueToothController.isStartDiscovering()){
                                                BlueToothController.cancelFindDevice()
                                            }
                                            deviceList.clear()
                                            for (device in BlueToothController.getBondedDeviceList()!!){
                                                deviceList.add(device!!)
                                            }

                                        }
                                        3 -> {
                                            if(BlueToothController.isStartDiscovering()){
                                                Log.d(TAG,"停止查找")
                                                BlueToothController.cancelFindDevice()
                                                deviceList!!.clear()
                                            }
                                            selected.value = 4
                                            BlueToothController.findDevice()
                                            Log.d(TAG,"开始查找")
                                        }
                                    }
                                    Log.d(TAG,selected.value.toString())
                                    expanded.value = false
                                }) {
                                    Text(text = s)
                                }
                            }
                        }
                    }
                }
            }

            DeviceListView()
        }
        if(openDialog.value){
            AlterDialog()
        }
    }

AlterDialog:    用来显示弹窗

    @Composable
    fun AlterDialog() {
            AlertDialog(
                onDismissRequest = { openDialog.value = false },
                title = { Text(text = text.value) },
                text = {
                    Text(
                        text = "0c 11 09 41 23 00 01 03 FF"
                    )
                }, confirmButton = {
                    TextButton(onClick = {
                        openDialog.value = false
                        sendMessage()
                    }) {
                        Text(text = "发送")
                    }
                }, dismissButton = {
                    TextButton(onClick = { openDialog.value = false }) {
                        Text(text = "取消")
                    }
                })
    }

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


    @Composable
    fun DeviceListView(){
        LazyColumn(
            Modifier
                .fillMaxSize(),
            contentPadding =  PaddingValues(5.dp,1.dp),
            verticalArrangement = Arrangement.spacedBy(5.dp)
        ){
            items(deviceList!!.size){ index->
                ListItem(index, deviceList[index])
            }
        }
    }

    

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


    @Composable
    fun ListItem(index: Int, blueToothDevice: BluetoothDevice){
        Card(
            shape = RoundedCornerShape(4.dp),
            elevation = 2.dp
        ) {
            Row(
                modifier = Modifier
                    .height(50.dp)
                    .fillMaxWidth()
                    .clickable {
                        openDialog.value = true
                        if (blueToothDevice.name == null) {
                            text.value = "N/A"
                        } else {
                            text.value = blueToothDevice.name
                        }

                        //Gatt协议连接蓝牙
                        var bluetoothGatt =
                            blueToothDevice.connectGatt(this@MainActivity, true, mGattCallback)
                        bluetoothGatt.connect()
                        Log.d(TAG, "点击了第$index 个item")
                    },
                verticalAlignment = Alignment.CenterVertically,
            ) {

                Image(
                    painter = painterResource(R.drawable.ic_blue),
                    contentDescription = null,
                    modifier = Modifier
                        .fillMaxHeight()
                        .padding(all = 5.dp)
                )

                Column(
                    modifier = Modifier.fillMaxWidth()
                ) {

                    if(blueToothDevice.name==null){
                        Text(
                            text = "N/A",
                            fontWeight = FontWeight.Bold
                        )
                    }else{
                        Text(
                            text = blueToothDevice.name,
                            fontWeight = FontWeight.Bold
                        )
                    }
                    Text(
                        text = blueToothDevice.address,
                    )
                }

            }
        }
    }

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

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

package com.example.bluetoothcompose

import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.annotation.SuppressLint
import android.bluetooth.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.PermissionChecker.PERMISSION_GRANTED
import com.example.bluetoothcompose.ui.theme.Blue
import com.example.bluetoothcompose.ui.theme.BlueToothComposeTheme
import com.example.bluetoothcompose.ui.theme.White
import java.util.*

class MainActivity : ComponentActivity() {

    private val TAG = "yf"
    private var deviceList = mutableStateListOf<BluetoothDevice>()
    private var data = mutableListOf(
        "检查设备是否支持蓝牙",
        "检查设备是否开启蓝牙",
        "查看已配过的蓝牙设备",
        "查找蓝牙设备"
    )

    var selected = mutableStateOf(0)
    var openDialog = mutableStateOf(false)
    var text = mutableStateOf("")

    var mGatt: BluetoothGatt? = null
    var mWriter: BluetoothGattCharacteristic? = null




    private var isRefresh = mutableStateOf(false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            BlueToothComposeTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    MainScreen()
                }
            }
        }
    }

    override fun onStart() {
        super.onStart()

        isPermission()
        registerBluetoothReceiver()
    }

    //处理找到蓝牙设备和搜索完成的广播消息
    var receiver: BroadcastReceiver = object : BroadcastReceiver() {
        @SuppressLint("MissingPermission")
        override fun onReceive(context: Context, intent: Intent) {
            val action = intent.action

            //开始查找设备
            when {
                BluetoothAdapter.ACTION_DISCOVERY_STARTED == action -> {
                    //开始搜索
                    if(deviceList!=null){
                        deviceList!!.clear()
                    }

                    isRefresh.value = true
                }
                BluetoothDevice.ACTION_FOUND == action -> {
                    //搜到蓝牙设备
                    val device =
                        intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)

                    //把搜索到的设备添加到已找到列表中,显示它的信息
                    deviceList?.add(device!!)
                    Log.d(TAG,"找到了: ${deviceList.size}")

                }
                BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action -> {
                    //搜索完毕
                    isRefresh.value = false
                    when (selected.value) {
                        3 -> {

                        }
                        4 -> {
                            Toast.makeText(this@MainActivity,"选择要配对的蓝牙设备",Toast.LENGTH_SHORT).show()

                        }
                    }
                }
                BluetoothDevice.ACTION_BOND_STATE_CHANGED == action -> {
                    val device =
                        intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                    if (device == null) {
                        Toast.makeText(this@MainActivity,"无设备",Toast.LENGTH_SHORT).show()

                        return
                    }
                    val state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0)
                    when (state) {
                        BluetoothDevice.BOND_BONDED -> {
                            Toast.makeText(this@MainActivity,"已配对",Toast.LENGTH_SHORT).show()

                        }
                        BluetoothDevice.BOND_BONDING -> {
                            Toast.makeText(this@MainActivity,"正在配对",Toast.LENGTH_SHORT).show()

                        }
                        BluetoothDevice.BOND_NONE -> {
                            Toast.makeText(this@MainActivity,"未配对",Toast.LENGTH_SHORT).show()

                        }
                    }
                }
            }
        }
    }


    //动态获取位置权限
    @SuppressLint("WrongConstant")
    private fun isPermission() {
        if (checkSelfPermission(ACCESS_COARSE_LOCATION) !== PERMISSION_GRANTED
            || checkSelfPermission(ACCESS_FINE_LOCATION) !== PERMISSION_GRANTED
        ) {
            requestPermissions(
                arrayOf(
                    ACCESS_COARSE_LOCATION,
                    ACCESS_FINE_LOCATION
                ), 200
            )
        }
    }

    private fun registerBluetoothReceiver() {
        //filter注册广播接收器
        val filter = IntentFilter()


        //蓝牙当前状态
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)


        //开始扫描蓝牙设备广播
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)

        //找到蓝牙设备广播
        filter.addAction(BluetoothDevice.ACTION_FOUND)

        //扫描蓝牙设备结束广播
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)

        //蓝牙设备配对状态改变广播
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)

        //设备扫描模式改变广播
        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
        registerReceiver(receiver, filter)
    }


    @SuppressLint("MissingPermission")
    @Composable
    fun MainScreen() {
        var expanded = remember {
            mutableStateOf(false)
        }


        Column(
            modifier = Modifier.fillMaxSize()
        )
        {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(Blue)
                    .padding(vertical = 12.dp)
                    .height(35.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.Start
            ) {
                Text(
                    text = "可用设备",
                    modifier = Modifier
                        .weight(1f)
                        .offset(10.dp)
                )

                if(isRefresh.value){
                    CircularProgressIndicator(
                        modifier = Modifier.size(25.dp),
                        color = White
                    )
                }

                Box() {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_setting),
                        contentDescription = null,
                        modifier = Modifier
                            .width(50.dp)
                            .fillMaxHeight()
                            .clickable {
                                expanded.value = true
                            },
                    )

                    if(expanded.value){
                        DropdownMenu(
                            expanded = expanded.value,
                            onDismissRequest = {
                                expanded.value = false
                            }) {
                            data.forEachIndexed{ index: Int, s: String ->
                                DropdownMenuItem(onClick = {
                                    when (index) {
                                        0 -> {
                                            if(BlueToothController.isBluetoothSupport()){
                                                Toast.makeText(this@MainActivity,"本机支持蓝牙功能",Toast.LENGTH_SHORT).show()
                                            }else{
                                                Toast.makeText(this@MainActivity,"本机暂不支持蓝牙功能",Toast.LENGTH_SHORT).show()
                                            }
                                        }
                                        1 -> {
                                            if(BlueToothController.isBluetoothEnabled()){
                                                Toast.makeText(this@MainActivity,"用户允许开启蓝牙",Toast.LENGTH_SHORT).show()
                                            }else{
                                                Toast.makeText(this@MainActivity,"用户拒绝开启蓝牙",Toast.LENGTH_SHORT).show()
                                            }
                                        }
                                        2 -> {
                                            selected.value = 3
                                            Log.d(TAG,"查看已绑定设备")
                                            if(BlueToothController.isStartDiscovering()){
                                                BlueToothController.cancelFindDevice()
                                            }
                                            deviceList.clear()
                                            for (device in BlueToothController.getBondedDeviceList()!!){
                                                deviceList.add(device!!)
                                            }

                                        }
                                        3 -> {
                                            if(BlueToothController.isStartDiscovering()){
                                                Log.d(TAG,"停止查找")
                                                BlueToothController.cancelFindDevice()
                                                deviceList!!.clear()
                                            }
                                            selected.value = 4
                                            BlueToothController.findDevice()
                                            Log.d(TAG,"开始查找")
                                        }
                                    }
                                    Log.d(TAG,selected.value.toString())
                                    expanded.value = false
                                }) {
                                    Text(text = s)
                                }
                            }
                        }
                    }
                }
            }

            DeviceListView()
        }
        if(openDialog.value){
            AlterDialog()
        }
    }

    @Preview(
        showBackground = true,
        group = "Group1",
    )
    @Composable
    fun DefaultPreview() {
        MainScreen()
    }


    @SuppressLint("MissingPermission")
    @Composable
    fun DeviceListView(){
        LazyColumn(
            Modifier
                .fillMaxSize(),
            contentPadding =  PaddingValues(5.dp,1.dp),
            verticalArrangement = Arrangement.spacedBy(5.dp)
        ){
            items(deviceList!!.size){ index->
                ListItem(index, deviceList[index])
            }
        }
    }

    @SuppressLint("MissingPermission")
    @Composable
    fun ListItem(index: Int, blueToothDevice: BluetoothDevice){
        Card(
            shape = RoundedCornerShape(4.dp),
            elevation = 2.dp
        ) {
            Row(
                modifier = Modifier
                    .height(50.dp)
                    .fillMaxWidth()
                    .clickable {
                        openDialog.value = true
                        if (blueToothDevice.name == null) {
                            text.value = "N/A"
                        } else {
                            text.value = blueToothDevice.name
                        }

                        //Gatt协议连接蓝牙
                        var bluetoothGatt =
                            blueToothDevice.connectGatt(this@MainActivity, true, mGattCallback)
                        bluetoothGatt.connect()
                        Log.d(TAG, "点击了第$index 个item")
                    },
                verticalAlignment = Alignment.CenterVertically,
            ) {

                Image(
                    painter = painterResource(R.drawable.ic_blue),
                    contentDescription = null,
                    modifier = Modifier
                        .fillMaxHeight()
                        .padding(all = 5.dp)
                )

                Column(
                    modifier = Modifier.fillMaxWidth()
                ) {

                    if(blueToothDevice.name==null){
                        Text(
                            text = "N/A",
                            fontWeight = FontWeight.Bold
                        )
                    }else{
                        Text(
                            text = blueToothDevice.name,
                            fontWeight = FontWeight.Bold
                        )
                    }
                    Text(
                        text = blueToothDevice.address,
                    )
                }

            }
        }
    }


    @SuppressLint("MissingPermission")
    @Composable
    fun AlterDialog() {
            AlertDialog(
                onDismissRequest = { openDialog.value = false },
                title = { Text(text = text.value) },
                text = {
                    Text(
                        text = "0c 11 09 41 23 00 01 03 FF"
                    )
                }, confirmButton = {
                    TextButton(onClick = {
                        openDialog.value = false
                        sendMessage()
                    }) {
                        Text(text = "发送")
                    }
                }, dismissButton = {
                    TextButton(onClick = { openDialog.value = false }) {
                        Text(text = "取消")
                    }
                })
    }


    private val mGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
        @SuppressLint("MissingPermission")
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            //连接成功
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                //进行服务发现
                gatt.discoverServices()
                Log.d(TAG, "连接成功")
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                //连接断开,处理断开逻辑
                Log.d(TAG, "连接断开")
            }
        }

        @SuppressLint("MissingPermission")
        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            Log.d(TAG, "onServicesDiscovered : $status ==>> $gatt")

            //发现服务成功,处理服务和特征值
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //发送消息
                mGatt = gatt
                val service =
                    gatt.getService(UUID.fromString("0000180a-0000-1000-8000-00805F9B34FB"))
                mWriter =
                    service.getCharacteristic(UUID.fromString("00002ad9-0000-1000-8000-00805F9B34FB"))

                //打开消息通知
                mGatt!!.setCharacteristicNotification(mWriter, true)
                val descriptor: BluetoothGattDescriptor =
                    mWriter!!.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
                descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                mGatt!!.writeDescriptor(descriptor)

            } else {
                Log.d(TAG, "发现服务失败")
            }
        }

        override fun onCharacteristicRead(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            Log.e(TAG, "onCharacteristicRead $status")
            //读取特征成功,处理特征值
            if (status == BluetoothGatt.GATT_SUCCESS) {
            }
        }

        override fun onCharacteristicWrite(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic,
            status: Int
        ) {
            Log.e(TAG, "onCharacteristicWrite $status")
            //写入特征成功
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, "发送成功")
            } else {
                Log.d(TAG, "发送失败")
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {

            //接收到数据
            val data = characteristic.value
            //处理接收到的数据
            Log.d(TAG, "Received data: " + bytesToHexFun2(data))
        }

        override fun onDescriptorRead(
            gatt: BluetoothGatt,
            descriptor: BluetoothGattDescriptor,
            status: Int
        ) {
            super.onDescriptorRead(gatt, descriptor, status)
        }

        override fun onDescriptorWrite(
            gatt: BluetoothGatt,
            descriptor: BluetoothGattDescriptor,
            status: Int
        ) {
            super.onDescriptorWrite(gatt, descriptor, status)
        }

        override fun onReliableWriteCompleted(gatt: BluetoothGatt, status: Int) {
            super.onReliableWriteCompleted(gatt, status)
        }

        override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
            super.onReadRemoteRssi(gatt, rssi, status)
        }

        override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
            super.onMtuChanged(gatt, mtu, status)
        }

        override fun onServiceChanged(gatt: BluetoothGatt) {
            super.onServiceChanged(gatt)
        }

        override fun onPhyUpdate(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status)
        }

        override fun onPhyRead(gatt: BluetoothGatt, txPhy: Int, rxPhy: Int, status: Int) {
            super.onPhyRead(gatt, txPhy, rxPhy, status)
        }
    }


    private fun bytesToHexFun2(bytes: ByteArray): String? {
        var result = 0
        for (i in bytes.indices) {
            result += bytes[i]
        }
        return byte2Hex((result.inv() and 0xFF).toByte())
    }
    fun byte2Hex(inByte: Byte?): String //1字节转2个Hex字符
    {
        return String.format("%02x", inByte).toUpperCase()
    }


    @SuppressLint("MissingPermission")
    fun sendMessage(){
        if (null == mWriter) {
            Log.e("yf123", "ble:发送失败:null == writer !!!!")
        } else {
            mWriter!!.value = byteArrayOf(
                0x0c.toByte(),
                0x11.toByte(),
                0x09.toByte(),
                0x41.toByte(),
                0x23.toByte(),
                0x00.toByte(),
                0x01.toByte(),
                0x03.toByte(),
                0xFF.toByte()
            )
            mGatt!!.writeCharacteristic(mWriter)
        }
    }

}



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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/957770.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

upload-labs靶场通关详解

文章目录 Pass-01Pass-02Pass-03Pass-04Pass-05Pass-06Pass-07Pass-08Pass-09Pass-10Pass-11Pass-12Pass-13Pass-14Pass-15Pass-16Pass-17Pass-18Pass-19Pass-20方法一&#xff08;文件夹名欺骗绕过&#xff09;方法二&#xff08;%00截断攻击&#xff09; Pass-21 Pass-01 绕过…

Vert.x 源码解析(4.x)(一)——Context源码解析

目录 1.简介 Vert.x 中&#xff0c;多线程环境下的资源管理和状态维护是一个复杂的问题。为了解决这个问题&#xff0c;Vert.x 引入了 Context 这个核心概念。Context 负责在多线程环境下协调异步操作&#xff0c;提供线程安全的资源访问&#xff0c;并确保异步操作的正确执行…

Spring MVC工作流程

SpringMVC 的执行流程如下。 用户通过浏览器发起一个 HTTP 请求&#xff0c;该请求会被 DispatcherServlet&#xff08;前端控制器&#xff09;拦截&#xff1b;DispatcherServlet 调用 HandlerMapping&#xff08;处理器映射器&#xff09;找到具体的处理器&#xff08;Handl…

LinuxUbuntu安装OpenWAF

Linux&Ubuntu安装OpenWAF 官方GitHub地址 介绍 OpenWAF&#xff08;Web Application Firewall&#xff09;是一个开源的Web应用防火墙&#xff0c;用于保护Web应用程序免受各种网络攻击。它通过与Web服务器集成&#xff0c;监控和过滤对Web应用程序的流量&#xff0c;识…

基于移动端的校园失物招领系统 微信小程序的设计与实现779m5

于校园失物招领系统功能所牵扯的数据都是通过失主进行校园失物招领系统等相关的数据信息内容、并且可以实现首页、个人中心、失主管理、物品类型管理、失物展示管理、失物认领管理、在线投诉管理、论坛交流、系统管理等功能可以通过系统进行分配&#xff0c;传统的手工作业模式…

WebDAV之π-Disk派盘 + notototo

notototo是一款功能丰富的笔记软件,提供了多种功能,包括载入PDF文件并进行批注和标记的能力。您可以使用Apple Pencil或手指在PDF文件上进行写作和绘图操作。 同时,notototo也提供了与团队合作的功能,您可以连接到服务器并与他人协作。此外,您还可以在notototo中进行绘图,…

Dolphin for Mac(Wii游戏模拟器)配置指南

Wii模拟器Dolphin Mac是款适合Mac电脑中的游戏玩家们使用的模拟器工具。Wii模拟器Dolphin Mac官方版支持直接运行游戏镜像文件&#xff0c;玩家可以将游戏ISO拷贝到某一个文件夹中统一进行管理。Wii模拟器Dolphin Mac除了键盘和鼠标外&#xff0c;还支持配合原版的Wii遥控器操作…

MySQL告警“Connection attributes of length 570 were truncated“

mysql的错误日志中看到如下报错"[Warning] Connection attributes of length 571 were truncated"。比如&#xff1a; 2023-09-01T08:37:49.87392408:00 9149015 [Warning] [MY-010288] [Server] Connection attributes of length 570 were truncated (76 bytes los…

ip route get ip地址 应用案例

应用场景 在做虚拟化实验用的虚拟机和实际的ECS云主机一般都会有多个网卡&#xff0c;网络的联通性是经常碰到的问题。比如在一个VM上有3个网卡&#xff0c;分别为ens160(和寄主机进行桥接的网卡10.0.0.128)、ens224&#xff08;连接仅主机网络10.0.0.0/24的网卡10.0.0.128&…

三维模型OBJ格式轻量化顶点压缩主要技术方法分析

三维模型OBJ格式轻量化顶点压缩主要技术方法分析 三维模型的OBJ格式轻量化中&#xff0c;顶点压缩是一项重要的技术方法&#xff0c;用于减小模型文件的大小。以下是关于三维模型OBJ格式轻量化顶点压缩的主要技术方法的分析&#xff1a; 1、顶点位置量化&#xff1a; 顶点位置…

直播平台源码弹性云托管技术:稳定直播与降低成本的利器

在当今的互联网时代&#xff0c;直播平台源码层出不穷&#xff0c;直播平台源码不仅可以让人们获取最新的资讯、查找资料等信息获取&#xff0c;还能让人们在其中观看短视频、直播、与其他人聊天等互动放松&#xff0c;直播平台源码的受欢迎与平台人数的增加使得人们在选择直播…

Python爬虫(十七)_糗事百科案例

糗事百科实例 爬取糗事百科段子&#xff0c;假设页面的URL是: http://www.qiushibaike.com/8hr/page/1 要求&#xff1a; 使用requests获取页面信息&#xff0c;用XPath/re做数据提取获取每个帖子里的用户头像连接、用户姓名、段子内容、点赞次数和评论次数保存到json文件内…

使用spring自带的发布订阅机制来实现消息发布订阅

背景 公司的项目以前代码里面有存在使用spring自带发布订阅的代码&#xff0c;因此稍微学习一下如何使用&#xff0c;并了解一下这种实现方式的优缺点。 优点 实现方便&#xff0c;代码方面基本只需要定义消息体和消费者&#xff0c;适用于小型应用程序。不依赖外部中间件&a…

JavaScript设计模式(二)——简单工厂模式、抽象工厂模式、建造者模式

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

pdfh5在线预览pdf文件

前言 pc浏览器和ios的浏览器都可以直接在线显示pdf文件&#xff0c;但是android浏览器不能在线预览pdf文件&#xff0c;如何预览pdf文件&#xff1f; Github: https://github.com/gjTool/pdfh5 Gitee: https://gitee.com/gjTool/pdfh5 使用pdfh5预览pdf 编写预览页面 <…

Spring Framework 学习笔记1:基础

Spring Framework 学习笔记1&#xff1a;基础 1.简介 1.1.生态和发展史 关于 Spring 的生态和发展史&#xff0c;可以观看这个视频。 1.2.系统架构 关于 Spring 的系统架构&#xff0c;可以观看这个视频。 2.Ioc Spring 的核心概念是 Ioc &#xff08;Inversion Of Cont…

grafana8.3创建告警规则

1. 部署grafana的配置文件修改 因为要采用发送邮件的方式通知告警内容所以&#xff0c;在部署grafana时要先配置好SMTP / Emailing的内容&#xff1a; [smtp]enabled true # 开启smtphost smtp.mxhichina.com:465 #设置邮箱服务器地址user testtest.com #设置邮箱用户pas…

Flink SQL你用了吗?

分析&回答 Flink 1.1.0&#xff1a;第一次引入 SQL 模块&#xff0c;并且提供 TableAPI&#xff0c;当然&#xff0c;这时候的功能还非常有限。Flink 1.3.0&#xff1a;在 Streaming SQL 上支持了 Retractions&#xff0c;显著提高了 Streaming SQL 的易用性&#xff0c;使…

排序之选择排序

文章目录 前言一、直接选择排序1、直接选择排序基本思想2、直接选择排序代码实现3、直接选择排序的效率 二、堆排序1、堆排序2、堆排序的效率 前言 选择排序的基本思想就是每一次从待排序的数据元素中选出最小(或最大)的一个元素&#xff0c;存放在序列的起始位置&#xff0c;…

汇编--int指令

中断信息可以来自CPU的内部和外部&#xff0c; 当CPU的内部有需要处理的事情发生的时候&#xff0c;将产生需要马上处理的中断信息&#xff0c;引发中断过程。在http://t.csdn.cn/jihpG&#xff0c;我们讲解了中断过程和两种内中断的处理。 这一章中&#xff0c; 我们讲解另一种…