Android Ble蓝牙App(二)连接与发现服务

Ble蓝牙App(二)连接与发现服务

  • 前言
  • 正文
    • 一、GATT回调
    • 二、连接和断连
    • 三、连接状态回调
    • 四、发现服务
    • 五、服务适配器
    • 六、显示服务
    • 七、源码

前言

  在上一篇中我们进行扫描设备的处理,本文中进行连接和发现服务的数据处理,运行效果图如下所示:
在这里插入图片描述

正文

  现在我们从MainActivity进入到ScanActivity,选中一个设备返回到MainActivity,下面要对选中的设备进行处理,首先我们来做连接。

一、GATT回调

  在之前我们写了一个BleCore,这里面是对扫描的封装,那么对于连接来说我们同样可以封装到这里,我们可以在BleCore中写一个BleGattCallback 类,代码如下所示:

class BleGattCallback : BluetoothGattCallback() {/*** 连接状态改变*/override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {}/*** 发现服务*/override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {}}

  因为本文要做的事情是连接和发现服务,所以我们就先重写这两个函数,注意一点的是,蓝牙的操作都是在子线程中进行的,如果我们需要知道当前是否连接,则需要写一个接口用于回调到Activity中,在ble包下新建一个BleCallback接口,代码如下所示:

interface BleCallback {/*** 设备的所有信息*/fun deviceInfo(info: String)/*** 连接状态* @param state true or false*/fun onConnectionStateChange(state: Boolean)/*** 发现服务*/fun onServicesDiscovered(services: List<BluetoothGattService>)
}

  接口中定义了三个函数,通过注释我们清晰的知道都是什么作用,这里着重介绍第一个函数,这个函数会显示设备各个时候的状态信息,从连接之后的所有动作,如果我们需要保存设备的操作日志的话,可以通过这个来进行处理保存。

然后回到BleCore,在companion object中声明变量和设置接口回调的函数:

    @SuppressLint("StaticFieldLeak")companion object {...private var mGatt: BluetoothGatt? = nullprivate var mBleCallback: BleCallback? = nullprivate lateinit var mBleGattCallback: BleGattCallback/*** 是否连接*/private var mIsConnected = false/*** 设备信息*/private fun deviceInfo(info: String) = mBleCallback?.deviceInfo(info)/*** 连接状态*/private fun connectState(state: Boolean) {mIsConnected = statemBleCallback?.onConnectionStateChange(state)}}

同时在 companion object外创建一个函数,代码如下所示:

    fun setBleCallback(bleCallback: BleCallback) {mBleCallback = bleCallback}

此函数和setPhyScanCallback()函数是同级的,下面我们增加连接和断连的函数。

二、连接和断连

在BleCore中增加如下代码:

	/*** 连接蓝牙设备*/fun connect(device: BluetoothDevice) {deviceInfo("连接中...")mGatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {device.connectGatt(context, false, mBleGattCallback, BluetoothDevice.TRANSPORT_LE, BluetoothDevice.PHY_LE_2M_MASK)} else {device.connectGatt(context, false, mBleGattCallback)}}/*** 断开连接*/fun disconnect() {deviceInfo("断开连接...")mGatt?.disconnect()}

连接与断开连接,调用时会触发onConnectionStateChange()函数。

三、连接状态回调

下面修改这个函数的代码,如下所示:

        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {val address = gatt.device.addresswhen (newState) {BluetoothProfile.STATE_CONNECTED -> {deviceInfo("已连接:$address")connectState(true)}BluetoothProfile.STATE_DISCONNECTED -> {deviceInfo("已断开连接:$address")connectState(false)}else -> {Log.d(TAG, "onConnectionStateChange: $status")connectState(false)mGatt?.close()mGatt = null}}}

