【Android14 ShellTransitions】(七)Transition就绪

news2024/11/15 21:34:35

Transition.onTransactionReady的内容比较长,我们挑重点的部分逐段分析(跳过的地方并非不重要,而是我柿子挑软的捏)。

1 窗口绘制状态的流转以及显示SurfaceControl

注意我们这里的SurfaceControl特指的是WindowSurfaceController的mSurfaceControl,如果对这个不是很了解的,可以回顾一下之前写的关于SurfaceControl的文章:

【基础】2、Surface的创建【Android 12】 - 掘金 (juejin.cn)

接着分析代码:

在这里插入图片描述

先跳过Transition.commitVisibleActivities,看到首先是将Transition.mState置为STATE_PLAYING,这意味着动画马上就要执行了。

然后是为Transition的两个成员变量mStartTransaction以及mFinishTransaction赋值,mFinishTransaction不用多说,看到mStartTransaction被赋值为传参transaction,传参即我们上一篇分析中的在SyncGroup.finishNow创建的一个Transaction,局部变量merged:

在这里插入图片描述

一个“start transaction”和一个“finish transaction”,我按照个人的理解,举个例子说明一下,如果我们从ActivityA上启动了一个ActivityB:

1)、对于ActivityA来说,它相关的SurfaceControl(准确一点说则是WindowSurfaceController.mSurfaceControl)需要在动画结束的时候再隐藏,如果它在动画开始前就隐藏,那么就无法看到ActivityA的动画效果了(向右平移退出或者淡出之类的动画)。

2)、对于ActivityB来说,它相关的SurfaceControl需要在动画开始的时候就显示出来,如果它在动画开始的时候还没有显示,那么同样也无法看到ActivityB的动画效果了(向右平移进入或者淡入之类的动画)。

从以上分析可知,ActivityA和ActivityB相关的SurfaceControl可见性变化的时机是不同的,那么这个行为通过一次Transacton.apply是无法做到的,所以就需要两个Transaction,即“start transaction”和“finish transaction”。“start transaction”在动画开始前调用apply,用于在动画开始执行前提前将ActivityB进行显示,“finish transaction”则是在动画结束的时候调用apply,用于在动画结束的时候再将ActivityA隐藏。

最重要的是要弄清楚“start transaction”和“finish transaction”这两个Transaction调用apply方法的时机,在以后的Transition流程中会分析到。

再来看Transition.commitVisibleActivities方法的内容:

在这里插入图片描述

如该方法的注释所说,当前Transition已经准备好执行动画了,这里先让“start transaction”把相关需要显示的SurfaceControl显示出来。

Transition.mParticipants是参与动画的WindowContainer集合,那么这个方法就是遍历这个集合:

1)、调用ActivityRecord.commitVisibility设置相关ActivityRecord的为可见。

2)、调用ActivityRecord.commitFinishDrawing进一步设置相关SurfaceControl为可见。

ActivityRecord.commitVisibility方法内容比较多,主要是用来ActivityRecord的可见性,即其成员变量mVisible,除此之外还有很多别的逻辑,但是和我们要分析的Transition内容无关,只需要知道这里设置了ActivityRecord的可见性即可,不去多说。我们主要看下ActivityRecord.commitFinishDrawing:

在这里插入图片描述

很简单,为每一个child,即WindowState调用commitFinishDrawing方法:

在这里插入图片描述

1)、调用WindowStateAnimator.commitFinishDrawingLocked方法,继续将窗口对应的WindowStateAnimator的mDrawState,即绘制状态进行流转。

2)、调用WindowStateAnimator.prepareSurfaceLocked,设置SurfaceControl的可见性。

这两个方法都比较重要,我们接下来分别进行分析。

1.1 窗口绘制状态的流转

SurfaceControl最终的显示和窗口的绘制状态密切相关,所以我感觉这里有必要看一下WindowStateAnimator.mDrawState这个状态是如何切换的,并且我自己对这个窗口的绘制状态也是不求甚解,也希望借着这个机会了解一下。

先分析一下代码,回头再试着总结一下。

·1.1.1 WindowStateAnimator.commitFinishDrawingLocked

在这里插入图片描述

首先将WindowState.mDrawState设置为READY_TO_SHOW。

