当前位置: 首页 > news >正文

Android 蓝牙开发( 四 )

前言

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

效果演示 : 

Android Compose 蓝牙开发

Android蓝牙实战开发步骤

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

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

2.封装BluetoothAdapter类

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

package com.example.bluetoothcomposeimport 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.Intentobject 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

    @Composablefun 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 = 3Log.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 = 4BlueToothController.findDevice()Log.d(TAG,"开始查找")}}Log.d(TAG,selected.value.toString())expanded.value = false}) {Text(text = s)}}}}}}DeviceListView()}if(openDialog.value){AlterDialog()}}

AlterDialog:    用来显示弹窗

    @Composablefun 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 = falsesendMessage()}) {Text(text = "发送")}}, dismissButton = {TextButton(onClick = { openDialog.value = false }) {Text(text = "取消")}})}

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

@Composablefun 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样式

@Composablefun ListItem(index: Int, blueToothDevice: BluetoothDevice){Card(shape = RoundedCornerShape(4.dp),elevation = 2.dp) {Row(modifier = Modifier.height(50.dp).fillMaxWidth().clickable {openDialog.value = trueif (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.bluetoothcomposeimport 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? = nullvar mWriter: BluetoothGattCharacteristic? = nullprivate var isRefresh = mutableStateOf(false)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {BlueToothComposeTheme {// A surface container using the 'background' color from the themeSurface(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 = falsewhen (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")@Composablefun 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 = 3Log.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 = 4BlueToothController.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",)@Composablefun DefaultPreview() {MainScreen()}@SuppressLint("MissingPermission")@Composablefun 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")@Composablefun ListItem(index: Int, blueToothDevice: BluetoothDevice){Card(shape = RoundedCornerShape(4.dp),elevation = 2.dp) {Row(modifier = Modifier.height(50.dp).fillMaxWidth().clickable {openDialog.value = trueif (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")@Composablefun 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 = falsesendMessage()}) {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 = gattval 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_VALUEmGatt!!.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 = 0for (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蓝牙开发的可以继续看我下一篇给大家的分享

相关文章:

Android 蓝牙开发( 四 )

前言 上一篇文章给大家分享了Kotlin版的Android蓝牙的基础知识和基础用法&#xff0c;不过上一篇都是一些零散碎片化的程序&#xff0c;&#xff0c;这一篇给大家分享Android蓝牙开发实战项目KotlinCompose的初步使用 效果演示 : Android Compose 蓝牙开发 Android蓝牙实战开发…...

涂鸦智能携手亚马逊云科技 共建“联合安全实验室” 为IoT发展护航

2023年8月31日&#xff0c;全球化IoT开发者平台涂鸦智能&#xff08;NYSE: TUYA&#xff0c;HKEX: 2391&#xff09;在“2023亚马逊云科技re:Inforce中国站”大会宣布与全球领先的云计算公司亚马逊云科技共同成立“联合安全实验室”&#xff0c;旨在加强IoT行业的安全合规能力与…...

Oracle21C--Windows卸载与安装

卸载方法&#xff1a; &#xff08;1&#xff09;WinR&#xff0c;输入services.msc,打开服务&#xff0c;把Oracle相关的服务全部停止运行&#xff08;重要&#xff09; &#xff08;2&#xff09;WinR&#xff0c;输入regedit&#xff0c;打开注册表&#xff0c;删除Oracle开…...

关于 MySQL、PostgresSQL、Mariadb 数据库2038千年虫问题

MySQL 测试时间&#xff1a;2023-8 启动MySQL服务后&#xff0c;将系统时间调制2038年01月19日03时14分07秒之后的日期&#xff0c;发现MySQL服务自动停止。 根据最新的MySQL源码&#xff08;mysql-8.1.0&#xff09;分析&#xff0c;sql/sql_parse.cc中依然存在2038年千年虫…...

Linux - Docker 安装使用 常用命令 教程

Docker 官方文档地址: Get Started | Docker 中文参考手册: https://docker_practice.gitee.io/zh-cn/ 1.什么是 Docker 1.1 官方定义 最新官网首页 # 1.官方介绍 - We have a complete container solution for you - no matter who you are and where you are on your contain…...

AtCoder Beginner Contest 318 G - Typical Path Problem 题解

G - Typical Path Problem 题目大意 给定一张 N N N 个点、 M M M 条边的简单无向图 G G G 和三个整数 A , B , C A,B,C A,B,C。 是否存在一条从顶点 A A A 到 C C C&#xff0c;且经过 B B B 的简单路径&#xff1f; 数据范围&#xff1a; 3 ≤ N ≤ 2 1 0 5 3\le …...

21.4 CSS 盒子模型

1. 边框样式 border-style属性: 指定元素的边框样式.常用属性值: - none: 无边框(默认值). - solid: 实线边框. - dotted: 点状边框. - dashed: 虚线边框. - double: 双线边框. - groove: 凹槽状边框. - ridge: 脊状边框. - inset: 内阴影边框. - outset: 外阴影边框.这些值可…...

MybatisPlus入门

MybatisPlus入门 1.MyBatis-Plus1.1 ORM介绍1.2 MyBatis-Plus介绍 2.代码链接数据库2.1 创建项目2.2 添加依赖2.3 链接数据库2.3.1 准备数据库2.3.2 链接数据库2.3.3 创建实体类 2.4 创建Mapper层2.5 创建Controller层2.6 浏览器访问测试 MybatisPlus官方网站&#xff1a; 官网…...

飞腾平台芯片测试固件(SFW)和开机启动log

一、说两句 最近公司飞腾产品越来越多了&#xff0c;FT-2000/4的D2000的X100的&#xff0c;最近又新出了E2000。越来越多新来的小孩儿开始加入到飞腾的调测试中&#xff0c;那么在他们实际的调试中会遇到很多的问题。在固件启动阶段有的板卡会有一些异常&#xff0c;有时我们需…...

【大数据实训】基于Hive的北京市天气系统分析报告(二)

博主介绍&#xff1a;✌全网粉丝6W,csdn特邀作者、博客专家、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于大数据技术领域和毕业项目实战✌ &#x1f345;文末获取项目联系&#x1f345; 目录 1. 引言 1.1 项目背景 1 1.2 项目意义 1 2.…...

WPF列表样式

WPF的数据绑定系统自动生成列表项对象&#xff0c;为单个项应用所需的样式不是很容易。解决方案是ItemContainerStyle 属性。如果设置了ItemContainerStyle 属性&#xff0c;当创建列表项时&#xff0c;列表控件会将其向下传递给每个项。对于ListBox控件&#xff0c;每个项有Li…...

Android逆向学习(二)vscode进行双开与图标修改

Android逆向学习&#xff08;二&#xff09;vscode进行双开与图标修改 写在前面 这其实应该还是吾爱的第一个作业&#xff0c;但是写完上一个博客的时候已经比较晚了&#xff0c;如果继续敲机械键盘吵到室友&#xff0c;我怕我看不到明天的太阳&#xff0c;所以我决定分成两篇…...

一个基于YAPI接口生产代码的开源工具

前后端分离的开发模式是一种趋势&#xff0c;但如果缺少好的开发工具跟管理模式&#xff0c;会使得前后端开发人员相互等待&#xff0c;扯皮等问题。从而影响项目的交付进度。 通过实践摸索&#xff0c;YAPI是一款很适合前后端分离开发的协助工具。它以项目为维度&#xff0c;可…...

Redis 缓存穿透击穿和雪崩

一、说明 Redis 缓存的使用&#xff0c;极大的提升了应用程序的性能和效率&#xff0c;特别是数据查询方面。但同时&#xff0c;它也带来了一些问题。其中&#xff0c;最要害的问题&#xff0c;就是数据的一致性问题&#xff0c;从严格意义上讲&#xff0c;这个问题无解。如果对…...

在windows上配置ninja环境

ninja使用并行任务来编译工程&#xff0c;比cmake编译快了一个数量级&#xff0c;是谷歌在2010年为了提高cmake的编译速度而开发一款编译工具。下面介绍在windows上配置ninja环境。 1 下载ninja ninja官网地址&#xff1a; https://github.com/ninja-build/ninja/releases   …...

③matlab向量和矩阵

目录 手动输入数组 创建等间距向量 数组创建函数 手动输入数组 1.背景 单个称为标量的数值实际上是一个 11 数组&#xff0c;也即它包含 1 行 1 列。 任务 创建一个名为 x 并且值为 4 的变量。 2.您可以使用方括号创建包含多个元素的数组。 x [3 5] x 3 5 任务 …...

一、了解[mysql]索引底层结构和算法

目录 一、索引1.索引的本质2.mysql的索引结构 二、存储引擎1.MyISAM2.InnoDB3.为什么建议InnoDB表要建立主键并且推荐int类型自增&#xff1f;4.innodb的主键索引和非主键索引&#xff08;二级索引&#xff09;区别5.联合索引 一、索引 1.索引的本质 索引:帮助mysql高效获取数…...

DockerFile常用命令

以下是常见的Dockerfile命令&#xff1a; FROM&#xff1a;FROM命令用于指定基础镜像。基础镜像是构建镜像的起点。例如&#xff0c;FROM ubuntu:latest表示使用最新版本的Ubuntu作为基础镜像。 MAINTAINER&#xff1a;MAINTAINER命令用于指定镜像的维护者信息。一般格式为&am…...

Android 动画之插值器PathInterpolator

Android 的View动画、属性动画都可以设置动画插值器&#xff0c;以此来实现不同的动画效果。 这篇文章 Android View动画整理 有介绍各种插值器的效果&#xff0c;这一篇专访 PathInterpolator 。 参考官网 添加曲线动作 &#xff0c; PathInterpolator 基于 贝塞尔曲线 或 …...

递归学习(转载)

转载至 https://www.cnblogs.com/king-lps/p/10748535.html 为避免原文丢失&#xff0c;因此原文转载作者【三年一梦】的帖子 前言 相信不少同学和我一样&#xff0c;在刚学完数据结构后开始刷算法题时&#xff0c;遇到递归的问题总是很头疼&#xff0c;而一看解答&#xff0c…...

Stitches API完全指南:从基础配置到自定义扩展

Stitches API完全指南&#xff1a;从基础配置到自定义扩展 【免费下载链接】stitches HTML5 Sprite Sheet Generator 项目地址: https://gitcode.com/gh_mirrors/sti/stitches Stitches是一款强大的HTML5 Sprite Sheet Generator&#xff0c;它提供了直观的API接口&…...

Kerberos身份认证原理与企业级排错实战指南

1. 这不是“另一个登录框”&#xff0c;而是一套精密运转的身份验证齿轮系统很多人第一次听说 Kerberos&#xff0c;是在公司内网登录邮箱或访问内部系统时&#xff0c;看到那个带小盾牌图标的弹窗——“正在使用 Kerberos 协议进行身份验证”。于是下意识觉得&#xff1a;“哦…...

ParaView时间戳设置全攻略:从基础标注到自定义格式(5.8.0实测)

ParaView时间戳设置全攻略&#xff1a;从基础标注到自定义格式&#xff08;5.8.0实测&#xff09; 在科学可视化领域&#xff0c;时间戳不仅是数据演变的见证者&#xff0c;更是研究成果呈现的专业语言。ParaView作为开源可视化工具链的标杆&#xff0c;其时间标注功能在学术论…...

2026年HR招聘偏好白皮书:这5项附加技能出现频率暴涨

2026 年的招聘市场&#xff0c;正在从“看你会什么岗位技能”&#xff0c;转向“看你能不能把岗位做得更智能”。HR筛简历时&#xff0c;越来越关注候选人的AI应用能力、数据化思维和业务落地能力。人社部近年发布的新职业中&#xff0c;已经出现生成式人工智能系统应用员、人工…...

CANN-昇腾NPU-RAG推理-检索增强生成怎么部署

RAG&#xff08;Retrieval-Augmented Generation&#xff09;是 LLM 知识库的组合&#xff1a;先检索相关文档&#xff0c;再让 LLM 基于文档回答。昇腾NPU 上部署 RAG 需要两个组件&#xff1a;Embedding 模型&#xff08;做向量检索&#xff09;和 LLM&#xff08;做生成&am…...

量子软件测试的挑战与优化策略

1. 量子软件测试的挑战与机遇量子计算正在从实验室走向实际应用&#xff0c;随之而来的是对可靠量子软件的需求激增。与传统软件不同&#xff0c;量子程序面临三大独特挑战&#xff1a;首先&#xff0c;量子态的叠加性和纠缠性使得测试变得异常复杂。一个n量子比特系统可以同时…...

网络配置工具类详解

CNet 网络配置工具类详解平台&#xff1a;仅支持 Linux&#xff0c;大量使用 ioctl 系统调用一、概述 CNet 是一个 纯静态方法的网络配置工具类&#xff0c;封装了 Linux 下常用的网络操作&#xff1a;功能类别涵盖内容IP 地址读取/设置本机 IP、子网掩码网关读取/添加/删除/设…...

告别SVN恐惧症:美术策划也能轻松上手的Unity PlasticSCM极简入门(附团队项目拉取实战)

告别SVN恐惧症&#xff1a;美术策划也能轻松上手的Unity PlasticSCM极简入门&#xff08;附团队项目拉取实战&#xff09; 在游戏开发团队中&#xff0c;版本控制系统是协作的基石&#xff0c;但传统工具如SVN往往让非技术成员望而生畏。当美术资源频繁更新、策划案不断迭代时&…...

总线式智能提示灯系统设计:从恒流驱动到模块化架构

1. 项目概述&#xff1a;从传统到智能的剧场提示灯系统革新在剧场、演播室或者大型活动现场的后台&#xff0c;如果你待过&#xff0c;一定对那套“红灯停&#xff0c;绿灯行”的提示灯系统不陌生。导演或舞台监督通过对讲机喊“Standby”&#xff08;准备&#xff09;&#xf…...

微信小程序项目实战:从npm安装Vant Weapp到解决样式冲突的完整避坑指南

微信小程序工程化实战&#xff1a;Vant Weapp集成与样式冲突解决方案全解析 第一次在小程序里引入Vant Weapp时&#xff0c;我对着满屏错位的组件样式发呆了半小时——原本优雅的按钮变成了扭曲的色块&#xff0c;表单元素叠在一起像抽象画。这不是个例&#xff0c;根据社区反…...