【Android 13源码分析】WindowContainer窗口层级-3-实例分析
在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。
对这一块的概念以及相关源码做了详细分析,整理出以下几篇。
【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树
【Android 13源码分析】WindowContainer窗口层级-2-构建流程
【Android 13源码分析】WindowContainer窗口层级-3-实例分析
【Android 13源码分析】WindowContainer窗口层级-4-Surface树
当前为第三篇,以应用窗口和系统窗口2大类型窗口的挂载为例介绍窗口是如何挂载到层级树中的。
这篇看完对AOSP中整个窗口树就有了比较完整的了解。
1. 应用窗口挂载
应用启动流程中会触发ActivityRecord,Task,WindowState的创建与挂载,其中WindowState的处理上在addWindow流程。
ActivityTaskManagerService::startActivityActivityTaskManagerService::startActivityAsUserActivityTaskManagerService::startActivityAsUserActivityStartController::obtainStarterActivityStarter::executeActivityStarter::executeRequest -- 构建 ActivityRecord --2.2.1 创建ActivityRecordActivityStarter::startActivityUncheckedActivityStarter::startActivityInner -- 2.2.2 关键函数startActivityInnerActivityStarter::getOrCreateRootTask -- 2.2.2.1 创建或者拿到TaskActivityStarter::setNewTask -- 2.2.2.2 将task与activityRecord 绑定RootWindowContainer::resumeFocusedTasksTopActivities --2.2.2.3 显示ActivityaddWindow流程调用链:
WindowManagerImpl::addView --- 创建ViewRootImplWindowManagerGlobal::addView ViewRootImpl::setView --- 与WMS通信 addViewSession.addToDisplayAsUserWindowManagerService::addWindowWindowState::init -- WindowState的创建WindowToken::addWindow -- WindowState的挂载
1.1 ActivityRecord的创建
启动流程开始的时候会执行到ActivityStarter::executeRequest,在这个方法里会创建一个ActivityRecord
# ActivityStarterprivate int executeRequest(Request request) {......final ActivityRecord r = new ActivityRecord.Builder(mService).setCaller(callerApp).setLaunchedFromPid(callingPid).setLaunchedFromUid(callingUid).setLaunchedFromPackage(callingPackage).setLaunchedFromFeature(callingFeatureId).setIntent(intent).setResolvedType(resolvedType).setActivityInfo(aInfo).setConfiguration(mService.getGlobalConfiguration()).setResultTo(resultRecord).setResultWho(resultWho).setRequestCode(requestCode).setComponentSpecified(request.componentSpecified).setRootVoiceInteraction(voiceSession != null).setActivityOptions(checkedOptions).setSourceRecord(sourceRecord).build();......// 继续执行startActivityUncheckedmLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,inTask, inTaskFragment, restrictedBgActivity, intentGrants);......}
tips: ActivityRecord的构造方法会创建一个Token,这个token就是阅读源码经常看到看到代表activity的那个token。
1.2 Task的创建与挂载
流程开始会执行到ActivityStarter::startActivityInner方法,在这里会执行ActivityStarter::getOrCreateRootTask方法来创建(获取)一个Task
调用链如下:
ActivityStarter::getOrCreateRootTaskRootWindowContainer::getOrCreateRootTaskRootWindowContainer::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTaskDisplayArea::getOrCreateRootTaskTask::Build ---创建Task
主流程代码
# ActivityStarterprivate Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,ActivityOptions aOptions) {final boolean onTop =(aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,mLaunchParams, launchFlags);}// onTop 表示是否要移到到当前栈顶,那肯定是要的,新启动的Activity当前要再最上面,这里 aOptions 为null,所以为true// sourceTask 表示从哪里启动的,当前launch所在的Task 就是sourceTask# RootWindowContainerTask getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,@Nullable Task candidateTask, boolean onTop) {return getOrCreateRootTask(r, options, candidateTask, null /* sourceTask */, onTop,null /* launchParams */, 0 /* launchFlags */);}Task getOrCreateRootTask(@Nullable ActivityRecord r,@Nullable ActivityOptions options, @Nullable Task candidateTask,@Nullable Task sourceTask, boolean onTop,@Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {......final int activityType = resolveActivityType(r, options, candidateTask);if (taskDisplayArea != null) {if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {// 重点*1. 传递到TaskDisplayAreareturn taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,sourceTask, launchParams, launchFlags, activityType, onTop);} else {taskDisplayArea = null;}}......}// 经过同名调用后,逻辑进入到 TaskDisplayArea# TaskDisplayAreaTask getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,@Nullable Task candidateTask, @Nullable Task sourceTask,@Nullable ActivityOptions options, int launchFlags) {if(....) {// 拿到之前创建的Taskreturn candidateTask.getRootTask();}......// 第一次显示所以是新建Taskreturn new Task.Builder(mAtmService).setWindowingMode(windowingMode).setActivityType(activityType).setOnTop(onTop).setParent(this) // 主要这个this被设置为Parent。所以直接挂载到了DefaultTaskDisplayArea下.setSourceTask(sourceTask).setActivityOptions(options).setLaunchFlags(launchFlags).build();}// 看方法名是获取或创建Task, 这边是新启动的Activity所以需要创建Task。如果是以默认启动方式打开应用内的另一个Activity,就走的是上面的 return candidateTask.getRootTask();接下来就是真正触发Task的创建。// 另外设置的parent就是层级结构树应用所在的名为“DefaultTaskDisplayArea”的TaskDisplayArea# Task# Task.BuilderTask build() {if (mParent != null && mParent instanceof TaskDisplayArea) {validateRootTask((TaskDisplayArea) mParent);}if (mActivityInfo == null) {mActivityInfo = new ActivityInfo();mActivityInfo.applicationInfo = new ApplicationInfo();}mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);mTaskAffiliation = mTaskId;mLastTimeMoved = System.currentTimeMillis();mNeverRelinquishIdentity = true;mCallingUid = mActivityInfo.applicationInfo.uid;mCallingPackage = mActivityInfo.packageName;mResizeMode = mActivityInfo.resizeMode;mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();if (mActivityOptions != null) {mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();}// 重点* 1. 创建taskfinal Task task = buildInner();task.mHasBeenVisible = mHasBeenVisible;// Set activity type before adding the root task to TaskDisplayArea, so home task can// be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {task.setActivityType(mActivityType);}// 重点* 2. 入栈 这里的 mOnTop为trueif (mParent != null) {if (mParent instanceof Task) {final Task parentTask = (Task) mParent;parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,(mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);} else {mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);}}// Set windowing mode after attached to display area or it abort silently.if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {task.setWindowingMode(mWindowingMode, true /* creating */);}// 返回return task;}// 创建Task buildInner() {return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);}
小结:
最后描述一下最后创建的2个重点部分:
- 看到通过buildInner 创建了一个task,而buildInner 也很简单粗暴,通过各个变量直接new Task 对象。
- mParent 不为null, 是 因为在创建的时候 setParent(this),当前的这个this,就是 getDefaultTaskDisplayArea返回的也就是应用Activity存在的"DefaultTaskDisplayArea"。
在 RootWindowContainer::getOrCreateRootTask 体现。
注意log里的 #17 的这个Task,与前面的层级结构树新增的Task,是对应的上的。而且this= DefaultTaskDisplayArea 说明也确实是往DefaultTaskDisplayArea里添加了。
1.3 ActivityRecord的挂载
调用链
ActivityStarer::setNewTask
ActivityStarer::addOrReparentStartingActivity
主流程代码
# ActivityStarerprivate void setNewTask(Task taskToAffiliate) {// 为truefinal boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;// 就是mTargetRootTask,也就是刚刚创建的Taskfinal Task task = mTargetRootTask.reuseOrCreateTask(mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);task.mTransitionController.collectExistenceChange(task);// ActivityRecord的挂载addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");// 需要注意这里的日志打印ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",mStartActivity, mStartActivity.getTask());// mLaunchTaskBehind 为false,所以taskToAffiliate 为null if (taskToAffiliate != null) {mStartActivity.setTaskToAffiliateWith(taskToAffiliate);}}
这里的task 和mTargetRootTask是同一个对象, 进源码跟到流程也是一样。
然后进入 addOrReparentStartingActivity
# ActivityStarerprivate void addOrReparentStartingActivity(@NonNull Task task, String reason) {// newParent = task 都是刚刚创建的TaskTaskFragment newParent = task;......if (mStartActivity.getTaskFragment() == null|| mStartActivity.getTaskFragment() == newParent) {// 重点, 将 ActivityRecord挂在到新创建的Task中,并且是顶部newParent.addChild(mStartActivity, POSITION_TOP);} else {mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);}}
这里的逻辑设计到的Task就是上一步创建的Task,mStartActivity则是“电话”在之前逻辑创建的ActivityRecord.
setNewTask的堆栈信息如下
另外这段逻辑里有个ProtoLog打印,日志如下:
1.4 WindowState的创建与挂载
WindowManagerService::addWindowWindowState::init -- WindowState的创建WindowToken::addWindow -- WindowState的挂载
# WindowManagerServicepublic int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {......// token处理// 创建WindowStatefinal WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);......// 7. 窗口添加进容器win.attach();......win.mToken.addWindow(win);......}"win.mToken"窗口的token是ActyivityRecord# ActivityRecord@Overridevoid addWindow(WindowState w) {super.addWindow(w);......}直接调用其父类方法,ActivityRecord是WindowToken# WindowTokenvoid addWindow(final WindowState win) {ProtoLog.d(WM_DEBUG_FOCUS,"addWindow: win=%s Callers=%s", win, Debug.getCallers(5));if (win.isChildWindow()) {// Child windows are added to their parent windows.return;}// This token is created from WindowContext and the client requests to addView now, create a// surface for this token.// 真正添加进子容器,调用的是WindowContainer的方法if (!mChildren.contains(win)) {ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);// 定义在WindowContainer中,其实就是挂载到父容器下了addChild(win, mWindowComparator);mWmService.mWindowsChanged = true;// TODO: Should we also be setting layout needed here and other places?}}
2. 系统窗口挂载
2.1 WindowToken,WindowState的创建与挂载
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {......// 系统应用获取不到tokenWindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);......if (token == null) {......} else {final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}}// 创建WindowStatefinal WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);......// 窗口添加进容器win.attach();......win.mToken.addWindow(win);......
WindowState的创建和应用窗口一样,区别在与WindowToken,系统窗口执行addWindow方法是没有token的,所以会执行创建逻辑。
在创建的时候会根据窗口类型选择挂载的层级。
2.2 WindowToken的挂载
WMS::addWindowWindowToken::initDisplayContent::addWindowTokenmTokenMap::put -- 存入mTokenMapDisplayContent::findAreaForToken -- 找到对应的层级DisplayContent::findAreaForWindowTypeDisplayAreaPolicyBuilder.Result::findAreaForWindowTypeRootDisplayArea::findAreaForWindowTypeInLayer -- 在 mAreaForLayer中根据type查找对应的位置DisplayArea.Tokens::addChild -- 挂载
WindowToken的构造方法如下:
# WindowTokenprotected WindowToken(WindowManagerService service, IBinder _token, int type,boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {super(service);token = _token;windowType = type;......if (dc != null) {// 添加tokendc.addWindowToken(token, this);}}
创建WindowToken的时候会由DisplayContent执行挂载逻辑,
# DisplayContentDisplayAreaPolicy mDisplayAreaPolicy;void addWindowToken(IBinder binder, WindowToken token) {......// 放入集合mTokenMap.put(binder, token);if (token.asActivityRecord() == null) {......// 找到对应的位置挂载final DisplayArea.Tokens da = findAreaForToken(token).asTokens();da.addChild(token);}}DisplayArea findAreaForToken(WindowToken windowToken) {// 根据type查找return findAreaForWindowType(windowToken.getWindowType(), windowToken.mOptions,windowToken.mOwnerCanManageAppTokens, windowToken.mRoundedCornerOverlay);}DisplayArea findAreaForWindowType(int windowType, Bundle options,boolean ownerCanManageAppToken, boolean roundedCornerOverlay) {// 应用类型if (windowType >= FIRST_APPLICATION_WINDOW && windowType <= LAST_APPLICATION_WINDOW) {return mDisplayAreaPolicy.getTaskDisplayArea(options);}// 输入法窗口if (windowType == TYPE_INPUT_METHOD || windowType == TYPE_INPUT_METHOD_DIALOG) {return getImeContainer();}// 其他类型return mDisplayAreaPolicy.findAreaForWindowType(windowType, options,ownerCanManageAppToken, roundedCornerOverlay);}
状态栏不属于应用窗口,走后面的逻辑,DisplayAreaPolicy是个接口,真正的实现是DisplayAreaPolicyBuilder的内部类Result
# DisplayAreaPolicyBuilderstatic class Result extends DisplayAreaPolicy {final BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc;......@Overridepublic DisplayArea.Tokens findAreaForWindowType(int type, Bundle options,boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {return mSelectRootForWindowFunc.apply(type, options).findAreaForWindowTypeInLayer(type,ownerCanManageAppTokens, roundedCornerOverlay);}......}mSelectRootForWindowFunc是一个存放RootDisplayArea的map,所以后续逻辑在RootDisplayArea中// 根据type 找到在容器树的位置, 如果是应用或者输入法都走不到这# RootDisplayArea// 这个就是层级树的private DisplayArea.Tokens[] mAreaForLayer;@NullableDisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens,boolean roundedCornerOverlay) {// 获取到typeint windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,ownerCanManageAppTokens, roundedCornerOverlay);if (windowLayerFromType == APPLICATION_LAYER) {throw new IllegalArgumentException("There shouldn't be WindowToken on APPLICATION_LAYER");}// 根据type查找对应的位置return mAreaForLayer[windowLayerFromType];}
getWindowLayerFromTypeLw会根据type找到对应的层级,返回一个int。
然后根据这个值去mAreaForLayer拿到对应的DisplayArea.Tokens,将系统窗口的WindowToken挂载进去
mAreaForLayer其实就是开始构建层级树的那个集合。
2.2.1 mAreaForLayer的赋值
在开机构建窗口层级树的逻辑,最后会执行到RootDisplayArea::onHierarchyBuilt将层级树的集合传递出去。
# DisplayAreaPolicyBuilder.HierarchyBuilderprivate final RootDisplayArea mRoot;private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {......// 层级树的构建// 通知根节点已经完成了所有DisplayArea的添加 (将displayAreaForLayer保存在RootDisplayArea成员变量roomAreaForLayer中,供后面逻辑使用)mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);}
RootDisplayArea下的mAreaForLayer变量赋值
# RootDisplayAreaprivate DisplayArea.Tokens[] mAreaForLayer;void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas) {if (mHasBuiltHierarchy) {throw new IllegalStateException("Root should only build the hierarchy once");}mHasBuiltHierarchy = true;mFeatures = Collections.unmodifiableList(features);// 赋值mAreaForLayer = areaForLayer;mFeatureToDisplayAreas = featureToDisplayAreas;}
所以mAreaForLayer保存了层级树各个层级的对象因此根据index可以获取到对应的DisplayArea.Tokens,并执行系统窗口WindowToken的挂载。
相关文章:

【Android 13源码分析】WindowContainer窗口层级-3-实例分析
在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析,整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…...

Redis常用操作及springboot整合redis
1. Redis和Mysql的区别 数据模型:二者都是数据库,但是不同的是mysql是进行存储到磁盘当中,而Redis是进行存储到内存中. 数据模型 : mysql的存储的形式是二维表而Redis是通过key-value键值对的形式进行存储数据. 实际的应用的场景: Redis适合于需要快速读写的场景&…...
动态规划day34|背包理论基础(1)(2)、46.携带研究材料(纯粹的01背包)、416. 分割等和子集(01背包的应用)
动态规划day34|背包理论基础(1)(2)、46.携带研究材料、416. 分割等和子集 背包理论基础(1)——二维背包理论基础(2)——一维46.携带研究材料(卡码网 01背包)1. 二维背包2. 一维背包 …...
pytorch优化器
在反向传播计算完所有参数的梯度后,还需要使用优化方法更新网络的权重和参数。例如,随机梯度下降法(SGD)的更新策略如下: weight weight - learning_rate * gradient 手动实现如下: learning_rate 0.01 …...

必备工具,AI生成证件照,再也不用麻烦他人,电子驾驶证等多种证件照一键生成
最近有一个生成证件照的开源项目很火,今天我们来学习一下。之前我生成证件照都是线下去拍照,线上使用也是各种限制,需要付费或看广告,而且效果也不是很理想, 今天要分享的这个 AI 证件照生成工具可以一键可以生成一寸…...

深度解析 MintRich 独特的价格曲线机制玩法
随着 Meme 币赛道的迅速崛起,NFT 市场也迎来了新的变革。作为一个创新的 NFT 发行平台,Mint.Rich 正掀起一场全民参与的 NFT 热潮。其简易的操作界面和独特的价格曲线设计,让任何人都能以极低的门槛发行和交易自己的 NFT,从而参与…...

实时数仓3.0DWD层
实时数仓3.0DWD层 DWD层设计要点:9.1 流量域未经加工的事务事实表9.1.1 主要任务9.1.2 思路9.1.3 图解9.1.4 代码 9.2 流量域独立访客事务事实表9.2.1 主要任务9.2.2 思路分析9.2.3 图解9.2.4 代码 9.3 流量域用户跳出事务事实表9.3.1 主要任务9.3.2 思路分析9.3.3 …...

路径规划 | 基于A*算法的往返式全覆盖路径规划的改进算法(Matlab)
目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 基于A*算法的往返式全覆盖路径规划的改进算法 matlab实现代码 往返式全覆盖路径规划,通过建立二维栅格地图,设置障碍物,以及起始点根据定义往返式路径规划的定义的优先级运动规则从…...

QT 串口上位机读卡显示
目录 一. QT创建工程 二. 软件更换图标 三. QT打包 一. QT创建工程 文件新建,选择创建一个桌面QT。 重命名RFID,并选择工程保存路径 RFID.pro QT core gui serialport #串行串口greaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET RFID TE…...

Chrome谷歌浏览器登录账号next无反应
文章目录 问题描述 我们的Chrome浏览器在更新之后,会出现登录谷歌账号的时候,当你输入你的谷歌邮箱之后,点击 n e x t next next,也就是下一步的时候,页面没有反应,也就是没有跳转到输入密码的页面。 分析 根据logs里…...

Android相关线程基础
线程基础 进程与线程 进程:可以被看做是程序的实体, 是系统进行资源分配和调度的基本单位. 线程:是操作系统调度的最小单元, 也叫轻量级进程 使用多线程的优点 可以减少程序的响应时间。如果某个操作很耗时, 能够避免陷入长时间的等待, 从而有着更好的交互性. 线程较之进…...

uniapp 如何自定义导航栏并自适应机型
如今的移动设备有各种不同的屏幕形状,如刘海屏、水滴屏等。这些异形屏会影响页面的布局,尤其是导航栏和底部栏的显示。通过获取安全区域信息,可以确保页面内容不会被异形屏的特殊区域遮挡。 在设计页面顶部导航栏时,可以根据 saf…...

Java高级Day43-类加载
117.类加载 静态和动态加载 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强 动态加载:运行时加载需要的类,如果运行时不用该类…...
【LeetCode 算法笔记】155. 最小栈
目录 问题描述单个栈实现双栈实现不开辟额外空间 问题描述 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop()…...

面试题 05.01. 插入
目录 一:题目: 二:代码: 三:结果: 一:题目: 给定两个整型数字 N 与 M,以及表示比特位置的 i 与 j(i < j,且从 0 位开始计算)。…...
稠密向量检索、稀疏向量检索、BM25检索三者对比
在当今的信息检索领域,随着人工智能和自然语言处理技术的发展,稠密向量检索和稀疏向量检索成为了两种主要的研究方向。稠密向量检索依托于高维空间中的向量表示,能够捕捉文档的深层语义信息,而稀疏向量检索则侧重于关键词的匹配&a…...
UEFI学习笔记(六):EDK II 模块:Libraries,DriversApplication
UEFI学习笔记(六):EDK II Modules:Libraries,Application&Drivers 一、模块(Modules)的概念1、Library模块2、Application模块3、Driver模块4、Application和Driver的区别 二、EDK II 实现U…...
详解 Pandas 的透视表函数
Pandas 的透视表函数主要为 pivot() 和 pivot_table(),主要的功能为对 DataFrame 的行和列进行重新组合来重塑数据。 一、pivot 函数 pivot 函数只能对数据进行重塑,不能进行聚合 1. 数据准备 import pandas as pddf1 pd.DataFrame({department_id: […...

基于python+django+vue的农业管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的农…...

动态内存管理之malloc,free,calloc和realloc函数
Hello,各位小伙伴们,小编在这里祝福各位中秋佳节快乐呀,今天让我们来学习一下动态内存管理吧! 引言 像我们之前在开辟一段空间的时候你可能会使用整型变量来申请一块空间,或者使用数组来申请一段连续的空间ÿ…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...

通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
JS设计模式(5): 发布订阅模式
解锁JavaScript发布订阅模式:让代码沟通更优雅 在JavaScript的世界里,我们常常会遇到这样的场景:多个模块之间需要相互通信,但是又不想让它们产生过于紧密的耦合。这时候,发布订阅模式就像一位优雅的信使,…...

【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理
这篇学习笔记是Spring系列笔记的第7篇,该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记,供自己和他人参考。 Spring学习笔记目录 笔记1:【SSM】Spring基础: IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…...

【自然语言处理】大模型时代的数据标注(主动学习)
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构D 实验设计E 个人总结 A 论文出处 论文题目:FreeAL: Towards Human-Free Active Learning in the Era of Large Language Models发表情况:2023-EMNLP作者单位:浙江大…...