然后如果当前WindowStateAnimator相关的WindowState满足以下条件之一,则继续调用WindowStateAnimator.performShowLocked:

1)、没有对应的ActivityRecord,即是一个非Activity窗口:

activity == null

2)、有对应的ActivityRecord,并且此时已经可以显示窗口了:

activity.canShowWindows()

在这里插入图片描述

重要是就是看这个ActivityRecord的mSyncState是不是SYNC_STATE_WAITING_FOR_DRAW,如果是这个值,那么就说明这个ActivityRecord是处于动画中的。

但是有一个问题是,ActivityRecord的mSyncState是不会被设置为SYNC_STATE_WAITING_FOR_DRAW的,只有WindowState才会,那岂不是每次走到这里判断ActivityRecord是否drawn,都将一直是true。

3)、是一个TYPE_APPLICATION_STARTING类型的窗口,即SplashScreen或者Snapshot:

mWin.mAttrs.type == TYPE_APPLICATION_STARTING

总而言之,如果这个WindowState满足了绘制了条件,那么将继续调用WindowState.performShowLocked。

1.1.2 WindowState.performShowLocked

在这里插入图片描述

如果WindowStateAnimator.mDrawState不是READY_TO_SHOW,那么返回false,否则将其置为HAS_DRAWN,并且返回true,这将使得我们可以下一步继续调用WindowStateAnimator.prepareSurfaces方法。

从WindowStateAnimator.commitFinishDrawingLocked以及WindowState.performShowLocked这两个方法都能看到,窗口的绘制状态是循序渐进的,必须是状态A -> 状态B -> 状态C,不存在状态A直接到状态C之类的。

1.1.3 窗口绘制状态小结

首先是mDrawState在WindowStateAnimator的定义,以及几个取值:

在这里插入图片描述

结合着Activity启动的一般流程,我大致总结一下:

1)、NO_SURFACE:当没有Surface的就置为这个状态。

这个很好理解,一般窗口销毁相关的流程,会将WindowStateAnimator.mDrawState设置为NO_SURFACE,比如:

WindowState.removeImmediately

-> WindowStateAnimator.destroySurfaceLocked

-> WindowStateAnimator.destroySurface

此阶段没有窗口,也没有Surface。

2)、DRAW_PENDING:当Surface被创建之后,窗口被添加但还没有开始绘制之前,就会置为这个状态。在这个时期,Surface是隐藏的。这表明Surface正等待应用程序绘制窗口的内容。

当窗口被添加,接着App侧开始走measure、layout以及draw流程,在draw之前,会将窗口在WMS侧进行relayout,经过:

WMS.relayoutWindow

-> WMS.createSurfaceControl

-> WindowStateAnimator.createSurfaceLocked

-> WindowStateAnimator.resetDrawState

会将WindowStateAnimator.mDrawState设置为DRAW_PENDING。

这个流程我们也很熟悉,即之前分析创建WindowSurfaceController的SurfaceControl的流程。

此阶段窗口被添加但还没绘制出来,SurfaceControl也是隐藏的。

3)、COMMIT_DRAW_PENDING:当窗口的绘制操作完成,但是这个Surface还没有显示出来之前,状态会设置为此值。这个Surface会在下次layout过程中显示出来。

当窗口绘制完成,App侧调用ViewRootImpl.reportDrawFinished后,就会调用IWindowSession的对端,经过:

Session.finishDrawing

-> WMS.finishDrawingWindow

-> WindowState.finishDrawing

-> WindowStateAnimator.finishDrawingLocked

会将WindowStateAnimator.mDrawState设置为COMMIT_DRAW_PENDING。

此阶段窗口已经绘制完成,但是Surface由于一些原因还不能显示。

4)、READY_TO_SHOW:这个状态标识窗口的绘制操作已经提交,但Surface还没有真正显示。在一组窗口(例如属于同一个应用的多个窗口)准备显示时,系统会使用这个状态来延迟显示Surface,直到所有相关窗口都准备好一起显示。

首先我们看到在动画的流程中,窗口的绘制状态被设置为READY_TO_SHOW的流程为:

Transition.onTransactionReady

-> Transition.commitVisibleActivities

-> ActivityRecord.commitFinishDrawing

