Android Ble蓝牙App(七)扫描过滤
Ble蓝牙App(七)扫描过滤
- 前言
- 目录
- 正文
- 一、增加菜单
- 二、使用MMKV
- ① 添加依赖
- ② 封装MMKV
- ③ 使用MMKV
- 三、过滤空设备名
- 四、过滤Mac地址
- 五、过滤RSSI
- 六、源码
前言
在上一篇文章中了解了MTU的相关知识以及对于设备操作信息的展示,本篇文章中将增加扫描设备的过滤功能让你更方便的扫描想要找的低功耗蓝牙设备。
目录
- Ble蓝牙App(一)扫描
- Ble蓝牙App(二)连接与发现服务
- Ble蓝牙App(三)特性和属性
- Ble蓝牙App(四)UI优化和描述符
- Ble蓝牙App(五)数据操作
- Ble蓝牙App(六)请求MTU与显示设备信息
- Ble蓝牙App(七)扫描过滤
正文
增加扫描过滤主要就是让扫描设备的时候更方便找到想要的设备,下面我们来看有哪些功能的增加。
一、增加菜单
为了不占用扫描页面的空间,我打算通过添加菜单来进行扫描的过滤操作,那么首先我们在menu下增加一个menu_scan.xml文件,代码如下所示:
<menu xmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:id="@+id/item_filter_null"android:checkable="true"android:title="过滤空设备名" /><itemandroid:id="@+id/item_filter_mac"android:checkable="true"android:title="过滤Mac地址" /><itemandroid:id="@+id/item_filter_rssi"android:checkable="true"android:title="过滤RSSI" /></menu>
菜单中有三个Item,看一下预览效果图:
三个Item都是选中Item,选中表示这个过滤功能项启用,可以全部都选中,也可以任意选择,之后我们进入到ScanActivity,首先是创建菜单和菜单选中,修改地方有三处:
第一处:在onCreate()
函数中增加支持ActionBar,代码如下所示:
override fun onCreate(savedInstanceState: Bundle?) {...setSupportActionBar(binding.toolbar)...}
第二处:在ScanActivity中重写onCreateOptionsMenu()
函数,代码如下所示:
private lateinit var mMenu: Menuoverride fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_main, menu)mMenu = menureturn true}
创建选项菜单,再创建一个mMenu变量,在后面会用到的这个变量。
第三处:在ScanActivity中重写onOptionsItemSelected()
函数,代码如下所示:
override fun onOptionsItemSelected(item: MenuItem): Boolean {when(item.itemId) {R.id.item_filter_null -> { // 过滤空设备名称}R.id.item_filter_mac -> { // 过滤Mac地址}R.id.item_filter_rssi -> { // 过滤RSSI}}return true}
现在三个Item的点击事件中什么都不做,我们一步一步给它加上,现在菜单就创建好了。
二、使用MMKV
因为我们修改的菜单项会涉及到保存过滤设置的功能,所以需要将一些参数报错到手机中,那么我们可以使用SP、DataStore等方式,但是这里我是用MMKV,主要是因为用起来比较的方便,下面我们来使用MMKV。
① 添加依赖
MMKV是腾讯的一个开源项目,已经发布在mavenCentral()
仓库中了,我们在App中使用只需要在app模块下的build.gradle
中的dependencies{}
闭包中添加如下依赖代码即可:
dependencies {...//mmkvimplementation 'com.tencent:mmkv:1.2.14'
}
然后点击Sync Now
,同步一下,添加依赖就完成了。
② 封装MMKV
针对于MMKV的使用其实非常简单,就是两步,先初始化,然后使用就好了,那么为了使用的更方便,我们可以简单封装一下MMKV,做成一个工具类,下面我们在com.llw.goodble
包下新建一个utils
包,utils
包下新建一个MVUtils
类,代码如下所示:
object MVUtils {val mmkv = MMKV.defaultMMKV()fun put(key: String, value: Any): Boolean {return when (value) {is String -> mmkv.encode(key, value)is Float -> mmkv.encode(key, value)is Boolean -> mmkv.encode(key, value)is Int -> mmkv.encode(key, value)is Long -> mmkv.encode(key, value)is Double -> mmkv.encode(key, value)is ByteArray -> mmkv.encode(key, value)is Parcelable -> mmkv.encode(key, value)else -> false}}fun put(key: String, sets: Set<String>?): Boolean {if (sets == null) {return false}return mmkv.encode(key, sets)}fun getInt(key: String, defaultValue: Int = 0) = mmkv.decodeInt(key, defaultValue)fun getDouble(key: String, defaultValue: Double = 0.00) = mmkv.decodeDouble(key, defaultValue)fun getLong(key: String, defaultValue: Long = 0L) = mmkv.decodeLong(key, defaultValue)fun getBoolean(key: String, defaultValue: Boolean = false) = mmkv.decodeBool(key, defaultValue)fun getFloat(key: String, defaultValue: Float = 0F) = mmkv.decodeFloat(key, defaultValue)fun getByteArray(key: String) = mmkv.decodeBytes(key)fun getString(key: String, defaultValue: String = "") = mmkv.decodeString(key, defaultValue)inline fun <reified T : Parcelable> getParcelable(key: String) =mmkv.decodeParcelable(key, T::class.java)fun getStringSet(key: String) = mmkv.decodeStringSet(key, Collections.emptySet())fun removeKey(key: String) = mmkv.removeValueForKey(key)fun clearAll() = mmkv.clearAll()
}
这里实际上大体就分为三个部分,首先是初始化,然后是数据的存和取,最后是清除数据,是不是很简单呢?
③ 使用MMKV
使用MMKV,首先需要做的就是初始化,我们需要在BleApp
的onCreate()
函数中进行初始化,代码如下所示:
override fun onCreate() {...//mmkv初始化MMKV.initialize(this)}
使用MMKV同样是采用键值对的形式,那么基于我们的菜单功能,我们需要增加一些键,在BleConstant
中增加如下常量,代码如下所示:
//过滤RSSIconst val FILTER_RSSI_FLAG = "filterRssiFlag"//RSSI 值const val FILTER_RSSI_VALUE = "filterRssiValue"//过滤空设备名const val FILTER_NULL_FLAG = "filterNullFlag"//是否过滤Mac地址const val FILTER_MAC_FLAG = "filterMacFlag"//需要过滤的Mac地址const val FILTER_MAC_VALUE = "filterMacValue"
下面我们修改ScanActivity
中的onCreateOptionsMenu()
函数,代码如下所示:
override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_rssi).isChecked = MVUtils.getBoolean(FILTER_RSSI_FLAG)if (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {mMenu.findItem(R.id.item_filter_rssi).title ="过滤RSSI:-" + MVUtils.getInt(FILTER_RSSI_VALUE, 100)}mMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)return true}
在这里的代码就是在创建菜单的时候,判断一下保存的参数,是否需要选中Item,可以修改Item的选中状态和标题内容,这里就是获取参数。
三、过滤空设备名
下面我们来保存参数,修改onOptionsItemSelected()
函数中的代码:
R.id.item_filter_null -> { // 过滤空设备名称if (bleCore.isScanning()) stopScan()val filterNull = MVUtils.getBoolean(FILTER_NULL_FLAG)MVUtils.put(FILTER_NULL_FLAG, !filterNull)mMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)showMsg(if (MVUtils.getBoolean(FILTER_NULL_FLAG)) "过滤空设备名称的设备" else "保留空设备名称的设备")if (!bleCore.isScanning()) startScan()}
这里看到就是在点击过滤空设备Item时,首先停止扫描,然后获取参数值,再保存,根据值设置Item是否选中,最后开始扫描,那么我们怎么过滤这个空设备名称的设备呢?还需要修改扫描回调中的代码:
override fun onScanResult(result: ScanResult) {//过滤空设备名if (MVUtils.getBoolean(FILTER_NULL_FLAG)) {if (result.scanRecord!!.deviceName == null) {return}if (result.scanRecord!!.deviceName!!.isEmpty()) {return}}...}
这里我们只需要在原有的条件上再增加一个判断即可,因为缺省值是false,所以如果是不过滤空设备名就不会执行判断里面空处理和空设备名处理,看一下运行的效果。
我们看到默认是不过滤空设备名称的,当选中过滤空设备名后就会过滤设备名称为空的设备,只不过我们这里对于空设备名称的设备显示的UI还没有处理的很好,下面我们简单改一下,将onScanResult()
函数中的这一行代码:
val bleDevice = BleDevice(result.scanRecord!!.deviceName, result.device.address, result.rssi, result.device)
改成
val realName = result.scanRecord?.deviceName?.let { it.ifEmpty { BleConstant.UNKNOWN_DEVICE } } ?: BleConstant.UNKNOWN_DEVICEval bleDevice = BleDevice(realName, result.device.address, result.rssi, result.device)
这里改的目的就是首先判断获取的设备名是否为空,如果为空则返回一个Unknown device
作为设备名称,不为空则检查是否为空字符串,是的话也返回Unknown device
,不是则返回本身设备名称,再运行一下就可以了。
四、过滤Mac地址
下面我们要做过滤Mac地址,那么要过滤Mac地址,首先要输入Mac地址,那么我们可以写一个弹窗来进行输入的工作,在layout下创建一个dialog_settings_mac.xml
作为弹窗布局,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"><com.google.android.material.appbar.MaterialToolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:title="过滤Mac地址" /><com.google.android.material.textfield.TextInputLayoutandroid:id="@+id/data_layout"style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginEnd="16dp"app:boxStrokeColor="@color/black"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/toolbar"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/et_data"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Mac Address"android:lines="1"android:singleLine="true" /></com.google.android.material.textfield.TextInputLayout><CheckBoxandroid:id="@+id/cb_format_check"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Mac地址格式检查"app:layout_constraintStart_toStartOf="@+id/data_layout"app:layout_constraintTop_toBottomOf="@+id/data_layout" /><Buttonandroid:id="@+id/btn_negative"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="18dp"android:layout_weight="1"android:text="取消"app:layout_constraintEnd_toStartOf="@+id/btn_positive"app:layout_constraintTop_toTopOf="@+id/btn_positive" /><Buttonandroid:id="@+id/btn_positive"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="16dp"android:layout_weight="1"android:text="确定"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/data_layout"app:layout_constraintTop_toBottomOf="@+id/cb_format_check" /></androidx.constraintlayout.widget.ConstraintLayout>
这个布局中有一个检查Mac地址正确性的复选框,同样我们需要在BleUtils中增加一个函数,代码如下所示:
fun isValidMac(macStr: String) = Regex("([A-Fa-f0-9]{2}[:]){5}[A-Fa-f0-9]{2}").matches(macStr)
下面我们回到ScanActivity中,写一个showSettingMacDialog()
函数,代码如下所示:
private fun showSettingMacDialog() {val dialog = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)val macBinding = DialogSettingMacBinding.inflate(layoutInflater)macBinding.btnPositive.setOnClickListener {val inputData = macBinding.etData.text.toString()if (inputData.isEmpty()) {macBinding.dataLayout.error = "请输入Mac地址"return@setOnClickListener}if (macBinding.cbFormatCheck.isChecked) {if (!BleUtils.isValidMac(inputData)) {macBinding.dataLayout.error = "请输入正确的Mac地址"return@setOnClickListener}}if (bleCore.isScanning()) stopScan()MVUtils.put(FILTER_MAC_VALUE, inputData)MVUtils.put(FILTER_MAC_FLAG, true)mMenu.findItem(R.id.item_filter_mac).isChecked = trueshowMsg("过滤Mac地址")if (!bleCore.isScanning()) startScan()dialog.dismiss()}macBinding.btnNegative.setOnClickListener {dialog.dismiss()}dialog.setContentView(macBinding.root)dialog.show()}
弹窗中点击确定按钮就会先检查一遍,然后就会保存Mac地址,再保存过滤标识,然后我们修改一下过滤Mac地址Item的点击事件,代码如下所示:
R.id.item_filter_mac -> { // 过滤Mac地址if (MVUtils.getBoolean(FILTER_MAC_FLAG)) {mMenu.findItem(R.id.item_filter_mac).isChecked = falseMVUtils.put(FILTER_MAC_FLAG, false)MVUtils.put(FILTER_MAC_VALUE, "")showMsg("不过滤设备地址")} else {showSettingMacDialog()}}
首先判断是否过滤,有的话就不再过滤,没有的话就显示输入Mac地址弹窗,如果过滤了,我们就需要在扫描回调函数中增加一个过滤的选项。
override fun onScanResult(result: ScanResult) {//过滤空设备名...//过滤Mac地址if (MVUtils.getBoolean(FILTER_MAC_FLAG)) {val filterMac: String? = MVUtils.getString(FILTER_MAC_VALUE, "")if (filterMac!!.isNotEmpty()) {if (!result.device.address.contains(filterMac)) return}}...}
过滤的位置可以放在过滤空设备名称之后或者之前都可以,最后还需要修改onCreateOptionsMenu()
函数中的代码如下所示:
override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)return true}
增加了一个对于Mac地址Item项是否选中的判断,下面我们可以运行看看,我们的过滤是否有效果。
这样过滤Mac地址就做好了,下面过滤RSSI信号强度。
五、过滤RSSI
与过滤Mac地址一样,过滤RSSI首先要做的就是设置RSSI,对此,我们同样在layout下创建一个dialog_settings_rssi.xml
作为弹窗的布局文件,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"><com.google.android.material.appbar.MaterialToolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:title="过滤RSSI" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginTop="16dp"android:text="RSSI:"android:textColor="@color/black"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/toolbar" /><androidx.appcompat.widget.AppCompatSeekBarandroid:id="@+id/sb_rssi"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:max="100"android:min="35"android:progress="100"android:progressTint="@color/orange"android:thumbTint="@color/dark_orange"app:layout_constraintBottom_toBottomOf="@+id/textView"app:layout_constraintEnd_toStartOf="@+id/tv_rssi"app:layout_constraintStart_toEndOf="@+id/textView"app:layout_constraintTop_toTopOf="@+id/textView" /><TextViewandroid:id="@+id/tv_rssi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"android:text="-100 dBm"android:textColor="@color/black"app:layout_constraintBottom_toBottomOf="@+id/textView"app:layout_constraintEnd_toEndOf="parent" /><Buttonandroid:id="@+id/btn_negative"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="18dp"android:layout_weight="1"android:text="取消"app:layout_constraintEnd_toStartOf="@+id/btn_positive"app:layout_constraintTop_toTopOf="@+id/btn_positive" /><Buttonandroid:id="@+id/btn_positive"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:layout_weight="1"android:text="确定"app:layout_constraintEnd_toEndOf="@+id/tv_rssi"app:layout_constraintTop_toBottomOf="@+id/tv_rssi" />
</androidx.constraintlayout.widget.ConstraintLayout>
然后就是在ScanActivity中增加一个showSettingRssi()
函数,代码如下所示:
private fun showSettingRssi() {val dialog = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)val rssiBinding = DialogSettingRssiBinding.inflate(layoutInflater)var progress = 100rssiBinding.sbRssi.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {@SuppressLint("SetTextI18n")override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {rssiBinding.tvRssi.text = "-$progress dBm"}override fun onStartTrackingTouch(seekBar: SeekBar) {}override fun onStopTrackingTouch(seekBar: SeekBar) {progress = seekBar.progress}})val rssi: Int = MVUtils.getInt(FILTER_RSSI_VALUE, 100)rssiBinding.sbRssi.progress = rssirssiBinding.tvRssi.text = String.format("-%s dBm", rssi)rssiBinding.btnPositive.setOnClickListener {//保存if (bleCore.isScanning()) stopScan()MVUtils.put(FILTER_RSSI_FLAG, true)//保存设置的RSSI值MVUtils.put(FILTER_RSSI_VALUE, progress)mMenu.findItem(R.id.item_filter_rssi).isChecked = truemMenu.findItem(R.id.item_filter_rssi).title = "过滤RSSI:-$progress"showMsg("过滤RSSI:-" + progress + "dBm")if (!bleCore.isScanning()) startScan()dialog.dismiss()}rssiBinding.btnNegative.setOnClickListener { dialog.dismiss() }dialog.setContentView(rssiBinding.root)dialog.show()}
在点击确定按钮的时候,保存设置的RSSI信号强度值,如果没有设置就是默认的值,然后我们修改一下过滤RSSI Item的点击事件,代码如下所示:
R.id.item_filter_rssi -> { // 过滤RSSIif (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {if (bleCore.isScanning()) stopScan()//关闭过滤RSSIMVUtils.put(FILTER_RSSI_FLAG, false)mMenu.findItem(R.id.item_filter_rssi).isChecked = falseMVUtils.put(FILTER_RSSI_VALUE, 100)showMsg("取消过滤RSSI")if (!bleCore.isScanning()) startScan()} else {showSettingRssi()}}
当前已有过滤RSSI,再次点击时就会取消过滤的信息,知道你再次设置RSSI过滤值,接下来就是扫描回调中,根据这个设置项进行一次过滤:
override fun onScanResult(result: ScanResult) {//过滤Mac地址...//过滤RSSIif (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {val rssi: Int = -MVUtils.getInt(FILTER_RSSI_VALUE, 100)if (result.rssi < rssi) {return}}...}
最后为了保存设置项,是我们再次打开App时,UI上是正确的,我们修改onCreateOptionsMenu()函数,代码如下所示:
override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_scan, menu)mMenu = menumMenu.findItem(R.id.item_filter_null).isChecked = MVUtils.getBoolean(FILTER_NULL_FLAG)mMenu.findItem(R.id.item_filter_mac).isChecked = MVUtils.getBoolean(FILTER_MAC_FLAG)mMenu.findItem(R.id.item_filter_rssi).isChecked = MVUtils.getBoolean(FILTER_RSSI_FLAG)if (MVUtils.getBoolean(FILTER_RSSI_FLAG)) {mMenu.findItem(R.id.item_filter_rssi).title ="过滤RSSI:-" + MVUtils.getInt(FILTER_RSSI_VALUE, 100)}return true}
运行一下,看看效果:
关于扫描过滤的功能就写好了,本文内容介绍。
六、源码
如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~
源码地址:GoodBle
相关文章:

Android Ble蓝牙App(七)扫描过滤
Ble蓝牙App(七)扫描过滤 前言目录正文一、增加菜单二、使用MMKV① 添加依赖② 封装MMKV③ 使用MMKV 三、过滤空设备名四、过滤Mac地址五、过滤RSSI六、源码 前言 在上一篇文章中了解了MTU的相关知识以及对于设备操作信息的展示,本篇文章中将增…...
小程序当前页面栈以及跳转
1.调用页面栈刷新接口 let pages getCurrentPages(); //当前页面栈 if (pages.length > 1) { let beforePage pages[pages.length - 2]; //获取上一个页面实例对象 beforePage.$vm.getActivityLi…...
jQuery获取表单的值val()
(1)页面中有很多元素,包括表单中的输入项,如输入文本框等;获取、设置、输入文本框的值;val()方法。 (2)也包括<p>、<span>等元素;获取、设置这些元素的文本…...

【专栏必读】数字图像处理(MATLAB+Python)专栏目录导航及学习说明
文章目录 第一章:绪论第二章:数字图像处理基础第三章:图像基本运算第四章:图像的正交变换第五章:图像增强第六章:图像平滑第七章:图像锐化第八章:图像复原第九章:图像形态…...

2023年非证券类投资银行业发展报告
第一章 行业概况 非证券投资银行业是一个专门为公司、政府和高净值个人提供金融服务的行业,与传统的证券投资银行不同,其主要业务不涉及证券交易,而是注重为客户提供咨询服务、融资和投资管理等服务。 非证券投资银行通常涉及的业务领域包括…...

Matlab 如何把频谱图的纵坐标设置为分贝刻度
Matlab 如何把频谱图的纵坐标设置为分贝刻度 Matlab代码如下: % 如何把频谱图的纵坐标设置为分贝刻度 % % pr2_2_6 clc; clear; close all;load pr2_2_6_sndata1.mat % 读入数据 X fft(y); % FFT n2 1:L/21; % 计算正频率…...
VUE写后台管理(2)
VUE写后台管理(2) 1.环境2.Element界面3.Vue-Router路由后台1.左导航栏2.上面导航条 1.环境 1.下载管理node版本的工具nvm(Node Version Manager) 2.安装node(vue工程的环境管理工具):nvm install 16.13.0 3.安装vue工…...
RHCSA8.2
Node1 配置您的系统以使用默认存储库 配置您 的系统以使用默认存储库YUM 存储库已可以从 http://foundation0.ilt.example.com/dvd/BaseOS 和 http://foundation0.ilt.example.com/dvd/AppStream 使用配置您的系统,以将这些位置用作默认存储库[rootclear ~]# cat …...

修改linux中tomcat的端口
随便修改一个 以8055为例子 开放8081端口 firewall-cmd --permanent --add-port8081/tcp firewall-cmd --reload firewall-cmd --list-all...

学妹学Java(一)
⭐简单说两句⭐ 作者:后端小知识 CSDN个人主页:后端小知识 🔎GZH:后端小知识 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 Hello,亲爱的各位友友们,好久不见࿰…...

湖南省副省长秦国文一行调研考察亚信科技
9月5日,湖南省人民政府党组成员、副省长秦国文一行到亚信科技调研考察,亚信科技高级副总裁陈武主持接待。 图:双方合影 在亚信科技创新展示中心,秦国文了解了亚信科技在5G、算力网络、人工智能、大数据等前沿领域的创新探索&…...

k8s部署redis 3主3从
k8s部署redis6节点,组成3主3从集群模式 一般来说,redis部署有三种模式。 单实例模式,一般用于测试环境。 哨兵模式 集群模式后两者用于生产部署 哨兵模式 在redis3.0以前,要实现集群一般是借助哨兵sentinel工具来监控master节点…...

Vue2安装vuex和vue-router报错处理
Vue2安装vuex和vue-router报错处理 Vue2.6安装VuexVue2.6安装vue-router Vue2.6安装Vuex 报错信息 处理方法 #查看vuex版本 npm view vuex versions --json #安装合适版本 npm install vuex3.6.2 --saveVue2.6安装vue-router 报错信息 处理方法 #查看vue-router版本 npm…...

算法leetcode|79. 单词搜索(rust重拳出击)
文章目录 79. 单词搜索:样例 1:样例 2:样例 3:提示:进阶: 分析:题解:rust:go:c:python:java: 79. 单词搜索: …...

2023年高教社杯全国大学生数学建模竞赛参赛事项注意
MathClub数模资源,含专属思路 资源链接:点击这里获取众多数模资料、思路精讲、论文模板latex和word、学习书籍等 2023高教社杯数学建模国赛–赛前准备 一年一度的数学建模国赛要来啦!!!小编仔细阅读了比赛官方网站上…...

数学建模--逻辑回归算法的Python实现
首先感谢CSDN上发布吴恩达的机器学习逻辑回归算法任务的各位大佬. 通过大佬的讲解和代码才勉强学会. 这篇文章也就是简单记录一下过程和代码. CSDN上写有关这类文章的大佬有很多,大家都可以多看一看学习学习. 机器学习方面主要还是过程和方法. 这篇文章只完成了线性可分方面的任…...

Qt6_贪吃蛇Greedy Snake
贪吃蛇Greedy Snake 1分析 首先这是一个贪吃蛇界面,由一个长方形边框和一只贪吃蛇组成 默认开局时,贪吃蛇身体只有3个小方块,使用画笔画出 1.1如何移动 对于蛇的移动,有2种方法 在一定时间范围内(定时器),未对游戏…...

Credo推出业界首款单片集成CMOS VCSEL驱动器的800G光DSP芯片
针对AOC及短距(SR)光模块优化的新型Credo DSP,适用于下一代超大规模数据中心/AI应用 加州圣何塞和中国深圳,2023年9月6日——Credo Technology(纳斯达克股票代码:CRDO)今日发布两款新品&#x…...

【经验分享】如何使用VSCode对比两个文件
问题: 当有两个不同版本的文件,如何使用VSCode对比两个文件 解决办法 长按ctrl选择想要对比的两个文件-----右键选择将已选项进行比较----大功告成 大功告成...
从裸机开始安装ubuntu系统到安装NVIDIA驱动
这篇文章为总结类文章,更多的是把各个博主的内容总结一下,形成一套端到端的方法,主要内容包括: 安装ubuntu22.04版本(含启动U盘制作)配置ssh、固定ip和端口号安装NVIDIA驱动安装cuda11.7和cudnn8.6 文章目录 一、安装ubuntu22.041…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...