在回调中,连接成功和断开连接都会有一个对应的状态码,通过状态回调到接口函数中,然后回到MainActivity中使用一下这个回调,首先我们修改一下activity_main.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"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.google.android.material.appbar.MaterialToolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="@color/orange"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:navigationIcon="@drawable/ic_scan_ble"app:title="GoodBle"app:titleCentered="true"app:titleTextColor="@color/white"><TextViewandroid:id="@+id/tv_disconnect"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="end"android:layout_marginEnd="8dp"android:visibility="gone"android:padding="8dp"android:text="断开连接"android:textColor="@color/white" /></com.google.android.material.appbar.MaterialToolbar><TextViewandroid:id="@+id/tv_device_info"android:layout_width="0dp"android:layout_height="wrap_content"android:padding="16dp"android:text="设备信息"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/toolbar" /></androidx.constraintlayout.widget.ConstraintLayout>

在XML中只增加了两个TextView,分别用于断连和显示设备状态,然后我们修改MainActivity中的代码,如下所示:

class MainActivity : BaseActivity(), BleCallback {private val binding by viewBinding(ActivityMainBinding::inflate)private lateinit var bleCore: BleCore@SuppressLint("MissingPermission")private val scanIntent =registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->if (result.resultCode == Activity.RESULT_OK) {if (result.data == null) return@registerForActivityResult//获取选中的设备val device = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {result.data!!.getParcelableExtra("device", BluetoothDevice::class.java)} else {result.data!!.getParcelableExtra("device") as BluetoothDevice?}//连接设备if (device != null) bleCore.connect(device)}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)bleCore = (application as BleApp).getBleCore()bleCore.setBleCallback(this@MainActivity)//进入扫描页面binding.toolbar.setNavigationOnClickListener { scanIntent.launch(Intent(this,ScanActivity::class.java)) }//断开连接binding.tvDisconnect.setOnClickListener {binding.tvDisconnect.visibility = View.GONEbleCore.disconnect()}}override fun deviceInfo(info: String) {runOnUiThread {binding.tvDeviceInfo.text = info}}override fun onConnectionStateChange(state: Boolean) {runOnUiThread {if (state) binding.tvDisconnect.visibility = View.VISIBLE}}override fun onServicesDiscovered(services: List<BluetoothGattService>) {}
}

  这里我们首先是通过Activity Result API的StartActivityForResult()函数进行页面跳转,在返回的时候拿到device对象,这在前一篇已经写好了,拿到device对象之后调用BleCoreconnect()函数进行连接设备,在onCreate()函数中进行BleCore的赋值,然后设置Ble的回调,实现BleCallback接口,重写里面的函数,当连接成功之后会通过回调deviceInfo()得到设备状态,因为是子线程所以在ui线程中渲染UI。而onConnectionStateChange()函数,回调连接成功或者失败,如果成功则为ture,就显示tvDisconnect控件,此时连接成功,点击这个tvDisconnect就会断开连接,点击监听就在onCreate()中写好了,下面我们运行一下看看效果。

在这里插入图片描述

从这个效果图来看,我们连接成功之后有状态,点击断开连接也会有状态改变,那么连接就写好了。

四、发现服务

  连接写好了,下面可以写发现服务了,我们可以在连接成功的处理中进行发现服务,下面我们修改一下BleGattCallback中的onConnectionStateChange()函数中的代码,如下图所示:

在这里插入图片描述

通过gatt.discoverServices()进行发现服务的动作,在此之前通过deviceInfo设置当前的动作状态,发现服务执行会触发onServicesDiscovered()回调,在这个回调中我们可以回调到页面,修改代码如下所示:

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {if (status == BluetoothGatt.GATT_SUCCESS) {deviceInfo("发现了 ${gatt.services.size} 个服务")gatt.services?.let { mBleCallback?.onServicesDiscovered(it) }}}

在回调中设置发现服务的个数,然后回调,因为服务是多个的,那么下面我们就需要使用一个列表是装载服务,首先我们修改一下activity_main.xml,在里面增加一个RecyclerView,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout...>...<androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/white"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_device_info" /></androidx.constraintlayout.widget.ConstraintLayout>

五、服务适配器

