Android显示系统(13)- 向SurfaceFlinger提交Buffer
Android显示系统(01)- 架构分析
Android显示系统(02)- OpenGL ES - 概述
Android显示系统(03)- OpenGL ES - GLSurfaceView的使用
Android显示系统(04)- OpenGL ES - Shader绘制三角形
Android显示系统(05)- OpenGL ES - Shader绘制三角形(使用glsl文件)
Android显示系统(06)- OpenGL ES - VBO和EBO和VAO
Android显示系统(07)- OpenGL ES - 纹理Texture
Android显示系统(08)- OpenGL ES - 图片拉伸
Android显示系统(09)- SurfaceFlinger的使用
Android显示系统(10)- SurfaceFlinger内部结构
Android显示系统(11)- 向SurfaceFlinger申请Surface
Android显示系统(12)- 向SurfaceFlinger申请Buffer
Android显示系统(13)- 向SurfaceFlinger提交Buffer
一、前言:
前面获取了Surface,并且为Surface申请了Buffer,然后通过EGL的往这Buffer里头渲染数据,完成之后,我们就需要去提交这个Buffer。
二、回顾流程图:
- 首先,SF会创建一个Client代表着App;
- App调用
createSurface
得到一个SurfaceControl
,同时,SF会创建一个Layer,代表APP端的SurfaceControl
; - 同时,SF端的Layer会有生产者和消费者,两者都有成员变量
mCore
(代表BufferQueueCore
)和mSlots
数组,而App端会有一个生产者代理,代表SF那边的Layer中的生产者。(中间通过binder通信) - App端的
SurfaceControl
可以通过getSurface
获得Surface,Surface当中也有mSlots和生产者代理。 - App端想要往Surface里面填充数据,首先得通过lock申请buffer,通过dequeueBuffer返回一个buffer,如果SF侧分配的buffer已经用完了,就通过
Gralloc
模块向匿名内存Ashmem
申请一个buffer,并且填入自己的mSlots
当中,同时返回给APP,需要重新分配Buffer,APP侧会重新调用requestBuffer
让SF侧重新关联这个Buffer,然后进行mmap,并且将fd返回给APP; - App收到binder过来的fd之后(其实binder转换成fd’了),进行
mmap(fd')
得到虚拟地址vaddr
,然后填入自己的mSlots
当中; - 最后App填充数据到vaddr当中之后,通过
unlockAndPost
提交给SF。
三、unlockAndPost:
3.1、总体思路:
- Surface->lock被调用获取Buffer之后,生产者的dequeueBuffer调用。
- 获得buffer之后,App侧通过Surface->unlockAndPost提交填充数据的Buffer,也就是调用queueBuffer。
- 那么queueBuffer调用之后,主要做两件事入队列,并且通知消费者取数据,通知顺序是:
- 通知Layer的消费者;
- 通知SurfaceFlinger;
3.2、代码走读:
入口在这儿:
status_t Surface::unlockAndPost()
{if (mLockedBuffer == nullptr) {ALOGE("Surface::unlockAndPost failed, no locked buffer");return INVALID_OPERATION;}int fd = -1;// 解锁当前被锁的缓冲区(异步)status_t err = mLockedBuffer->unlockAsync(&fd);ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);// 提交缓冲区err = queueBuffer(mLockedBuffer.get(), fd);ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",mLockedBuffer->handle, strerror(-err));mPostedBuffer = mLockedBuffer;mLockedBuffer = nullptr;return err;
}
3.3、Layer的消费者:
1)为Layer创建消费者:
看代码之前,我们先看下这个Layer第一次被引用时候:
void BufferQueueLayer::onFirstRef() {BufferLayer::onFirstRef();// Creates a custom BufferQueue for SurfaceFlingerConsumer to usesp<IGraphicBufferProducer> producer;sp<IGraphicBufferConsumer> consumer;// 创建BufferQueue的时候,里面会创建生产者和消费者BufferQueue::createBufferQueue(&producer, &consumer, true);// 将消费者做一次封装mProducer = new MonitoredProducer(producer, mFlinger, this);{// Grab the SF state lock during this since it's the only safe way to access RenderEngine// 同样,将生产者做一次封装Mutex::Autolock lock(mFlinger->mStateLock);mConsumer =new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);}// 。。。
}
看在这里面创建了生产者和消费者。我们看下这个消费者BufferLayerConsumer
:
BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,renderengine::RenderEngine& engine, uint32_t tex,Layer* layer): ConsumerBase(bq, false), // 调用了父类构造函数//。。。mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {// 。。。
}
发现构造函数中首先调用了父类构造函数:
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :mAbandoned(false),mConsumer(bufferQueue),mPrevFinalReleaseFence(Fence::NO_FENCE) {// Choose a name using the PID and a process-unique ID.mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());// Note that we can't create an sp<...>(this) in a ctor that will not keep a// reference once the ctor ends, as that would cause the refcount of 'this'// dropping to 0 at the end of the ctor. Since all we need is a wp<...>// that's what we create.wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);status_t err = mConsumer->consumerConnect(proxy, controlledByApp);if (err != NO_ERROR) {CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",strerror(-err), err);} else {mConsumer->setConsumerName(mName);}
}
构造函数主要完成以下工作:
- 接受一个指向
IGraphicBufferConsumer
类型的缓冲队列接口和一个控制标志(是否由应用程序控制)。 - 初始化成员变量,例如放弃状态、消费者句柄等。
- 创建并绑定
ConsumerListener
,监听缓冲区的操作事件。 - 通过
BufferQueue
的consumerConnect
方法,将当前消费端与缓冲队列连接。 - 设置消费端名称,以便开发时调试和跟踪。
进去看看consumerConnect:
// 代码路径:native\libs\gui\include\gui\BufferQueueConsumer.hvirtual status_t consumerConnect(const sp<IConsumerListener>& consumer,bool controlledByApp) {return connect(consumer, controlledByApp);}
调用这个connect就将consumer保存到Layer当中了:
status_t BufferQueueConsumer::connect(const sp<IConsumerListener>& consumerListener, bool controlledByApp) {ATRACE_CALL();if (consumerListener == nullptr) {BQ_LOGE("connect: consumerListener may not be NULL");return BAD_VALUE;}BQ_LOGV("connect: controlledByApp=%s",controlledByApp ? "true" : "false");std::lock_guard<std::mutex> lock(mCore->mMutex);if (mCore->mIsAbandoned) {BQ_LOGE("connect: BufferQueue has been abandoned");return NO_INIT;}// 将Consumer赋值给mCoremCore->mConsumerListener = consumerListener;mCore->mConsumerControlledByApp = controlledByApp;return NO_ERROR;
}
其中sp<BufferQueueCore> mCore;
2)给Consumer设置监听者:
void BufferQueueLayer::onFirstRef() {// ...// 给Consumer设置一个ListenermConsumer->setContentsChangedListener(this);mConsumer->setName(mName);// ...
}
稍微进去看看:
void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {setFrameAvailableListener(listener);Mutex::Autolock lock(mMutex);mContentsChangedListener = listener;
}
这个会走到父类:
void ConsumerBase::setFrameAvailableListener(const wp<FrameAvailableListener>& listener) {CB_LOGV("setFrameAvailableListener");Mutex::Autolock lock(mFrameAvailableMutex);mFrameAvailableListener = listener;
}
发现Listener最终保存到父类的成员变量mFrameAvailableListener
以及BufferLayerConsumer
的mContentsChangedListener
,以后Consumer有变化,就通过这个通知给监听者。
3.3、queueBuffer
入口函数:
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {// ...nsecs_t now = systemTime();// 调用生产者代理的queueBuffer,导致Binder调用status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);mLastQueueDuration = systemTime() - now;if (err != OK) {ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);}// ...
}
调用代理对象的函数:
然后就是调用了QueueBuffer的生产者代理提交buffer。中间一堆binder调用我们省略了,直接看子类BufferQueueProducer
。
status_t BufferQueueProducer::queueBuffer(int slot,const QueueBufferInput &input, QueueBufferOutput *output) { BufferItem item;// 。。。// 取出要提交的那一项,用取出的项构造一个BufferItem对象;item.mAcquireCalled = mSlots[slot].mAcquireCalled;item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;item.mCrop = crop;item.mTransform = transform &~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);item.mTransformToDisplayInverse =(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;item.mScalingMode = static_cast<uint32_t>(scalingMode);item.mTimestamp = requestedPresentTimestamp;item.mIsAutoTimestamp = isAutoTimestamp;item.mDataSpace = dataSpace;item.mHdrMetadata = hdrMetadata;item.mFrameNumber = currentFrameNumber;item.mSlot = slot;item.mFence = acquireFence;item.mFenceTime = acquireFenceTime;item.mIsDroppable = mCore->mAsyncMode ||(mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||(mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);item.mSurfaceDamage = surfaceDamage;item.mQueuedBuffer = true;item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;item.mApi = mCore->mConnectedApi;// 。。。output->bufferReplaced = false;if (mCore->mQueue.empty()) {// When the queue is empty, we can ignore mDequeueBufferCannotBlock// and simply queue this buffermCore->mQueue.push_back(item);frameAvailableListener = mCore->mConsumerListener;} else {// ...mCore->mQueue.push_back(item);frameAvailableListener = mCore->mConsumerListener;}
}
其实不管队列空不空,最终都会push_back进去;
同时,记录了一下listener,这个listener前面已经分析过了,是一个proxy,也就是BufferQueue::ProxyConsumerListener
对象。
通知观察者:
status_t BufferQueueProducer::queueBuffer(int slot,const QueueBufferInput &input, QueueBufferOutput *output) { // ...{ // scope for the lockstd::unique_lock<std::mutex> lock(mCallbackMutex);while (callbackTicket != mCurrentCallbackTicket) {mCallbackCondition.wait(lock);}// 通知监听者,有数据了if (frameAvailableListener != nullptr) {frameAvailableListener->onFrameAvailable(item);} else if (frameReplacedListener != nullptr) {frameReplacedListener->onFrameReplaced(item);}// ...}// ...
}
就是通知监听者有数据了,这个监听者是谁呢?上面我们说了,是Queue的消费者,以及SurfaceFlinger。所以,我们到了消费者的代理类:
void BufferQueue::ProxyConsumerListener::onFrameAvailable(const BufferItem& item) {sp<ConsumerListener> listener(mConsumerListener.promote());if (listener != nullptr) {listener->onFrameAvailable(item);}
}
这个listener是ConsumerBase
,于是我们看看消费者的onFrameAvailable
函数:
void ConsumerBase::onFrameAvailable(const BufferItem& item) {CB_LOGV("onFrameAvailable");sp<FrameAvailableListener> listener;{ // scope for the lockMutex::Autolock lock(mFrameAvailableMutex);// 通过调用 promote() 方法,将弱引用 mFrameAvailableListener 提升为强引用 listenerlistener = mFrameAvailableListener.promote();}if (listener != nullptr) {CB_LOGV("actually calling onFrameAvailable");listener->onFrameAvailable(item);}
}
当然,mFrameAvailableListener
这个就是Layer:
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {// ...// If this layer is orphaned, then we run a fake vsync pulse so that// dequeueBuffer doesn't block indefinitely.if (isRemovedFromCurrentState()) {fakeVsync();} else {// 通知Layer已经更新mFlinger->signalLayerUpdate();}mConsumer->onBufferAvailable(item);
}
通过SurfaceFlinger来通知Layer已经更新:
// 通知Layer已经更新
void SurfaceFlinger::signalLayerUpdate() {mScheduler->resetIdleTimer();mEventQueue->invalidate();
}
这个invalidate()
会导致另外一个很重要的线程被唤醒,后面文章再分析。
四、总结:
Buffer被填充完渲染数据之后,通过Binder告诉SF端对应的消费者,后续通知流程:生产者->进入了消费者->Layer->SurfaceFlinger。就进入了视频显示的主流程,具体如何显示,且听下回分解。
相关文章:
Android显示系统(13)- 向SurfaceFlinger提交Buffer
Android显示系统(01)- 架构分析 Android显示系统(02)- OpenGL ES - 概述 Android显示系统(03)- OpenGL ES - GLSurfaceView的使用 Android显示系统(04)- OpenGL ES - Shader绘制三角…...
python小课堂(一)
基础语法 1 常量和表达式2 变量和类型2.1 变量是什么2.2 变量语法 3 变量的类型3.1 动态类型特性 4 注释4.1注释是什么 5 输入输出5.1 print的介绍5.2 input 6 运算符6.1 算术运算符在这里插入图片描述6.2 关系运算符6.3 逻辑运算符6.4赋值运算符 1 常量和表达式 在print()中可…...
【原创教程】西门子1500TCP_UDP通信说明大全(下篇)
2.3.3 TRCV故障说明 通讯无法正常连接时,ERROR引脚和STATUS引脚得状态有助于我们判断错误得原因,根据下表得提示,快速排除问题。 2.3.4 TRCV使用 点击TRCV指令得右上角蓝色图标,打开开始组态画面,按照控制要求填写 EN_R:用于激活接收的控制参数,及何时使用TRCV的接收功…...
【报错记录】Ubuntu22.04解决开机卡在 /dev/sda5 : clean , *files , *blocks
一个愿意伫立在巨人肩膀上的农民...... 一、错误现象 本人的电脑安装Windows10和Ubuntu22.04双系统,一次训练中电脑死机无法开机,重启之后便出现如下错误,在网上寻找过很多方法均无效,在root下禁用了samba服务,也无济…...
JumpServer开源堡垒机搭建及使用
目录 一,产品介绍 二,功能介绍 三,系统架构 3.1 应用架构 3.2 组件说明 3.3 逻辑架构 3.3 逻辑架构 四,linux单机部署及方式选择 4.1 操作系统要求(JumpServer-v3系列版本) 4.1.1 数据库 4.1.3创建数据库参考 4.2 在线安装 4.2.1 环境访问 4.3 基于docker容…...
libilibi项目总结(17)Elasticsearch 的使用
这段代码定义了一个 EsSearchComponent 类,主要用于与 Elasticsearch 进行交互,执行一些基本的操作,如创建索引、保存、更新和删除文档,及搜索操作。以下是对每部分代码的详细解释: 1. 类的依赖注入 Resource privat…...
C++ 模版函数 函数模版 区别
C中,函数模板(Function Template)和模板函数(Template Function)是同一个概念,通常没有区分,但为了避免混淆,有时我们可以从不同的角度来看待它们。 1. 函数模板 (Function Templat…...
SpringBoot 3.4.x踩坑记录及解决方案(持续更新)
废话 最近使用JDK17Spring Boot3.4.0 做新项目遇到的一些坑,记录并且给出一些实际的解决方案 一、集成Mybatis Plus 3.5.9的问题 第一:不能只引入mybatis-plus-spring-boot3-starter依赖了,需要配合mybatis-plus-jsqlparser <dependenc…...
Linux文件属性 --- 七种文件类型---文件.目录、软硬链接、字符设备文件
目录 七种文件类型 1、普通文件和目录 2、链接文件 2.1硬链接 2.2软链接 3、字符设备文件 一、七种文件类型 Linux的文件属性中一共有以下七种类型 : 符号类型含义解释-普通文件纯文本文件(ASCII)和二进制文件(binaryÿ…...
C# 读取EXCEL的数据批量插入单个PDF里的多个位置
C# 读取EXCEL的数据批量插入单个PDF里的多个位置 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; usin…...
ARM Linux 虚拟环境搭建
一、目标 在没有arm硬件的情况下,使用QEMU模拟器,在PC上模拟一块ARM开发板,对ARM Linux进行学习。 二、搭建步骤 首先先有一个Linux 开发环境,我目前使用的是Ubuntu20. 首先安装qemu,qemu的官网:https:…...
【功能安全】安全确认
目录 01 功能安全确认介绍 02 安全确认用例 03 安全确认模板 01 功能安全确认介绍 定义: 来源...
LruCache(本地cache)生产环境中遇到的问题及改进
问题:单机qps增加时请求摘要后端,耗时也会增加,因为超过了后端处理能力(最大qps,存在任务堆积)。 版本一 引入LruCache。为了避免数据失效,cache数据的时效性要小于摘要后端物料的更新时间&…...
【21天学习AI底层概念】day8 什么是类意识?
类意识(Quasi-Consciousness) 是一个用来描述人工智能或复杂系统表现出的类似意识的行为或特性的概念。虽然这种系统不具备真正的意识(即主观体验、情感和自我觉知),但在外部表现上,它们可能表现出与有意识…...
PostgreSQL JSON/JSONB 查询与操作指南
PostgreSQL 提供了强大的 JSON 和 JSONB 数据类型及相关操作,适用于存储和查询半结构化数据。本文将详细介绍其常用操作。 1. 基础操作 1.1 JSON 属性访问 ->: 返回 JSON 对象中的值,结果为 JSON 格式。 SELECT {"a": {"b": 1…...
SamOutV2 0.18B模型发布
项目地址 SamOutV2 0.18B模型 采取 em参数共享在参数量减半的情况下将维度从1024 拉升到了1536sft 单论对话 loss 保持1.8如果未来匹配state 推理代码性能不变的同时推理任意长度使用资源空间保持不变 模型代码 import torchclass MaxState(torch.nn.Module):def __init__(…...
〔 MySQL 〕事务管理
事务代码目录 1. 设置事务隔离级别 2. 开启事务 3. CRUD操作 3.1 创建(Create) 3.2 读取(Read) 3.3 更新(Update) 3.4 删除(Delete) 4. 提交或回滚事务 5. 示例:…...
centOS定时任务-cron服务
最近在训练模型的过程中,经常会因为内存爆炸而停止模型训练过程,而且因为内存占满停止的训练进程甚至都没有任何的报错提示。 1、需要减少num_worker的数量,降低需要占用内存的数据数量 2、可以通过free -h监控内存的占用情况 3、可以通过lin…...
ubuntu22.04.5本地apt源部署
很多情况下,内网服务器无法连接互联网,这样如果原始系统只是最基本的下载安装包,因为存在依赖包不全的情况,难以对其进行更新及通过apt安装包 所以为解决不能联网的问题,首先先通过可以联网的机器制造好源,…...
CSS 实现带tooltip的slider
现代 CSS 强大的令人难以置信 这次我们来用 CSS 实现一个全功能的滑动输入器,也就是各大组件库都有的slider,效果如下 还可以改变一下样式,像这样 特别是在拖动时,tooltip还能跟随拖动的方向和速度呈现不同的倾斜角度,…...
【LeetCode每日一题】——220.存在重复元素 III
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 数组 二【题目难度】 困难 三【题目编号】 220.存在重复元素 III 四【题目描述】 给你一个…...
Git命令
目录 一、创建版本库 二、pwd 命令是用于显示当前的目录 三、通过命令 git init 把这个目录变成git可以管理的仓库 四、ll 五、添加文件和修改提交文件 1.创建文件test.txt --- 此刻文件在工作区(WorkSpace) 2.使用命令 git add test.txt添加到暂…...
2024第十六届蓝桥杯模拟赛(第二期)-Python
# 2024第十六届蓝桥杯模拟赛(第二期)-Python题解 # 自己改注释# -----------------------1------------------------ # def prime(x): # if x < 2: # return 0 # for i in range(2, int(x ** 0.5) 1): # if x % i 0: # …...
数据结构:Win32 API详解
目录 一.Win32 API的介绍 二.控制台程序(Console)与COORD 1..控制台程序(Console): 2.控制台窗口坐标COORD: 3.GetStdHandle函数: (1)语法: (2)参数: 4.GetConsoleCursorInf…...
Hive-4.0.1数据库搭建(可选配置用户名密码远程连接,涵盖切换为tez引擎)
一、hive搭建(所依赖的Hadoop集群参照文章:最新版hadoop-3.4.0集群安装和配置(目前论坛的都是老古董了,看我的准没错!!!)这里以三台服务器为例_hadoop 3.4安装-CSDN博客)…...
【从零开始入门unity游戏开发之——C#篇13】命名规范——驼峰命名法和帕斯卡命名法,函数(方法)的使用介绍
文章目录 一、命名规范1、**驼峰命名法(Camel Case)**用途: 2、**帕斯卡命名法(Pascal Case)**用途: 3、**C# 中命名约定的最佳实践**3.1 **类、结构体、接口、枚举、委托**3.2 **方法、属性、事件**3.3 **…...
Android 写排行榜,顶部前三
activity_step_rank.xml <?xml version"1.0" encoding"UTF-8"?> <FrameLayout android:layout_height"match_parent" android:layout_width"match_parent" android:id"id/fragment_parent" android:orientation…...
sql server一些冷知识
1. Sql Server冷知识 (1) 删除表内容的方法 truncate table 表名 (清除表记录,这个快) (2) 列出所有数据库 sp_redatabases (3) 存储过程的参数命名 参数一定要以开头&am…...
【功能安全】随机硬件失效导致违背安全目标的评估(FMEDA)
目录 01 随机硬件失效介绍 02 FMEDA介绍 03 FMEDA模板 01 随机硬件失效介绍 GBT 34590 part5...
【Qt】信号、槽
目录 一、信号和槽的基本概念 二、connect函数:关联信号和槽 例子: 三、自定义信号和槽 1.自定义槽函数 2.自定义信号函数 例子: 四、带参的信号和槽 例子: 五、Q_OBJECT宏 六、断开信号和槽的连接 例子: …...
二叉树、平衡二叉树、红黑树、BTree、B+Tree的区别
一、二叉查找树 二叉树具有以下性质:左子树的键值小于根的键值,右子树的键值大于根的键值。 如下图所示就是一棵二叉查找树, 对该二叉树的节点进行查找发现深度为1的节点的查找次数为1,深度为2的查找次数为2,深度为n…...
【Rust自学】3.1. 变量与可变性
3.1.0. 写在正文之前 欢迎来到Rust自学的第三章,一共有6个小节,分别是: 变量与可变性(本文)数据类型:标量类型数据类型:复合类型函数和注释控制流:if else控制流:循环 通过第二章…...
如何使用生成式AI实现跨领域内容生成
文章目录 引言生成式AI的基本概念定义与分类技术发展现状 跨领域内容生成的技术实现数据准备模型选择与设计训练策略 应用案例分析教育培训新闻媒体文化创意产业 实践建议确定明确的目标构建合适的团队持续迭代改进遵守法律法规 结论 引言 在当今数字化时代,信息的…...
ubuntu无网络图标无法上网解决方案
1.打开/etc/resolv.conf,在其中添加需要配置的DNS地址,根据自己的电脑情况配置IP和网关 # interfaces(5) file used by ifup(8) and ifdown(8) auto lo iface lo inet loopback#网卡2,双网卡的话eth0、eth1 auto eth1 iface eth1 inet stat…...
手写Redis分布式锁+RedisUtil二次封装
文章目录 1.手写Redis分布式锁1.RedisShareLockUtil2.使用方式 2.RedisUtil二次封装1.RedisUtil2.使用案例 1.手写Redis分布式锁 1.RedisShareLockUtil package com.sunxiansheng.redis.util;import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springfra…...
APP测试中ios和androis的区别,有哪些注意点
一、运行机制不同 IOS采用的是沙盒运行机制,安卓采用的是虚拟机运行机制。 1、沙盒机制: 概念:沙盒是一种安全机制,用于防止不同应用之间互相访问 作用:就是存储数据,每个沙盒就相当于每个每个应用的系…...
Qt编译MySQL数据库驱动
目录 Qt编译MySQL数据库驱动 测试程序 Qt编译MySQL数据库驱动 (1)先找到MySQL安装路径以及Qt安装路径 C:\Program Files\MySQL\MySQL Server 8.0 D:\qt\5.12.12 (2)在D:\qt\5.12.12\Src\qtbase\src\plugins\sqldrivers\mysql下…...
Springboot中使用Retrofit
Retrofit官网 https://square.github.io/retrofit/ 配置gradle implementation("com.squareup.okhttp3:okhttp:4.12.0")implementation ("com.squareup.retrofit2:retrofit:2.11.0")implementation ("com.squareup.retrofit2:converter-gson:2.11.0…...
字体子集化实践探索
最近项目rust生成PDF组件printpdf需要内嵌完整字体导致生成的PDF很大,需要做压缩,但是rust的类库allsorts::subset::subset不支持windows,所以做了一些windows下字体子集化的尝试 方案一:node.js做子集化 fontmin 缺点是也需要集…...
统计一个目录下的文件及目录数量-linux010
要统计一个目录下的文件数量(包括子目录中的文件),可以使用以下命令: 1. 统计所有文件数量(包括子目录) 在终端中运行以下命令: find /path/to/directory -type f | wc -l 解释:…...
【计算机网络】期末考试预习复习|上
作业讲解 物理层作业 共有4个用户进行CDMA通信。这4个用户的码片序列为: A: (–1 –1 –1 1 1 –1 1 1);B: (–1 –1 1 –1 1 1 1 –1) C: (–1 1 –1 1 1 1 –1 –1);D: (–1 1 –1 –1 –1 –1 1 –1) 现收到码片序列:(–1 1 –…...
构建一个rust生产应用读书笔记四(实战3)
从这一节开始,我们将继续完善邮件订阅生产级应用,根据作者的选型sqlx作为数据库操作的类库,它有如下优点: 它旨在提供高效、安全且易于使用的数据库交互体验。sqlx 支持多种数据库,包括 PostgreSQL、MySQL 和 SQLite&…...
idea无法识别文件,如何把floder文件恢复成model
前景: 昨天,我在之前的A1214模块包下新增了一个demo类,然后又新建了一个A1216模块,写了算法题,后面打算用git提交,发现之前的A1214模块下的demo类和新建的模块源文件都已经被追踪了,都是绿色的&…...
更新数据时Redis的操作
一般做法是在数据库更新后删除Redis中对应的缓存数据,而非更新数据。那么为什么要这么做呢? 以下是一些拙见 场景使用 金融交易系统:在金融领域,数据的准确性至关重要。任何数据不一致都可能导致严重的财务损失。因此࿰…...
Flink CDC 读取oracle库数据性能优化
通过综合考虑Oracle数据库配置、Flink作业配置以及其他优化措施,可以显著提升Flink CDC读取Oracle库数据的性能和效率。可以从以下几个方面进行: 一、Oracle数据库配置优化 开启归档日志: 通过执行sqlplus /assysdba或sqlplus/nolog命令…...
学习记录:js算法(一百二十三):不同路径 II
文章目录 不同路径 II思路一 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。 网格中的障碍物…...
mybatis 的动态sql 和缓存
动态SQL 可以根据具体的参数条件,来对SQL语句进行动态拼接。 比如在以前的开发中,由于不确定查询参数是否存在,许多人会使用类似于where 1 1 来作为前缀,然后后面用AND 拼接要查询的参数,这样,就算要查询…...
You need to call SQLitePCL.raw.SetProvider()
在.NET环境中使用Entity Framework Core(EF Core)连接SQLite数据库时,报错。 使用框架 .NET8 错误信息: Exception: You need to call SQLitePCL.raw.SetProvider(). If you are using a bundle package, this is done by calling…...
MYSQL执行一条update语句,期间发生了什么
客户端先通过连接器建立连接,连接器自会判断用户身份; 因为这是一条 update 语句,所以不需要经过查询缓存,但是表上有更新语句,是会把整个表的查询缓存清空的,所以说查询缓存很鸡肋,在 MySQL 8…...
《安全工程师自我防护指南:直面数字威胁的有效策略与实践》
一、法律层面的保护 获取授权 在对目标系统进行任何测试之前,确保已经获得了合法的授权。这可以是来自目标组织(如企业的信息安全部门)的书面授权或者合同协议。例如,一家公司聘请外部安全团队来测试其网络安全防御能力ÿ…...