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蓝牙的基础知识和基础用法,不过上一篇都是一些零散碎片化的程序,,这一篇给大家分享Android蓝牙开发实战项目KotlinCompose的初步使用 效果演示 : Android Compose 蓝牙开发 Android蓝牙实战开发…...
涂鸦智能携手亚马逊云科技 共建“联合安全实验室” 为IoT发展护航
2023年8月31日,全球化IoT开发者平台涂鸦智能(NYSE: TUYA,HKEX: 2391)在“2023亚马逊云科技re:Inforce中国站”大会宣布与全球领先的云计算公司亚马逊云科技共同成立“联合安全实验室”,旨在加强IoT行业的安全合规能力与…...
Oracle21C--Windows卸载与安装
卸载方法: (1)WinR,输入services.msc,打开服务,把Oracle相关的服务全部停止运行(重要) (2)WinR,输入regedit,打开注册表,删除Oracle开…...
关于 MySQL、PostgresSQL、Mariadb 数据库2038千年虫问题
MySQL 测试时间:2023-8 启动MySQL服务后,将系统时间调制2038年01月19日03时14分07秒之后的日期,发现MySQL服务自动停止。 根据最新的MySQL源码(mysql-8.1.0)分析,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,且经过 B B B 的简单路径? 数据范围: 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官方网站: 官网…...
飞腾平台芯片测试固件(SFW)和开机启动log
一、说两句 最近公司飞腾产品越来越多了,FT-2000/4的D2000的X100的,最近又新出了E2000。越来越多新来的小孩儿开始加入到飞腾的调测试中,那么在他们实际的调试中会遇到很多的问题。在固件启动阶段有的板卡会有一些异常,有时我们需…...
【大数据实训】基于Hive的北京市天气系统分析报告(二)
博主介绍:✌全网粉丝6W,csdn特邀作者、博客专家、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于大数据技术领域和毕业项目实战✌ 🍅文末获取项目联系🍅 目录 1. 引言 1.1 项目背景 1 1.2 项目意义 1 2.…...
WPF列表样式
WPF的数据绑定系统自动生成列表项对象,为单个项应用所需的样式不是很容易。解决方案是ItemContainerStyle 属性。如果设置了ItemContainerStyle 属性,当创建列表项时,列表控件会将其向下传递给每个项。对于ListBox控件,每个项有Li…...
Android逆向学习(二)vscode进行双开与图标修改
Android逆向学习(二)vscode进行双开与图标修改 写在前面 这其实应该还是吾爱的第一个作业,但是写完上一个博客的时候已经比较晚了,如果继续敲机械键盘吵到室友,我怕我看不到明天的太阳,所以我决定分成两篇…...
一个基于YAPI接口生产代码的开源工具
前后端分离的开发模式是一种趋势,但如果缺少好的开发工具跟管理模式,会使得前后端开发人员相互等待,扯皮等问题。从而影响项目的交付进度。 通过实践摸索,YAPI是一款很适合前后端分离开发的协助工具。它以项目为维度,可…...
Redis 缓存穿透击穿和雪崩
一、说明 Redis 缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对…...
在windows上配置ninja环境
ninja使用并行任务来编译工程,比cmake编译快了一个数量级,是谷歌在2010年为了提高cmake的编译速度而开发一款编译工具。下面介绍在windows上配置ninja环境。 1 下载ninja ninja官网地址: https://github.com/ninja-build/ninja/releases …...
③matlab向量和矩阵
目录 手动输入数组 创建等间距向量 数组创建函数 手动输入数组 1.背景 单个称为标量的数值实际上是一个 11 数组,也即它包含 1 行 1 列。 任务 创建一个名为 x 并且值为 4 的变量。 2.您可以使用方括号创建包含多个元素的数组。 x [3 5] x 3 5 任务 …...
一、了解[mysql]索引底层结构和算法
目录 一、索引1.索引的本质2.mysql的索引结构 二、存储引擎1.MyISAM2.InnoDB3.为什么建议InnoDB表要建立主键并且推荐int类型自增?4.innodb的主键索引和非主键索引(二级索引)区别5.联合索引 一、索引 1.索引的本质 索引:帮助mysql高效获取数…...
DockerFile常用命令
以下是常见的Dockerfile命令: FROM:FROM命令用于指定基础镜像。基础镜像是构建镜像的起点。例如,FROM ubuntu:latest表示使用最新版本的Ubuntu作为基础镜像。 MAINTAINER:MAINTAINER命令用于指定镜像的维护者信息。一般格式为&am…...
Android 动画之插值器PathInterpolator
Android 的View动画、属性动画都可以设置动画插值器,以此来实现不同的动画效果。 这篇文章 Android View动画整理 有介绍各种插值器的效果,这一篇专访 PathInterpolator 。 参考官网 添加曲线动作 , PathInterpolator 基于 贝塞尔曲线 或 …...
递归学习(转载)
转载至 https://www.cnblogs.com/king-lps/p/10748535.html 为避免原文丢失,因此原文转载作者【三年一梦】的帖子 前言 相信不少同学和我一样,在刚学完数据结构后开始刷算法题时,遇到递归的问题总是很头疼,而一看解答,…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...