-> WindowState.commitFinishDrawing

-> WindowStateAnimator.commitFinishDrawingLocked

结合注释,我个人的理解是,绘制状态被置为READY_TO_SHOW,表明此窗口已经绘制完了,可以准备显示它的SurfaceControl了,但是它的SurfaceControl需要等待和其它的SurfaceContrl一起显示,或者说等待动画走到特定阶段才能显示,因此我们这里推迟其SurfaceControl的显示时间,将窗口的绘制状态设置为READY_TO_SHOW。

如果不考虑和其它窗口一起显示,那么我想在这一步就可以将绘制状态设置为HAS_DRAWN了,即READY_TO_SHOW这个状态值是不必要的。

5)、HAS_DRAWN:当窗口首次在屏幕上显示时,就会设置为此状态。

这个值在WindowState.performShowLocked方法中被设置,紧跟着WindowStateAnimator.commitFinishDrawingLocked方法。

严谨一点的话注释的说法其实是不准确的,当窗口绘制状态被设置为HAS_DRAWN的时候,只是说明SurfaceControl接下来可以显示了,但是SurfaceControl仍然没有显示,屏幕上是看不见的。

6)、总结一下,从以上分析可知,这些状态值不只涉及了窗口的绘制流程,还涉及了SurfaceControl的显示流程:

  • NO_SURFACE:没窗口,也没SurfaceControl。
  • DRAW_PENDING:有窗口,但没开始绘制。有SurfaceControl,但不能显示。
  • COMMIT_DRAW_PENDING,窗口刚刚绘制完,SurfaceControl还不能显示。
  • READY_TO_SHOW:窗口已经绘制完了,SurfaceControl可以显示了,但没必要,再等等。
  • HAS_DRAWN:窗口已经绘制完了,SurfaceControl也可以显示了。

1.2 显示SurfaceControl

回到WindowState.commitFinishDrawing,在调用WindowStateAnimator.commitFinishDrawingLocked将窗口的绘制状态走完后,接下来就是调用WindowStateAnimator.prepareSurfaceLocked来显示SurfaceControl了。

注意这个方法被调用的地方有两处:

在这里插入图片描述

还有一处调用的地方在WindowState.prepareSurfaces,这个是更通用的流程,但是动画流程下,则稍微不同,即我们分析的这个流程。

看代码:

在这里插入图片描述

我们只看和显示SurfaceControl相关的部分:

1)、如果窗口不在屏幕上,则调用WindowStateAnimator.hide -> WindowSurfaceController.hide来隐藏SurfaceControl。

2)、如果窗口在屏幕上,那么进一步判断窗口的绘制状态,只有窗口的绘制状态为HAS_DRAWN,才能继续调用WindowSurfaceController.showRobustly来显示SurfaceControl:

在这里插入图片描述

关键的就那一句,调用Transaction.show来显示相关SurfaceControl,但是要注意的是这里并没有调用Transaction.apply,所以这个时候窗口还是没有显示。

窗口的最终显示则是和这个传参Transaction对象有关,这个Transaction对象则是之前说的”start transaction“,那么这个Transaction的apply方法的调用时机则是跟Transition的流程相关,以后的分析会看到。

2 计算动画目标

这一节的内容是调用Transition.calculateTargets来计算动画的目标:

在这里插入图片描述

Transition的成员变量mTargets定义为:

在这里插入图片描述

之前收集到的动画参与者提升后的最终的动画目标,也就是说最终执行动画的主体并非是之前收集到的动画参与者,而是这一步用动画参与者计算得到的动画目标。

Transition.calculateTargets的内容为:

在这里插入图片描述

大致的内容为:

1)、创建一个Transition.Targets类型的局部变量targets,来收集动画目标。

2)、遍历Transition.mParticipants,从Transition.mChanges中取出对应的ChangeInfo对象放到Transition.Targets.mArray中,但是跳过WindowState类型的动画参与者,以及跳过那些根据ChangeInfo.hasChanged得出前后没有发生变化的动画参与者。

3)、调用Transition.tryPromote尝试提升targets中保存的动画目标的级别。

我们这一节主要来看下这个Transition.tryPromote。