  要显示服务列表数据,首先需要一个适配器,而适配器又需要一个item去渲染数据,下面我们在layout下创建一个item_service.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:id="@+id/item_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="2dp"android:background="@color/white"android:orientation="vertical"><TextViewandroid:id="@+id/tv_service_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginTop="8dp"android:text="服务"android:textColor="@color/black"android:textSize="16sp"android:textStyle="bold"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_uuid_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID:"app:layout_constraintStart_toStartOf="@+id/tv_service_name"app:layout_constraintTop_toBottomOf="@+id/tv_service_name" /><TextViewandroid:id="@+id/tv_service_uuid"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID"android:textColor="@color/black"app:layout_constraintBottom_toBottomOf="@+id/tv_uuid_title"app:layout_constraintStart_toEndOf="@+id/tv_uuid_title"app:layout_constraintTop_toTopOf="@+id/tv_uuid_title" /><TextViewandroid:id="@+id/tv_service_info"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:text="PRIMARY SERVICE"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="@+id/tv_service_name"app:layout_constraintTop_toBottomOf="@+id/tv_uuid_title" /></androidx.constraintlayout.widget.ConstraintLayout>

下面我们在ble包下新建一个BleUtils类,代码如下所示:

object BleUtils {private val generic = "-0000-1000-8000-00805F9B34FB"/*** 获取蓝牙服务名称* @param uuid UUID*/fun getServiceName(uuid: UUID) =when ("0x${uuid.toString().substring(4, 8).uppercase(Locale.getDefault())}") {"0x1800" -> "Generic Access service""0x1801" -> "Generic Attribute service""0x1802" -> "Immediate Alert service""0x1803" -> "Link Loss service""0x1804" -> "Tx Power service""0x1805" -> "Current Time service""0x1806" -> "Reference Time Update service""0x1807" -> "Next DST Change service""0x1808" -> "Glucose service""0x1809" -> "Health Thermometer service""0x180A" -> "Device Information service""0x180D" -> "Heart Rate service""0x180E" -> "Phone Alert Status service""0x180F" -> "Battery service""0x1810" -> "Blood Pressure service""0x1811" -> "Alert Notification service""0x1812" -> "Human Interface Device service""0x1813" -> "Scan Parameters service""0x1814" -> "Running Speed and Cadence service""0x1815" -> "Automation IO service""0x1816" -> "Cycling Speed and Cadence service""0x1818" -> "Cycling Power service""0x1819" -> "Location and Navigation service""0x181A" -> "Environmental Sensing service""0x181B" -> "Body Composition service""0x181C" -> "User Data service""0x181D" -> "Weight Scale service""0x181E" -> "Bond Management service""0x181F" -> "Continuous Glucose Monitoring service""0x1820" -> "Internet Protocol Support service""0x1821" -> "Indoor Positioning service""0x1822" -> "Pulse Oximeter service""0x1823" -> "HTTP Proxy service""0x1824" -> "Transport Discovery service""0x1825" -> "Object Transfer service""0x1826" -> "Fitness Machine service""0x1827" -> "Mesh Provisioning service""0x1828" -> "Mesh Proxy service""0x1829" -> "Reconnection Configuration service""0x183A" -> "Insulin Delivery service""0x183B" -> "Binary Sensor service""0x183C" -> "Emergency Configuration service""0x183D" -> "Authorization Control service""0x183E" -> "Physical Activity Monitor service""0x183F" -> "Elapsed Time service""0x1840" -> "Generic Health Sensor service""0x1843" -> "Audio Input Control service""0x1844" -> "Volume Control service""0x1845" -> "Volume Offset Control service""0x1846" -> "Coordinated Set Identification service""0x1847" -> "Device Time service""0x1848" -> "Media Control service""0x1849" -> "Generic Media Control service""0x184A" -> "Constant Tone Extension service""0x184B" -> "Telephone Bearer service""0x184C" -> "Generic Telephone Bearer service""0x184D" -> "Microphone Control service""0x184E" -> "Audio Stream Control service""0x184F" -> "Broadcast Audio Scan service""0x1850" -> " Published Audio Capabilities service""0x1851" -> "Basic Audio Announcement service""0x1852" -> "Broadcast Audio Announcement service""0x1853" -> "Common Audio service""0x1854" -> "Hearing Access service""0x1855" -> "Telephony and Media Audio service""0x1856" -> "Public Broadcast Announcement service""0x1857" -> "Electronic Shelf Label service"else -> "Unknown Service"}fun getServiceUUID(uuid: UUID) ="0x${uuid.toString().substring(4, 8).uppercase(Locale.getDefault())}"
}

