当前位置: 首页 > news >正文

Android 14 昼夜色切换多屏时候非主屏的Activity无法收到onConfigurationChanged

记录一下遇见的这个问题和查看源码的过程

首先先说遇见的问题

Android 14 昼夜色切换多屏时候 非主屏的Activity 会经常收不到 onConfigurationChanged的回调

分析原因源码中ActivityThread::performActivityConfigurationChanged 里面

   private Configuration performActivityConfigurationChanged(ActivityClientRecord r,Configuration newConfig, Configuration amOverrideConfig, int displayId,boolean alwaysReportChange) {final Activity activity = r.activity;final IBinder activityToken = activity.getActivityToken();// WindowConfiguration differences aren't considered as public, check it separately.// multi-window / pip mode changes, if any, should be sent before the configuration// change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransitionhandleWindowingModeChangeIfNeeded(r, newConfig);final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),displayId);final Configuration currentResConfig =                 activity.getResources().getConfiguration();final int diff = currentResConfig.diffPublicOnly(newConfig);final boolean hasPublicResConfigChange = diff != 0;// TODO(b/173090263): Use diff instead after the improvement of AssetManager and// ResourcesImpl constructions.final boolean shouldUpdateResources = hasPublicResConfigChange|| shouldUpdateResources(activityToken, currentResConfig, newConfig,amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig, r.mSizeConfigurations,activity.mActivityInfo.getRealConfigChanged(), alwaysReportChange);// Nothing significant, don't proceed with updating and reporting.if (!shouldUpdateResources && !shouldReportChange) {return null;}。。。。省略shouldReportChange 这个值出问题时候是个false 也就是说系统认为没有变化 activity.mConfigChangeFlags = 0;if (shouldReportChange) {activity.mCalled = false;activity.mCurrentConfig = new Configuration(newConfig);activity.onConfigurationChanged(configToReport);if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +" did not call through to super.onConfigurationChanged()");}}}

shouldReportChange 这个值出问题时候是个false 也就是说系统认为没有变化 

可是为什么 shouldReportChange 是false?而不是true?昼夜色变化一定会发生改变的,我们看下这个shouldReportChange 方法
 

   @VisibleForTestingpublic static boolean shouldReportChange(@Nullable Configuration currentConfig,@NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets,int handledConfigChanges, boolean alwaysReportChange) {final int publicDiff = currentConfig.diffPublicOnly(newConfig);// Don't report the change if there's no public diff between current and new config.if (publicDiff == 0) {return false;// Report the change regardless if the changes across size-config-buckets.if (alwaysReportChange) {return true;}final int diffWithBucket = SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig,newConfig, sizeBuckets);// Compare to the diff which filter the change without crossing size buckets with// {@code handledConfigChanges}. The small changes should not block Activity to receive// its handlConfigChanges}. The small changes should not block Activity to receive// its handled config updates. Also, if Activity handles all small changes, we should// dispatch the updated config to it.final int diff = diffWithBucket != 0 ? diffWithBucket : publicDiff;// If this activity doesn't handle any of the config changes, then don't bother// calling onConfigurationChanged. Otherwise, report to the activity for the// changes.return (~handledConfigChanges & diff) == 0;}

        final int publicDiff = currentConfig.diffPublicOnly(newConfig);
这个方法比较了新老的 config是否有区别的,而且大家可以感兴趣看一下这个currentConfig.diffPublicOnly方法,这个方法check了uimode也就是新的uimode和老的uimode是否一个值

但是不可能,因为新的uimode如果没有变化就不应该回调,但是回调了就是因为有变化,于是我们接着看这个地方newConfig 是从何而来

我们先看一下到这的调用栈

ActivityThread::handleActivityConfigurationChangedActivityThread::performConfigurationChangedForActivity (这步获取newconfig)           ActivityThread::performConfigurationChangedForActivityActivityThread::performActivityConfigurationChangedactivity::onConfigurationChanged

在ActivityThread::performConfigurationChangedForActivity  获取新的config

看看performConfigurationChangedForActivity 代码

 void handleActivityConfigurationChanged(ActivityClientRecord r,@NonNull Configuration overrideConfig, int displayId, boolean alwaysReportChange) {。。省略final Configuration reportedConfig = performConfigurationChangedForActivity(r,mConfigurationController.getCompatConfiguration(),movedToDifferentDisplay ? displayId : r.activity.getDisplayId(),alwaysReportChange);// Notify the ViewRootImpl instance about configuration changes. It may have initiated this// update to make sure that resources are updated before updating itself.if (viewRoot != null) {if (movedToDifferentDisplay) {viewRoot.onMovedToDisplay(displayId, reportedConfig);}viewRoot.updateConfiguration(displayId);}mSomeActivitiesChanged = true;
}

mConfigurationController.getCompatConfiguration() 就是我们后面拿到的newConfig

也就是说这个config其实不是系统直接发来的其实是APP进程内获取的,那么为什么APP进程为什么获取的错的?mConfigurationController.getCompatConfiguration()是什么时候更新的?为什么会偶尔拿错呢?到底是因为没回调还是时序错误呢?

我先说结论,这是个时序问题

这个地方Android14一共有3个回调,我给出的顺序也是正常应该回调的顺序

1. ConfigurationChangeItem::handleConfigurationChanged 更新 APP进程内cofig 就是这个方法更新完后去getCompatConfiguration 取的数据

 @Overridepublic void handleConfigurationChanged(Configuration config, int deviceId) {mConfigurationController.handleConfigurationChanged(config);
}

2. ActivityConfigurationChangeItem::handleActivityConfigurationChanged

    @Overridepublic void handleActivityConfigurationChanged(ActivityClientRecord r,@NonNull Configuration overrideConfig, int displayId) {handleActivityConfigurationChanged(r, overrideConfig, displayId,// This is the only place that uses alwaysReportChange=true. The entry point should// be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side// has confirmed the activity should handle the configuration instead of relaunch.// If Activity#onConfigurationChanged is called unexpectedly, then we can know it is// something wrong from server side.true /* alwaysReportChange */);}

3. ViewRootImpl::onConfigurationChanged 是解除冻屏后进行resize传来的,这个一会说

 private void init() {parent = null;embeddedID = null;paused = false;stopped = false;hideForNow = false;activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() {@Overridepublic void onConfigurationChanged(Configuration overrideConfig,int newDisplayId) {if (activity == null) {throw new IllegalSttateException("Received config update for non-existing activity");}//我是在这个地方做了判断,没有修改就不回调了,有更好的办法可以讲下activity.mMainThread.handleActivityConfigurationChanged(ActivityClientRecord.this, overrideConfig, newDisplayId,false /* alwaysReportChange */);}

这三个回调按照这个顺序是不会错的,但是,在非主屏的情况下,可能会先收到ViewRootImpl::onConfigurationChanged然后剩下两个回调 这个时候就会出现我们说的当 ViewRootImpl::onConfigurationChanged回调时候APP进程内的config还没更信导致昼夜色取到的值是一样的
那问题来了

ActivityConfigurationChangeItem::handleActivityConfigurationChanged 回调时候为什么uimode不一样了还不进行回调?

  void handleActivityConfigurationChanged(ActivityClientRecord r,@NonNull Configuration overrideConfig, int displayId, boolean alwaysReportChange) {synchronized (mPendingOverrideConfigs) {final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {if (DEBUG_CONFIGURATION) {Slog.v(TAG, "Activity has newer configuration pending so drop this"+ " transaction. overrideConfig=" + overrideConfig+ " pendingOverrideConfig=" + pendingOverrideConfig);}return;}mPendingOverrideConfigs.remove(r.token);}if (displayId == INVALID_DISPLAY) {// If INVALID_DISPLAY is passed assume that the activity should keep its current// display.displayId = r.activity.getDisplayId();}final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity.getDisplayId(), displayId);if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)&& !movedToDifferentDisplay) {if (DEBUG_CONFIGURATION) {Slog.v(TAG, "Activity already handled newer configuration so drop this"+ " transaction. overrideConfig=" + overrideConfig + r.overrideConfig);}return;}// Perform updates.r.overrideConfig = overrideConfig;
这地方更新了config的版本号

r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
                && !movedToDifferentDisplay 这个isOtherSeqNewer 的判断阻拦了我们后续的回调

isOtherSeqNewer判断的是什么?是seq也就是版本号,因为ViewRootImpl::onConfigurationChanged 已经把我们config版本号给更新了,导致后续的回调都被屏蔽了

可能有人注意到 ConfigurationChangeItem::handleConfigurationChanged 里面 走到 handleConfigurationChanged 会更新APP的数据还分发过数据但是为什么收不到,我们来看代码

 void handleConfigurationChanged(@Nullable Configuration config,@Nullable CompatibilityInfo compat) {int configDiff;boolean equivalent;。。。省略直接看分发这步就是后续能get到更新的值  进行修改的地方config = applyCompatConfiguration();这个地方好像能进行分发,但是为什么收不到?final ArrayList<ComponentCallbacks2> callbacks =mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);freeTextLayoutCachesIfNeeded(configDiff);if (callbacks != null) {final int size = callbacks.size();for (int i = 0; i < size; i++) {ComponentCallbacks2 cb = callbacks.get(i);if (!equivalent) {performConfigurationChanged(cb, config);}}}

ActivityThread::collectComponentCallbacks 这个方法获取到的并不是所有的Activity

   @Overridepublic ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {ArrayList<ComponentCallbacks2> callbacks= new ArrayList<ComponentCallbacks2>();ArrayList<ComponentCallbacks2> callbacks= new ArrayList<ComponentCallbacks2>();synchronized (mResourcesManager) {final int NAPP = mAllApplications.size();for (int i=0; i<NAPP; i++) {callbacks.add(mAllApplications.get(i));}这地方includeUiContexts 是个false 所以Activity根本加不进来if (includeUiContexts) {for (int i = mActivities.size() - 1; i >= 0; i--) {final Activity a = mActivities.valueAt(i).activity;if (a != null && !a.mFinished) {callbacks.add(a);}}}final int NSVC = mServices.size();for (int i=0; i<NSVC; i++) {final Service service = mServices.valueAt(i);// If {@code includeUiContext} is set to false, WindowProviderService should not be// collected because WindowProviderService is a UI Context.if (includeUiContext} is set to false, WindowProviderService should not be// collected because WindowProviderService is a UI Context.if (includeUiContexts || !(service instanceof WindowProviderService)) {callbacks.add(service);}}}synchronized (mProviderMap) {final int NPRV = mLocalProviders.size();for (int i=0; i<NPRV; i++) {callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);}}return callbacks;}

 所以根本分发不到Activity,但是Application能接到,就是有点快

屏蔽了所以副屏的APP都无法回调了,那问题来了,到底是系统层的回调错了还是为什么会导致回调时序错误?

还是先说结论系统层分发来的时候时序是没问题的上代码看一下

首先先看昼夜色切换触发的方法

首先是UIModeManager::setNightModeActivated 

@RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)public boolean setNightModeActivated(boolean active) {if (mService != null) {try {return mService.setNightModeActivated(active);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return false;}

mService其实就是 UiModeManagerService

然后走到 UiModeManagerService

 @Overridepublic boolean setNightModeActivated(boolean active) {return setNightModeActivatedForModeInternal(mNightModeCustomType, active);}private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)!= PackageManager.PERMISSION_GRANTED) {Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission");return false;}final int user = Binder.getCallingUserHandle().getIdentifier();if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)!= PackageManager.PERMISSION_GRANTED) {Slog.e(TAG, "Target user is not current user,"+ " INTERACT_ACROSS_USERS permission is required");return false;} // Store the last requested bedtime night mode state so that we don't need to notify// anyone if the user decides to switch to the night mode to bedtime.if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {mLastBedtimeRequestedNightMode = active;}if (modeCustomType != mNightModeCustomType) {return false;}synchronized (mLock) {final long ident = Binder.clearCallingIdentity();try {if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {unregisterScreenOffEventLocked();mOverrideNightModeOff = !active;mOverrideNightModeOn = active;mOverrideNightModeUser = mCurrentUser;persistNightModeOverrides(mCurrrentUser);} else if (mNightMode == UiModeManager.MODE_NIGHT_NO&& active) {mNightMode = UiModeManager.MODE_NIGHT_YES;} else if (mNightMode == UiModeManager.MODE_NIGHT_YES&& !active) {mNightMode = UiModeManager.MODE_NIGHT_NO;}updateConfigurationLocked();applyConfigurationExternallyLocked();persistNightMode(mCurrentUser);return true;} finally {Binder.restoreCallingIdentity(ident);}}}注意一下                    applyConfigurationExternallyLocked();
方法

重点关注                    applyConfigurationExternallyLocked();方法

 private void applyConfigurationExternallyLocked() {if (mSetUiMode != mConfiguration.uiMode) {mSetUiMode = mConfiguration.uiMode;// load splash screen instead of screenshotmWindowManager.clearSnapshotCache();try {ActivityTaskManager.getService().updateConfiguration(mConfiguration);} catch (RemoteException e) {Slog.w(TAG, "Failure communicating with activity manager", e);}}}

很明显,在这走到了ATMS的方法里

   @Overridepublic boolean updateConfiguration(Configuration values) {。。。updateConfigurationLocked(values, null, false, false /* persistent */,UserHandle.USER_NULL, false /* deferResume */,mTmpUpdateConfigurationResult);。。。}boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,boolean initLocale, boolean persistent, int userId, boolean deferResume,ActivityTaskManagerService.UpdateConfigurationResult result) {int changes = 0;boolean kept = true; 
首先先冻屏deferWindowLayout();try {if (values != null) {1然后更新所有的config 给所有的config新的赋值changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId);}if (!deferResume) {2更新所有的Activity把Activity数据更新 真正的分发kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);} } finally {3最后解除冻屏continueWindowLayout();}if (result != null) {result.changes = changes;result.activityRelaunched = !kept;}return kept;}

 updateConfigurationLocked 这个方法很重要,可以看到这三个方法不存在顺序出错的可能性

我们可以看到上面的三个APP层回调也是这三个方法分别回调

因为我们已经知道系统中三个回调顺序不可能出错,但是冻屏解除的resize居然先于上面两个方法到来,个人猜测是因为解除冻屏的AIDL回调跟 updateGlobalConfigurationLocked 和 ensureConfigAndVisibilityAfterUpdate两个方法回调的AIDL不是一个导致的

updateGlobalConfigurationLocked 和 ensureConfigAndVisibilityAfterUpdate两个方法回调都到了Application里,但是 continueWindowLayout走的是ViewRootImpl

还有一个问题,正常AIDL也是同步的,博主看了下系统到APP的代码,AIDL都带了ONEWAY标签,所以没法控制时序

所以看到这里,结论就是原生的调用链其实顺序是没问题的,问题是到达APP的顺序出错,所以此题有两个解法

1. 修改版本号

2. 检查是否改变,无改变不回调(需要验证)

那我们来看看版本号在哪改变的让我们看方法1

  int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,boolean persistent, int userId) {mTempConfig.setTo(getGlobalConfiguration());final int changes = mTempConfig.updateFrom(values);if (changes == 0) {return 0;}
。。。mTempConfig.seq = increaseConfigurationSeqLocked();
。。。
}int increaseConfigurationSeqLocked() {mConfigurationSeq = Math.max(++mConfigurationSeq, 1);return mConfigurationSeq;}

所以是这个地方进行了改变,这个版本号接下来回调也使用

相关文章:

Android 14 昼夜色切换多屏时候非主屏的Activity无法收到onConfigurationChanged

记录一下遇见的这个问题和查看源码的过程 首先先说遇见的问题 Android 14 昼夜色切换多屏时候 非主屏的Activity 会经常收不到 onConfigurationChanged的回调 分析原因源码中ActivityThread::performActivityConfigurationChanged 里面 private Configuration performActivi…...

NLP常见任务专题介绍(4)-ConditionalGeneration和CasualLM区别

在 transformers 库中,ConditionalGeneration 和 CausalLM 是两种不同类型的语言模型,各自适用于不同的任务: 类别Conditional Generation (条件生成)CausalLM (因果语言模型)核心区别依赖输入 条件 生成文本只能 自回归 生成文本训练方式Encoder-Decoder(编码-解码) 结构…...

【vue+excel】导出excel(目前是可以导出两个sheet)

项目里经常用到的导出ecxel功能是默认导出一个sheet页 现在需要导出两个sheet&#xff0c;一个是总计&#xff0c;另一个是明细 效果如下&#xff1a; 我就在现有的单个导出的功能上改造了一下&#xff0c;只支持导出两个&#xff08;代码不够灵活&#xff0c;如果需要多个&…...

【数据结构】6栈

0 章节 3&#xff0e;1到3&#xff0e;3小节。 认知与理解栈结构&#xff1b; 列举栈的操作特点。 理解并列举栈的应用案例。 重点 栈的特点与实现&#xff1b; 难点 栈的灵活实现与应用 作业或思考题 完成学习测试&#xff12;&#xff0c;&#xff1f; 内容达成以下标准(考核…...

【C++】每日一练(有效的括号)

本篇博客给大家带来的是用C语言来解答有效的括号&#xff01; &#x1f41f;&#x1f41f;文章专栏&#xff1a;每日一练 &#x1f680;&#x1f680;若有问题评论区下讨论&#xff0c;我会及时回答 ❤❤欢迎大家点赞、收藏、分享&#xff01; 今日思想&#xff1a;不服输的少年…...

数字化新零售与 AI 大模型,如何重塑大健康赛道?​

在数字化浪潮中&#xff0c;大健康赛道正经历深刻变革。数字化新零售营销模式的兴起&#xff0c;与 AI 大模型的强大能力相结合&#xff0c;为大健康领域带来了全新的发展机遇。 数字化新零售营销模式融合线上线下&#xff0c;运用大数据、云计算分析消费者行为&#xff0c;实…...

Android实现Socket通信

问题&#xff1a; 我在Android端对接后端Socket服务实现消息模块的时候&#xff0c;使用WebSocketClient 库&#xff0c;手动构造了订阅和发送消息的 STOMP 帧&#xff0c;&#xff0c;Socket连接成功建立&#xff0c;但是当用户发送消息的时候&#xff0c;对方无法接受到。 …...

✅ Vue 3 响应式写法小抄表(Composition API 实战模板)

&#x1f4e6; 引入核心响应式工具 import { ref, reactive, computed, watch, toRefs } from vue1️⃣ 基本类型&#xff08;string / number / boolean&#xff09; → 推荐使用 ref() const username ref() const count ref(0) const isLoading ref(false)2️⃣ 对象/表…...

Python数据分析之数据可视化

Python 数据分析重点知识点 本系列不同其他的知识点讲解&#xff0c;力求通过例子让新同学学习用法&#xff0c;帮助老同学快速回忆知识点 可视化系列&#xff1a; Python基础数据分析工具数据处理与分析数据可视化机器学习基础 四、数据可视化 图表类型与选择 根据数据特…...

TCP协议支持全双工原因TCP发送接收数据是生产者消费者模型

一、TCP支持全双工的原因 TCP协议支持全双工&#xff0c;即使用TCP协议进行通信时&#xff0c;服务端和客户端可以同时进行数据的发送和接收&#xff0c;互不干扰&#xff0c;实现同时双向传输数据。 这是因为使用TCP协议通信时&#xff0c;读写套接字的文件描述符既用来发送…...

【ODHead】BEVDet的 CenterHead的推理和拓展到蒸馏损失的算法细节

文章目录 背景常识1、BEVDet的CenterHead整体方案2、蒸馏部分3、输出 preds_dicts 部分3.1、headmap3.2、bbox3.3、Mask掩膜3.4、损失 背景常识 在BEVDet和BEVFormer里&#xff0c;使用了不同的3D detection head&#xff08;BEVDet用了centerhead&#xff0c;BEVFormer用了de…...

在 CentOS 7 上安装 PHP 7.3

在 CentOS 7 上安装 PHP 7.3 可以按照以下步骤进行操作&#xff1a; 1. 安装必要的依赖和 EPEL 仓库 EPEL&#xff08;Extra Packages for Enterprise Linux&#xff09;是为企业级 Linux 提供额外软件包的仓库&#xff0c;yum-utils 用于管理 yum 仓库。 sudo yum install -…...

vue+dhtmlx-gantt 实现甘特图-快速入门【甘特图】

文章目录 一、前言二、使用说明2.1 引入依赖2.2 引入组件2.3 引入dhtmlx-gantt2.4 甘特图数据配置2.5 初始化配置 三、代码示例3.1 Vue2完整示例3.2 Vue3 完整示例 四、效果图 一、前言 dhtmlxGantt 是一款功能强大的甘特图组件&#xff0c;支持 Vue 3 集成。它提供了丰富的功…...

关于ModbusTCP/RTU协议对接Ethernet/IP(CIP)协议的方案

IGT-DSER智能网关模块支持西门子、倍福(BECKHOFF)、罗克韦尔AB&#xff0c;以及三菱、欧姆龙等各种品牌的PLC之间通讯&#xff0c;支持Ethernet/IP(CIP)、Profinet(S7)&#xff0c;以及FINS、MC等工业自动化常用协议&#xff0c;同时也支持PLC与Modbus协议的工业机器人、智能仪…...

python-leetcode 49.二叉树中的最大路径和

题目&#xff1a; 二叉树中的路径被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边&#xff0c;同一个节点在一条路径序列中至多出现一次&#xff0c;该路径至少包含一个节点&#xff0c;且不一定经过根节点。 路径和是路径中各节点值得总和&#xff0c;…...

C语言基础知识04

指针 指针概念 指针保存地址&#xff0c;地址是字节的编号 指针类型和保存的地址类型要一直 使用时注意&#xff0c;把地址转换为&变量的格式来看 int a[3]; a转为&a[0] 指针的大小 64bit 固定8字节&#xff0c; 32bit 固定4字节 指针…...

使用 Golang 操作 MySQL

在Go语言中&#xff0c;操作SQL数据库&#xff0c;通常会用到一些第三方库来简化数据库的连接、查询和操作过程。其中原生的 database/sql go-sql-driver/mysql 库更符合sql语句使用习惯。‌ 安装 go get github.com/go-sql-driver/mysql 直接上代码来演示基本的创建&#xff…...

前端面试:cookie 可以实现不同域共享吗?

在前端开发中&#xff0c;Cookie 不能直接实现不同域之间的共享。Cookie 的作用域受到域的限制&#xff0c;浏览器不会允许一个域下的 Cookie 被另一个域访问。这是为了保护用户隐私及安全&#xff0c;防止跨站请求伪造&#xff08;CSRF&#xff09;等安全问题。 Cookie 的基本…...

MyBatis-Plus接入和简单使用

如何接入 https://baomidou.com/getting-started/ 简单使用方法 使用 MyBatis-Plus 时&#xff0c;大多数场景下不需要编写 XML 和 SQL&#xff0c;因为它提供了强大的通用 CRUD 操作和条件构造器。但以下情况可能需要手动编写 SQL&#xff1a; 1. 不需要写 XML/SQL 的场景 …...

【Go万字洗髓经】Golang内存模型与内存分配管理

本文目录 1. 操作系统中的虚拟内存分页与进程管理虚拟内存与内存隔离 2. Golang中的内存模型内存分配流程内存单元mspan线程缓存mcache中心缓存mcentral全局堆缓存mheapheapArena空闲页索引pageAlloc 3. Go对象分配mallocgc函数tiny对象分配内存 4.结合GMP模型来看内存模型tiny…...

mov格式视频如何转换mp4?

mov格式视频如何转换mp4&#xff1f;在日常的视频处理中&#xff0c;经常需要将MOV格式的视频转换为MP4格式&#xff0c;以兼容更多的播放设备和平台。下面给大家分享如何将MOV视频转换为MP4&#xff0c;4款视频格式转换工具分享。 一、牛学长转码大师 牛学长转码大师是一款功…...

鸿蒙OS开发ForEach循环渲染

摘要 在ForEach循环渲染过程中&#xff0c;如果修改列表项中的数据&#xff0c;但是UI页面不会刷新。在最近开发公司app时遇到了这个问题&#xff0c;经过查看官方文档找到了解决方式 官方地址&#xff1a;数据变化不刷新 一、具体解决方案 思路&#xff1a;通过父子组件传…...

【算法】DFS、BFS、拓扑排序

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;算法 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 持续更新中...1、DFS2、BFSN 叉树的层序遍历二叉树的锯齿形层序遍历二叉树最大宽度 3、多源BFS腐烂的苹果 4、拓扑排序 持续更新中…...

【Godot4.0】贝塞尔曲线在游戏中的实际应用

概述 之前研究贝塞尔曲线绘制&#xff0c;完全是以绘图函数&#xff0c;以及实现节点连接为思考。并没有实际考虑贝塞尔曲线在游戏中的应用。今日偶然看到悦千简一年多前发的一个用贝塞尔曲线实现追踪弹或箭矢效果&#xff0c;还有玩物不丧志的老李杀戮尖塔系列中的卡牌动态箭…...

MongoDB 数据导出与导入实战指南(附完整命令)

1. 场景说明 在 MongoDB 运维中&#xff0c;数据备份与恢复是核心操作。本文使用 mongodump 和 mongorestore 工具&#xff0c;演示如何通过命令行导出和导入数据&#xff0c;解决副本集连接、路径指定等关键问题。 2. 数据导出&#xff08;mongodump&#xff09; 2.1 导出命…...

『Rust』Rust运行环境搭建

文章目录 rust编译工具rustupVisual Studio VS Code测试编译手动编译VSCode编译配置 参考完 rust编译工具rustup https://www.rust-lang.org/zh-CN/tools/install 换源 RUSTUP_DIST_SERVER https://rsproxy.cn RUSTUP_UPDATE_ROOT https://rsproxy.cn修改rustup和cargo的安…...

CPU+GPU结合的主板设计思路与应用探讨

在高性能计算和图形处理需求不断增长的背景下&#xff0c;CPUGPU结合的主板设计逐渐成为硬件架构的重要趋势。本文将探讨基于CPUGPU架构的主板设计思路、关键技术考量以及应用前景。 1. 设计思路概述 CPU&#xff08;中央处理器&#xff09;擅长处理复杂的逻辑运算和多任务控制…...

latex问题汇总

latex问题汇总 环境问题1 环境 texlive2024 TeXstudio 4.8.6 (git 4.8.6) 问题1 编译过程有如下错 ! Misplaced alignment tab character &. l.173 International Conference on Infrared &Millimeter Waves, 2004: 667--... I cant figure out why you would wa…...

学习springboot-Bean管理(Bean 注册,Bean 扫描)

Bean 扫描 可以浏览下面的博客链接 &#xff1a;spring 学习 &#xff08;注解&#xff09;-CSDN博客 在学习spring 注解时&#xff0c;我们使用 Component &#xff0c;Service,Controller等 这样的注解&#xff0c;将目标类信息&#xff0c;传递给IOC容器&#xff0c;为其创…...

iOS开发,SQLite.swift, Missing argument label ‘value:‘ in call问题

Xcode16中&#xff0c;集成使用SQLite.swift&#xff0c;创建表的时候&#xff1a; let id Expression<Int64>("id")&#xff0c;报错Missing argument label value: in call 直接使用SQLite.Expression<Int64>("id") 或者定义一个全局typ…...

【GIT】重新初始化远程仓库

有的时候我们克隆远端仓库会出错&#xff1a; git clone --depth 1 git116.*.*.*:/srv/customs.git D:\dev\projects\kdy\customs11\customs Cloning into D:\dev\projects\kdy\customs11\customs... remote: Enumerating objects: 1494, done. remote: Counting objects: 100…...

Vue3中 ref 与 reactive区别

ref 用途: ref 通常用于创建一个响应式的基本类型数据&#xff08;如 string、number、boolean 等&#xff09;&#xff0c;但它也可以用于对象或数组 返回值: ref 返回一个带有 .value 属性的对象&#xff0c;访问或修改数据需要通过 .value 进行 使用场景: …...

apollo3录音到wav播放解决方法

SDK DEMO项目:ap3bp_evb_vos_pcm_recorder_20210901 pcm_recorder.c //***************************************************************************** // // Options // //***************************************************************************** #define PRINT…...

信号处理抽取多项滤波的数学推导与仿真

昨天的《信号处理之插值、抽取与多项滤波》&#xff0c;已经介绍了插值抽取的多项滤率&#xff0c;今天详细介绍多项滤波的数学推导&#xff0c;并附上实战仿真代码。 一、数学变换推导 1. 多相分解的核心思想 将FIR滤波器的系数 h ( n ) h(n) h(n)按相位分组&#xff0c;每…...

Java网络多线程

网络相关概念: 关于访问: IP端口 因为一个主机上可能有多个服务, 一个服务监听一个端口,当你访问的时候主机通过端口号就能知道要和哪个端口发生通讯.因此一个主机上不能有两个及以上的服务监听同一个端口. 协议简单来说就是数据的组织形式 好像是两个人交流一样,要保证自己说…...

linux centos 忘记root密码拯救

在CentOS 7中&#xff0c;如果忘记root密码&#xff0c;可以通过修改系统启动参数进入单用户模式或紧急模式进行重置。以下是两种常用方法&#xff0c;适用于物理机或虚拟机环境&#xff1a; 方法一&#xff1a;通过rd.break参数重置密码 步骤&#xff1a; 重启系统并进入GRU…...

C# 事件使用详解

总目录 前言 在C#中&#xff0c;事件&#xff08;Events&#xff09;是一种基于委托的重要机制&#xff0c;用于实现对象之间的松耦合通信。它通过发布-订阅模式&#xff08;Publisher-Subscriber Pattern&#xff09;&#xff0c;允许一个对象&#xff08;发布者&#xff09;…...

flink cdc同步mysql数据

一、api 添加依赖 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-mysql-cdc</artifactId><!-- 请使用已发布的版本依赖&#xff0c;snapshot 版本的依赖需要本地自行编译。 --><version>3.3-SNAP…...

tomcat负载均衡配置

这里拿Nginx和之前做的Tomcat 多实例来实现tomcat负载均衡 1.准备多实例与nginx tomcat单机多实例部署-CSDN博客 2.配置nginx做负载均衡 upstream tomcat{ server 192.168.60.11:8081; server 192.168.60.11:8082; server 192.168.60.11:8083; } ser…...

Ceph(1):分布式存储技术简介

1 分布式存储技术简介 1.1 分布式存储系统的特性 &#xff08;1&#xff09;可扩展 分布式存储系统可以扩展到几百台甚至几千台的集群规模&#xff0c;而且随着集群规模的增长&#xff0c;系统整体性能表现为线性增长。分布式存储的水平扩展有以下几个特性&#xff1a; 节点…...

16、JavaEE核心技术-EL与 JSTL

EL与 JSTL 实践 一. EL&#xff08;Expression Language&#xff09; EL&#xff08;表达式语言&#xff09;是 JSP 2.0 中引入的一种简单的脚本语言&#xff0c;用于在 JSP 页面中简化数据的访问和显示。它通过一种类似于 JavaScript 的语法&#xff0c;允许开发者在 JSP 页面…...

RabbitMQ报错:Shutdown Signal channel error; protocol method

报错信息&#xff1a; Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code406, reply-textPRECONDITION_FAILED - unknown delivery tag 1, class-id60, method-id80) 原因 默认情况下 RabbitMQ 是自动ACK&#xff08;确认签收&…...

使用DeepSeek完成一个简单嵌入式开发

开启DeepSeek对话 请帮我使用Altium Designer设计原理图、PCB&#xff0c;使用keil完成代码编写&#xff1b;要求&#xff1a;使用stm32F103RCT6为主控芯片&#xff0c;控制3个流水灯的原理图 这里需要注意&#xff0c;每次DeepSeek的回答都不太一样。 DeepSeek回答 以下是使…...

NLP技术介绍

NLP技术介绍 语言分析技术分词词性标注命令实体识别句法分析语义分析文本处理技术文本分类文本聚类情感分析文本生成机器翻译对话系统与交互技术聊天机器人问答系统语音识别与合成知识图谱与语义理解技术知识图谱语义搜索语义推理深度学习与预训练模型循环神经网络(RNN)及其变…...

pycharm + anaconda + yolo11(ultralytics) 的视频流实时检测,保存推流简单实现

目录 背景pycharm安装配置代码实现创建本地视频配置 和 推流配置视频帧的处理和检测框绘制主要流程遇到的一些问题 背景 首先这个基于完整安装配置了anaconda和yolo11的环境&#xff0c;如果需要配置开始的话&#xff0c;先看下专栏里另一个文章。 这次的目的是实现拉取视频流…...

C++编译问题——1模板函数的实现必须在头文件中

今天编译数据结构时&#xff0c;遇见一个编译错误 假设你有一个头文件 SeqList.h 和一个源文件 SeqList.cpp。 SeqList.h #ifndef SEQLIST_H #define SEQLIST_H#include <stdexcept> #include <iostream>template<typename T> class SeqList { private:sta…...

深度学习PyTorch之数据加载DataLoader

深度学习pytorch之简单方法自定义9类卷积即插即用 文章目录 数据加载基础架构1、Dataset类详解2、DataLoader核心参数解析3、数据增强 数据加载基础架构 核心类关系图 torch.utils.data ├── Dataset (抽象基类) ├── DataLoader (数据加载器) ├── Sampler (采样策略)…...

使用Beanshell前置处理器对Jmeter的请求body进行加密

这里我们用HmacSHA256来进行加密举例&#xff1a; 步骤&#xff1a; 1.先获取请求参数并对请求参数进行处理&#xff08;处理成String类型&#xff09; //处理请求参数的两种方法&#xff1a; //方法一&#xff1a; //获取请求 Arguments args sampler.getArguments(); //转…...

前端面试:如何减少项目里面 if-else?

在前端开发中&#xff0c;大量使用 if-else 结构可能导致代码调试困难、可读性降低和冗长的逻辑。不妨考虑以下多种策略来减少项目中的 if-else 语句&#xff0c;提高代码的可维护性和可读性&#xff1a; 1. 使用对象字面量替代 用对象字面量来替代 if-else 语句&#xff0c;…...

05.基于 TCP 的远程计算器:从协议设计到高并发实现

&#x1f4d6; 目录 &#x1f4cc; 前言&#x1f50d; 需求分析 &#x1f914; 我们需要解决哪些问题&#xff1f; &#x1f3af; 方案设计 &#x1f4a1; 服务器架构 &#x1f680; 什么是协议&#xff1f;为什么要设计协议&#xff1f; &#x1f4cc; 结构化数据的传输问题 …...