”promote“,提升的动画目标在WindowContainer层级结构中的级别,这个逻辑之前在AppTransitionController.getAnimationTargets也用到了,思想都是类似的。比如一个Task中有两个ActivityRecord,并且这两个ActivityRecord要分别执行一段动画,也就是动画执行的主体是ActivityRecord。如果这两个ActivityRecord刚好都想向左平移同样的距离,那么我们就不需要为这两个ActivityRecord分别应用一段平移的动画,而是直接将这个平移的动画应用到它们共同的父容器Task上,并且实现的效果是一样的。这也就是”promote“的含义,动画的目标主体从ActivityRecord”提升“到了更高一级的Task上。

接着看代码,Transition.tryPromote。

2.1 Transition.tryPromote

在这里插入图片描述

主要逻辑为遍历Targets.mArray中的每一个ChangeInfo对象,调用Transition.canPromote方法来判断他们是否能够提升为父容器。

1)、如果不能,直接跳过该ChangeInfo对象,判断下一个。

2)、如果能,就说明提升成功。此外还要调用Transition.reportIfNotTop来继续判断它是否是organized(我的理解就是这个WindowContainer是否是系统开机后自动创建的,不是需要的时候再去创建的)。如果不是,那么将当前WindowContainer对应的ChangeInfo从局部变量targets中移除,然后把它的父WindowContainer对应的ChangeInfo加如到targets中。如果是,那么在不移除当前WindowContainer对应的ChangeInfo的前提下,把它的父WindowContainer对应的ChangeInfo加如到targets中。这里应该是针对organized的WindowContainer的特殊处理,确保organized的WindowContainer的变化也能够报告到WMShell那边。

因此重点其实是Transition.canPromote逻辑。

2.2 Transition.canPromote

在这里插入图片描述

感觉这段代码还是比较重要的,我们逐行分析。

2.2.1 片段1

在这里插入图片描述

1)、对应WindowContainer.canCreateRemoteAnimationTarget方法,目前只有TaskDisplayArea、TaskFragment以及ActivityRecord会返回true,其它类型的WindowContainer都会返回false,也就是说父容器不是这几类的WindowContainer将无法得到提升,那么目前只有这几种提升:WindowState到ActivityRecord,ActivityRecod到TaskFragment,TaskFragment到TaskFragment(因为TaskFragment存在嵌套,比如Home类型的TaskFragment),以及TaskFragment到TaskDisplayArea。另外从Transition.calculateTargets的逻辑我们看到了执行动画的target至少是WindowToken这一级的,并且看收集的逻辑,似乎也没有看到过直接收集WindowState的,因此实际上提升只存在以下几种情况:

  • ActivityRecod到TaskFragment。
  • TaskFragment到TaskFragment。
  • TaskFragment到TaskDisplayArea。

2)、如果找不到父WindowContainer对应的ChangeInfo,则不提升,返回false。

3)、如果父WindowContainer有ChangeInfo,但是此时的状态和收集开始时的状态没有变化,则不提升,返回false。

2.2.2 片段2

在这里插入图片描述

1)、如果当前要提升的WindowContainer是Wallpaper类型的,则不提升,返回false。

2)、如果当前WindowContainer前后的父WindowContainer不一致,即发生reparent了,则不提升,返回false。

2.2.3 片段3

在这里插入图片描述

遍历父WindowContainer的所有子WindowContainer:

1)、如果姊妹WindowContainer在Transition.mChanges中找不到一个对应的ChangeInfo对象,或者有这么一个ChangeInfo对象,但是该ChangeInfo对象不在Targets.mArray中,这种情况一共可以理解为这个姊妹WindowContainer没有参与到本次动画,那么还需要继续判断:

----1.1)、如果该姊妹WindowContainer可见,那么就不提升,直接返回false,当前WindowContainer无法提升到父WindowContainer。毕竟该姊妹WindowContainer是没有参与到动画中的,并且是可见的,如果你提升了,那后续动画执行的时候用户不是会看到该姊妹WindowContainer跟着一起动了嘛,这肯定是不对的。

----1.2)、如果该姊妹WindowContainer不可见,那么就跳过对这个WindowContainer的检查。不可见的姊妹WindowContainer对于本次动画也没有太大影响,即使跟着一起进行动画用户也看不到,直接跳过检查下一个姊妹WindowContainer就好了。