  这里需要说明一下蓝牙的UUID,蓝牙UUID(Universally Unique Identifier)是用于唯一标识蓝牙设备和服务的一种标识符。它是一个128位长的数字,在蓝牙通信中起到唯一标识的作用。蓝牙UUID按照标准分为两种类型:

  1. 16位UUID:这些UUID通常用于蓝牙标准定义的一些通用服务和特性。例如,设备名称服务的UUID是 00001800-0000-1000-8000-00805F9B34FB。

  2. 128位UUID:这些UUID通常用于自定义的服务和特性,以确保全球唯一性。可以自行生成一个128位的UUID作为自定义的服务或特性标识。例如,一个自定义的服务UUID可以是 0000XXXX-0000-1000-8000-00805F9B34FB,其中的 XXXX 部分可以是任意的16进制数字。

在蓝牙通信中,设备使用UUID来发布和查找服务以及识别特性。UUID是蓝牙设备之间进行通信时的重要标识,确保了设备和服务的唯一性。

那么getServiceName()中的键你就知道是什么意思了,0x1800就是16进制数字,而对应的值则是SIG定义的,可以参考这个文档:Assigned_Numbers.pdf。如果你的值找不到对应的,那说明它不是SIG规范的,你这个服务UUID就是自己公司自定义的。

下面我们写适配器,在adapter包下新建一个ServiceAdapter类,代码如下所示:

class ServiceAdapter(private val services: List<BluetoothGattService>
) : RecyclerView.Adapter<ServiceAdapter.ViewHolder>() {private var mOnItemClickListener: OnItemClickListener? = nullfun setOnItemClickListener(mOnItemClickListener: OnItemClickListener?) {this.mOnItemClickListener = mOnItemClickListener}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val viewHolder = ViewHolder(ItemServiceBinding.inflate(LayoutInflater.from(parent.context), parent, false))viewHolder.binding.itemService.setOnClickListener { mOnItemClickListener?.onItemClick(it, viewHolder.adapterPosition) }return viewHolder}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.binding.tvServiceName.text = BleUtils.getServiceName(services[position].uuid)holder.binding.tvServiceUuid.text = BleUtils.getServiceUUID(services[position].uuid)}override fun getItemCount() = services.sizeclass ViewHolder(itemView: ItemServiceBinding) : RecyclerView.ViewHolder(itemView.root) {var binding: ItemServiceBindinginit {binding = itemView}}
}

这里的代码就是比较简单的,就是基本的写法,下面回到MainActivity中进行显示数据。

六、显示服务

首先声明变量:

    private var mServiceAdapter: ServiceAdapter? = nullprivate val mServiceList: MutableList<BluetoothGattService> = mutableListOf()

然后实现OnItemClickListener 接口

class MainActivity : BaseActivity(), BleCallback, OnItemClickListener {

重写onItemClick()函数。

    override fun onItemClick(view: View?, position: Int) {showMsg(mServiceList[position].uuid.toString())}

修改onServicesDiscovered()函数,代码如下所示:

    override fun onServicesDiscovered(services: List<BluetoothGattService>) {runOnUiThread {mServiceList.clear()mServiceList.addAll(services)mServiceAdapter ?: run {mServiceAdapter = ServiceAdapter(mServiceList)binding.rvService.apply {(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = falselayoutManager = LinearLayoutManager(this@MainActivity)adapter = mServiceAdapter}mServiceAdapter!!.setOnItemClickListener(this@MainActivity)mServiceAdapter}mServiceAdapter!!.notifyDataSetChanged()}}

这里的写法其实和扫描设备哪里如出一辙,下面我们运行一下看看,什么效果。
在这里插入图片描述

七、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:GoodBle

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SystemVerilog scheduler

文章目录 简介调度器simulation regionPreponed regionActive regionInactive regionNBA(Non-blocking Assignment Events region)Observed regionReactive regionRe-Inactive Events regionRe-NBA RegionPostponed Region PLI region:Pre-active regionPre-NBA regionPost-NBA…

JMeter 4.x 简单使用

文章目录 前言JMeter 4.x 简单使用1. 启动2. 设置成中文3. 接口测试3.1. 设置线程组3.2. HTTP信息请求头管理器3.3. 添加HTTP请求默认值3.4. 添加HTTP cookie 管理3.5. 添加http请求3.5.1. 添加断言 3.6. 添加监听器-查看结果树3.7. 添加监听器-聚合报告 4. 测试 前言 如果您觉…

探索产品项目管理软件的种类及功能

随着科技的不断发展&#xff0c;越来越多的企业开始重视产品项目管理的重要性。产品项目管理软件作为一种有效的工具&#xff0c;可以帮助企业更好地规划、执行和控制项目&#xff0c;提高项目的成功率。本文将分为两部分&#xff0c;分别介绍产品项目管理软件的功能以及一些知…

java实现钉钉群机器人@机器人获取信息后,机器人回复(机器人接收消息)

1.需求 鉴于需要使用钉钉群机器人回复&#xff0c;人们提出的问题&#xff0c;需要识别提出的问题中的关键词&#xff0c;后端进行处理实现对应的业务逻辑 2.实现方式 用户群机器人&#xff0c;附带提出的问题&#xff0c;后端接收消息后识别消息内容&#xff0c;读取到关键…

深度学习,神经网络介绍

目录 1.神经网络的整体构架 2.神经网络架构细节 3.正则化与激活函数 4.神经网络过拟合解决方法 1.神经网络的整体构架 ConvNetJS demo: Classify toy 2D data 我们可以看看这个神经网络的网站&#xff0c;可以用来学习。 神经网络的整体构架如下1&#xff1a; 感知器&…

完全背包(从二维到一维)

图片来源活动 - AcWing 有 N件物品和一个容量为 V 的背包&#xff0c;每件物品有各自的价值且能被选择无数次&#xff0c;要求在有限的背包容量下&#xff0c;装入的物品总价值最大。 一&#xff0c;暴力解法&#xff08;容易超时&#xff09; #include<iostream> usi…

Java课题笔记~ 关联映射

一、MyBatis关联查询 在关系型数据库中&#xff0c;表与表之间存在着3种关联映射关系&#xff0c;分别为一对一、一对多、多对多。 一对一&#xff1a;一个数据表中的一条记录最多可以与另一个数据表中的一条记录相关。列如学生与学号就属于一对一关系。 一对多&#xff1a;主…

Intellij IDEA运行报Command line is too long的解决办法

想哭&#xff0c;vue前端运行起来&#xff0c;对应的后端也得起服务。 后端出的这个bug&#xff0c;下面的博客写的第二种方法&#xff0c;完整截图是下面这个。 ​​​​​​​​​​​​​​​​​​​​Intellij IDEA运行报Command line is too long的解决办法 - 知乎 (zh…

奥威BI系统|秒分析,更适合分析大数据

根据以往的经验&#xff0c;当数据量多到一定程度就容易导致系统卡顿、崩溃。这种现象给企业级数据分析造成了极大的困扰。随着业务发展扩大和分析需求精细化&#xff0c;企业需要一套能秒分析大数据的系统。而奥威BI系统就是这样一款可以秒分析大数据的商业智能系统。 奥威BI…

二十三种设计模式第二十三篇--状态模式

状态模式&#xff0c;是一种行为模式&#xff0c;在软件开发过程中&#xff0c;对象按照不同的情况做出不同的行为&#xff0c;我们把这样的对象称为具有状态的对象&#xff0c;而把影响对象行为的一个或者多个动态变化的属性称为状态。 对这种具有状态的对象变成&#xff0c;…

其他时区的时间转换成当前时区的时间

例子&#xff1a;项目获取到的时间在东二区&#xff0c;用户在东八区&#xff0c;那么要把东二区的时间转换成东八区的时间 时区可在pc上设置 //转换当前时区的时间 兼容ios、时间戳 export function convertTureTime(time){let nDate new Date();//当前时间let y nDate.ge…

用html+javascript打造公文一键排版系统14:为半角和全角字符相互转换功能增加英文字母、阿拉伯数字、标点符号、空格选项

一、实际工作中需要对转换选项细化内容 在昨天我们实现了最简单的半角字符和全角字符相互转换功能&#xff0c;就是将英文字母、阿拉伯数字、标点符号、空格全部进行转换。 在实际工作中&#xff0c;我们有时只想英文字母、阿拉伯数字、标点符号、空格之中的一两类进行转换&a…

C# 控制台彩色深度打印 工具类

文章目录 前言Nuget 环境安装代码使用打印结果 总结 前言 有时候我们想要靠打印获得程序信息&#xff0c;因为Dubeg模式需要一点一点断点进入进出&#xff0c;但是我们觉得断点运行实在是太慢了&#xff0c;还是直接打印后找结果会好一点。 Nuget 环境安装 想自己写的话可以看…

招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计 tbms

​功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

OpenCV图像处理技巧之空间滤波

1. 引言 再次问好&#xff0c;图像处理爱好者们&#xff01;&#x1f31f; 在前面的章节中&#xff0c;我们学习了图像处理的基础知识&#xff0c;并展现了图像增强的魅力。在这一节中&#xff0c;我们将更深入地研究空间滤波技术。 闲话少说&#xff0c;我们直接开始吧&#…

跟CZY一起深入理解C++(1)-一些基础知识

跟CZY一起深入理解C些基础知识 常量constconstexpr 初始化枚举与枚举类分离编译 常量 const 常量亦即不可改变的量(实际上可以暴力破解),那么常量在C中主要有以下几种应用场景 定义常量变量 //如果有以下情况,在GCC上能够破解,而在MSVC上不会改变 // int放在栈区,实际上是可…

Leetcode-每日一题【剑指 Offer 06. 从尾到头打印链表】

题目 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,3,2]输出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 链表长度 < 10000 解题思路 1.题目要求我们从尾到头反过…

【数据结构】排序算法系列

常见的排序如下&#xff1a; 一、比较类排序 1. 交换排序 &#xff08;1&#xff09; 冒泡排序 【数据结构】交换排序&#xff08;一&#xff09;——冒泡排序_Jacky_Feng的博客-CSDN博客 &#xff08;2&#xff09; 快速排序 【数据结构】交换排序&#xff08;二&#xf…

IDEA中修改类头的文档注释信息

IDEA中修改类头的文档注释信息 选择File--Settings--Editor--File and Code Templates--Includes&#xff0c;可以把文档注释写成这种的 /**author: Arbicoralcreate: ${YEAR}-${MONTH}-${DAY} ${TIME}Description: */这样回看就可以很清楚的看到自己创建脚本的时间&#xff…

FFmpeg解码详细流程

介绍 FFmpeg的 libavcodec 模块完成音视频多媒体的编解码模块。老版本的 FFmpeg 将avcodec_decode_video2()作为视频的解码函数 API&#xff0c;将avcodec_decode_audio4()作为音频的解码函数 API&#xff1b;从 3.4版本开始已经将二者标记为废弃过时 API&#xff08;attribut…