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 为避免原文丢失,因此原文转载作者【三年一梦】的帖子 前言 相信不少同学和我一样,在刚学完数据结构后开始刷算法题时,遇到递归的问题总是很头疼,而一看解答,…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...