2)、如果姊妹WindowContainer从Transition.mChanges中能找到一个对应的ChangeInfo对象,并且该ChangeInfo对象也在局部变量targets中,那么认为该姊妹WindowContainer也参与了本次动画,那么分别为他们的TransitionMode调用Transition.reduceMode方法来看它们动画的大方向是否是一致的,首先是根据ChangeInfo.getTransitMode拿到各自的TransitionMode:

在这里插入图片描述

TransitionMode定义在TransitionInfo中:

在这里插入图片描述

看到Transition模式其实就是定义在WindowManager中的Transition类型的子集。

ChangeInfo.getTransitMode的内容也比较简单:

TRANSIT_CHANGE:收集阶段的可见性和Transition就绪阶段的可见性没有发生变化。

TRANSIT_OPEN:存在发生了变化,且当前可见,即从无到有。

TRANSIT_CLOSE:存在发生了变化,且当前不可见,即从有到无。

TRANSIT_TO_FRONT:存在没有发生变化,且当前可见,说明从后台移动到了前台,从不可见变为了可见。

TRANSIT_TO_BACK:存在没有发生变化,且当前不可见,说明从前台移动到了后台,从可见变为了不可见。

再根据Transition.reduceMode的逻辑:

在这里插入图片描述

  • TRANSIT_TO_BACK和TRANSIT_CLOSE是一类的。
  • TRANSIT_TO_FRONT和TRANSIT_OPEN是一类的。
  • TRANSIT_CHANGE单独一类。

如果动画的大方向是一致的,那么即使TRANSIT_TO_BACK和TRANSIT_CLOSE的动画有点差别,但是为了大局考虑,各别同志也不是不能适当调整一下来实现集体上的一致。

如果动画的大方向都不一致,那么它们中的无论哪个肯定都是不能提升为它们的父容器的。比如TaskA想向左平移,TaskB想向右平移,那么如果擅自提升为父容器TaskDisplayArea,不管TaskDisplayArea向左还是向右平移肯定都不合适,这种矛盾就属于不可调和了,那父容器TaskDisplayArea就不用管了,也就是别提升了,让冲突的TaskA和TaskB自己玩去吧。

3)、最后总结一下检查姊妹WindowContainer的这段逻辑,其实就是检查所有的姊妹WindowContainer中,有没有和当前WindowContainer冲突的姊妹WindowContainer,至于是否冲突则看是否满足了以下条件之一:

  • 检查所有没有参与动画的姊妹WindowContainer,看能否找到一个可见的。
  • 检查所有参与了动画的姊妹WindowContainer,看能否找到一个动画的大方向和当前WindowContainer不一致。

只要找到了这么一个姊妹WindowContainer,我们就无法提升动画的主体。

3 构建TransitionInfo对象

在这里插入图片描述

这一节我感觉其实没有什么好说的,大概介绍一下TransitionInfo以及它的内部类Change。

在这里插入图片描述

1)、TransitionInfo,实现了Parcelable,结合注释,用来收集WMCore这边的Transition信息,用来同步给WMShell的TransitionPlayer。成员变量大概有这些:

在这里插入图片描述

2)、TransitionInfo.Change,同样实现了Parcelable,代表了WindowContainer在一个Transition期间的变化。看其成员变量,保存的信息还是挺多的,还有一个RunningTaskInfo的对象:

在这里插入图片描述

再结合Transition.calculateTransitionInfo方法,很明显就大概能弄懂这两个类的作用:

1)、TransitionInfo,对应一个Transition对象,用来收集WMShell感兴趣的Transition的信息,后续同步给WMShell。

2)、TransitionInfo.Change,对应一个Transition.ChangeInfo对象,用来收集WMShell感兴趣的Transition.ChangeInfo的信息,后续同步给WMShell。

顺便一提,google为啥不将Transition中ChangeInfo的命名为”Change“,将TransitionInfo中的Change命名为”ChangeInfo“呢,强迫症犯了。

4 Transition移动到PLAYING状态

在这里插入图片描述

其实在Transition.onTransactionReady方法的开头已经将Transition.mState状态置为STATE_PLAYING,这里又调用了一个TransitionController.moveToPlaying方法,看下是干啥的:

在这里插入图片描述

其实也非常简单:

1)、开始动画了,意味着当前Transition已经不能收集了,所以将TransitionController.mCollectingTransition置空。特别的,如果有其它Transition在排队,那么就继续将TransitionController.mCollectingTransition赋值为排队队列队首的那个Transition,我播我的动画,你收集你的WindowContainer,互不干扰。

2)、将当前Transition添加到TransitionController.mPlayingTransitions:

在这里插入图片描述

一个当前处于playing状态的Transition的队列,也就是说playing的Transition可以有多个。

5 切换到WMShell:onTransitionReady

在这里插入图片描述

在Transition.onTransactionReady方法的最后,调用了ITransitionPlayer.onTransitionReady方法将切换到了WMShell:

在这里插入图片描述

切换到WMShell意味着Transition就绪阶段已经结束,正式进入Transition的playing阶段,Transitions.TransitionPlayerImpl.onTransitionReady就是我们下一篇文章的起点。

最后稍微看一下调用ITransitionPlayer.onTransitionReady方法之前调用的Transition.buildFinishTransaction方法:

在这里插入图片描述

传入的Transaction对象为Transition.mFinishTransaction,如该方法的注释所说,这里对”finish transaction“的操作保证了动画结束后,所有的”reparent“操作或者是Layer的变化将会得到重置,特别是Layer的几何信息(位置、缩放、旋转这些)。如果你的Layer在动画结束的时候在Layer的这些信息上的确有变化,那就要注意不要让这个方法把你对Layer的操作重置了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Excel办公技巧:制作二级联动下拉菜单

分享制作二级联动下拉菜单的方法,即使数据有增删,菜单也能自动更新! 可以通过先定义名称,再结合数据验证,来做二级联动下拉菜单。 1. 准备数据 首先,我们需要准备好要进行二级联动下拉菜单的数据&#xff…

K8S 上部署 Emqx

文章目录 安装方式一:快速部署安装方式二:定制化部署1. 使用 Pod 直接部署 EMQX Broker2. 使用 Deoloyment 部署 Pod3. 使用 Services 公开 EMQX Broker Pod 服务4. 通过 kubernetes 自动集群 EMQX MQTT 服务器5. 修改 EMQX Broker 的配置6. 赋予 Pod 访…

共享自助台球厅系统,扫码开台,物联网开灯,智能计费

共享自助台球厅系统,扫码开台,物联网开灯,智能计费 含小程序,商家手机端和pc管理端 后端php 前端uniapp 纯开源 可定制 持续更新

常用的点云预处理算法

点云预处理是处理点云数据时的重要部分,其目的是提高点云数据的质量和处理效率。通过去除离群点、减少点云密度和增强特征,可以消除噪声、减少计算量、提高算法的准确性和鲁棒性,从而为后续的点云处理和分析步骤(如配准、分割和重…

实战打靶集锦-31-monitoring

文章目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 ssh服务4.2 smtp服务4.3 http/https服务 5. 系统提权5.1 枚举系统信息5.2 枚举passwd文件5.3 枚举定时任务5.4 linpeas提权 6. 获取flag 靶机地址:https://download.vulnhub.com/monitoring/Monitoring.o…

算法力扣刷题记录 四十九【112. 路径总和】和【113. 路径总和ii】

前言 二叉树篇继续。 记录 四十九【112. 路径总和】和【113. 路径总和ii】 一、【112. 路径总和】题目阅读 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 target…

VsCode远程ssh连接失败:Could not establish connection to XXX

一、问题描述 在VsCode中按下"F1",选择Remote-SSH:Connect to Host 选择一个已经配置好的SSH主机,比如我选择的是192.168.0.104: 结果提示:Could not establish connection to XXX 二、解决方法 观察VsCode的输出信息…

走进NoSql

一、引入 1.1什么是NoSql NoSQL(Not Only SQL)是一组非关系型数据库(或称为非SQL数据库)的统称,它们提供了与传统的关系型数据库不同的数据存储和检索方式。NoSQL数据库通常用于处理大量的、分布式的、非结构化或半结…

STM32使用Wifi连接阿里云

目录 1 实现功能 2 器件 3 AT指令 4 阿里云配置 4.1 打开阿里云 4.2 创建产品 4.3 添加设备 5 STM32配置 5.1 基础参数 5.2 功能定义 6 STM32代码 本文主要是记述一下,如何使用阿里云物联网平台,创建一个简单的远程控制小灯示例。 完整工程&a…

BurpSuit的intruder模块结果进行筛选和导出

文章目录 一、搭建的测试网站第一步 先抓去数据包,查看数据包第二步 可以控制返回信息一条一条的显示第三步 使用intrude模块进行遍历,每次只显示一条用户信息第四步 配置过滤规则第五步 查看结果显示第六步 进行数据导出第七步 查看导出的表格二、实际项目中使用免责声明一、…

PCIe驱动开发(3)— 驱动设备文件的创建与操作

PCIe驱动开发(3)— 驱动设备文件的创建与操作 一、前言 在 Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx” (xxx 是具体的驱动文件名字)的文件进行相应的操作即…

Azure Repos 仓库管理

从远端仓库克隆到本地 前提:本地要安装git,并且登录了账户 1.在要放这个远程仓库的路径下,打git 然后 git clone https://.. 如果要登录验证,那就验证下,点 generate git credentials,复制password 克隆完后,cd 到克隆的路径, 可以用 git branch -a //查看分…

Spring Cloud环境搭建

🎥 个人主页:Dikz12🔥个人专栏:Spring学习之路📕格言:吾愚多不敏,而愿加学欢迎大家👍点赞✍评论⭐收藏 目录 1. 开发环境安装 1.1 安装JDK ​1.2 安装MySQL 2. 案列介绍 2.1 …

Linux 命令 —— top命令(查看进程资源占用)

文章目录 top 命令显示信息介绍top 命令使用 top 命令显示信息介绍 top 命令是 Linux/Unix 系统中常用的进程监控工具,可以实时动态显示系统中各个进程的资源占用情况,包括CPU、内存等。 进入 linux 系统,直接输入 top,回车&…

全网超详细Redis主从部署(附出现bug原因)

主从部署 整体架构图 需要再建两个CentOs7,过程重复单机部署 http://t.csdnimg.cn/zkpBE http://t.csdnimg.cn/lUU5gLinux环境下配置redis 查看自己ip地址命令 ifconfig 192.168.187.137 进入redis所在目录 cd /opt/software/redis cd redis-stable 进入配置文件 vim redi…

书生大模型第三关-Git基础

1.任务1: 破冰活动:自我介绍 目标: 每位参与者提交一份自我介绍。 提交地址:https://github.com/InternLM/Tutorial 的 camp3 分支~ 行动: 首先Fork项目到自己Repo中,然后git clone在本地上 然后创建一个…

liunx面试题目

如何看当前Linux系统有几颗物理CPU和每颗CPU的核数? 查看物理cup: cat /proc/cpuinfo|grep -c ‘physical id’ 查看每颗cup核数 cat /proc/cpuinfo|grep -c ‘processor’ 若希望自动实现软件包的更新,可以使用yum-cron并启动该服务 yum -y …

【java计算机毕设】农产品仓库管理系统系统MySQL ssm JSP maven项目代码+文档 前后端一体 暑假作业

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】农产品仓库管理系统系统MySQL ssm vue maven项目代码文档 前后端一体 暑假作业 2项目介绍 系统功能: 农产品仓库管理包括管理员、用户俩种角色。 管理员功能包括个人中心模块用于修改个人信息和…

60K起?“软件安全岗”比“网络安全岗”薪资高在哪里?

在网络世界的江湖中,“软件安全”与“网络安全”这两大“武林高手”都肩负着守护数字领域和平的重任。不过,眼尖的小伙伴们可能发现了,软件安全岗位的薪资待遇往往比网络安全岗位要丰厚那么一些,这到底是为啥呢?今天&a…

【AI绘画教程】Stable Diffusion 1.5 vs 2

在本文中,我们将总结稳定扩散 1 与稳定扩散 2 辩论中的所有要点。我们将在第一部分中查看这些差异存在的实际原因,但如果您想直接了解实际差异,您可以跳下否定提示部分。让我们开始吧! Stable Diffusion 2.1 发布与1.5相比,2.1旨在解决2.0的许多相对缺点。本文的内容与理解…