【Android14 ShellTransitions】(八)播放动画

在这里插入图片描述

书接上回,话说当WMCore部分走到了Transition.onTransactionReady,计算完参与动画的目标,构建出TransitionInfo后,接下来就把这个包含了动画参与者的TransitionInfo发给了WMShell,然后就该播放动画了,这部分在WMShell。

1 TransitionPlayerImpl.onTransitionReady

在这里插入图片描述

TransitionPlayerImpl是ITransitionPlayer的本地实现,这里继续调用了Transitions.onTransitionReady方法,但要注意的是,这里并不是直接调用了Transitions.onTransitionReady,而是将这个调用操作放到了主线程中执行,那么这里多多少少会有一些延迟,更甚者,如果SystemUI主线程在做耗时操作,那么就会更晚走到Transitions.onTransitionReady,这影响的是这里的传参,Transaction类型的“t”,即之前的文章所提到的“start transaction”的apply的时间,严重点就会引发ANR,关于这一点当我们分析到“start transaction”被apply的时候再聊聊。

2 Transitions.onTransitionReady

在这里插入图片描述

这个方法也简单,主要看下这个局部变量activeIdx,之前在:

【Android14 ShellTransitions】(五)启动Transition这一节的内容涉及WMCore以及W - 掘金

这一文的分析中,主要是在Transitions.requestStartTransition中:

在这里插入图片描述

WMShell侧根据从WMCore侧传来的Transition对象的token,创建了一个与Transition对象一一对应的ActiveTransition对象,并且将这个传过来的token保存到了ActiveTransition.mToken中,然后将这个ActiveTransition对象保存到了Transitions.mPendingTransitions中。

现在回到Transitions.onTransitionReady,当WMCore侧再次把Transition的token发过来的时候,我们就可以根据这个token,从Transitions.mPendingTransitions中取到对应的ActiveTransition对象。

另外看到这里也把一些传参保存到了ActiveTransition的各个成员变量中,后续就直接从ActiveTransition中拿,而不是由Transitions保存,因为Transitions可能要同时处理多个Transition/ActiveTransition。

另外就如这里的注释所说,ActiveTransition发生了改变,“Move from pending to ready”,也就是说当ActiveTransition创建后就一直是pending的状态,当WMCore侧走到Transition.onTransactionReady,即Transition就绪的时候,再通知WMShell说WMCore这边就绪了,该你这边了,然后ActiveTransition就从等待状态切换到就绪状态(虽然没有一个状态值来表明ActiveTransition所处的状态)。

最后我们只分析最普通的单个Transition的情况,看到这里继续调用了Transitions.dispatchReady方法。

3 Transitions.dispatchReady

在这里插入图片描述

1)、为ActiveTransition分配一个Track,然后将该ActiveTransition添加到Track.mReadyTransitions。

看下Track类的定义:

在这里插入图片描述

Track用来播放动画,其成员变量mReadyTransitions保存了处于ready状态但是还没有轮到播放的ActiveTransition,它的存在说明了Track是可以收集多个ActiveTransition来并行播放动画的,成员变量mActiveTransition代表了正在播放动画的那个ActiveTransition。

2)、调用Transitions.setupStartState方法,该方法如其名字所表达的,用来设置动画初始状态的可见性、透明度和变换。

3)、继续调用Transitions.processReadyQueue。

这里看下Transitions.setupStartState方法:

在这里插入图片描述

这个方法是用来设置参与动画的一些SurfaceControl的初始状态的,其实也没啥好看的,唯一需要注意的是这里会对动画参与者的SurfaceControl进行一些设置,保证了一些动画的基本逻辑能够得到满足,就比如说对于TRANSIT_OPEN和TRANSIT_TO_FRONT类型的动画参与者,这里会强制它们在动画开始的时候显示,此外它会重置SurfaeControl的缩放和旋转等属性,如果你之前因为一些特殊需求改了SurfaceControl的缩放比例,那么就要注意这里不要让你的修改失效了。同样的对于TRANSIT_CLOSE和TRANSIT_TO_BACK类型的动画参与者,这里的逻辑则是保证了该动画参与者在动画结束后会被隐藏。

4 Transitions.processReadyQueue

在这里插入图片描述

1)、在我们的分析流程中,上一步Transitions.dispatchReady向Track.mReadyTransitions添加了就绪的ActiveTransition,所以这里Track.mReadyTransitions不为空。

2)、由于之前我们没有设置过Track.mActiveTranstion,那么这里我们会将Track.mReadyTransitions队首的那个ActiveTransition对象取出来,赋值给Track.mActiveTranstion,然后从Track.mReadyTransitions中移除,接着为ActiveTransition对象调用Transitions.playTransition,那么这个ActiveTransition会从ready状态变为playing状态,最后再调用一次Transitions.processReadyQueue。

3)、当再进入Transitions.processReadyQueue后,如果Track.mReadyTransitions仍然不为空,那么说明此前Track至少有两个ready的ActiveTransition,并且首个进入Transitions.processReadyQueue的那个ActiveTransition已经变为playing了,那么会尝试将剩下的这个ready的ActiveTransition和这个playing的ActiveTransition的动画合并,不过这部分我了解的很少,暂时跳过。

继续看Transitions.playTransition。

5 Transitions.playTransition

在这里插入图片描述

分为三个部分:

1)、Transitions.setupAnimHierarchy用来在动画开始前,将动画参与者reparent到共同的父Layer上,然后设置它们的Z轴层级。

2)、之前我们在Transitions.requestStartTransition中,尝试为每一个TransitionHandler调用handleRequest,来找到能处理当前ActiveTransition的那个TransitionHandler,如果能够找到,那么将这个TransitionHandler保存在ActiveTransition.mHandler中。这里便是调用这个TransitionHandler的startAnimation方法,让这个TransitionHandler来优先处理当前ActiveTransition,如果能够处理,那么就会返回true,这个ActiveTransition就算找到可以托付终生的handler了。这里也能看到,真正决定Transition被谁处理的是TransitionHandler.startAnimation,TransitionHandler.handleRequest只是给你一个优先处理的机会。

3)、如果TransitionHandler.startAnimation返回false,那还得继续调用Transitions.dispatchTransition来遍历Transitions.mHandlers中的所有TransitionHandler,继续找可以处理当前ActiveTransition的TransitionHandler。

接下来分别介绍。

5.1 Transitions.setupAnimHierarchy

在这里插入图片描述

Transitions.setupAnimHierarchy用来在动画开始前,将动画参与者reparent到一个共同的父Layer上,然后设置它们的Z轴层级。

我这里是从Launcher打开Message,截图为:

在这里插入图片描述

能看到在TaskDisplayArea下创建了一个名为“Transition Root:…”的Layer,作为动画参与者的共同parent。

然后是设置层级的逻辑,主要是根据:

  • “global transit type”,即TransitionInfo的类型。
  • “their transit mode”,即Change的mode。
  • “their destination z-orde”,即Change的目标Z轴层级(如果有的话)。

举个例子,比如上面的从Launcher打开Message的例子:

1)、TransitionInfo的类型为TRANSIT_TO_FRONT。

2)、Launcher对应的Change的mode为TRANSIT_TO_BACK,Messsage对应的Change的mode为TRANSIT_TO_FRONT。

对于这个场景,我们更想突出的应该是Message的打开动画,因此Message对应的Change的Z轴层级应该调高,而Launcher对应的Change的Z轴层级则应该调低,就像代码里面标黄的那样。

动画参与者是两个Task,那么这里的zSplitLine就是3,numChanges是2,Message的Task先被添加,因此i=0,Launcher的Task的i=1。

最终算出给Message的Task#11设置的Z轴层级为5:

在这里插入图片描述

Launcher的Task#1设置的Z轴层级为2:

在这里插入图片描述

因此Message盖在了Launcher之上,突出的是Message的动画。

5.2 TransitionHandler.startAnimation

在这里插入图片描述

翻译一下注释:startAnimation用来启动一个过渡动画。对于某一个特定的Transition,如果该TransitionHandler的handleRequset方法返回一个non-null的值的话,那么该TransitionHandler的startAnimation将总是会被调用。否则,只有当排在该TransitionHandler之前的其它TransitionHandler都没有办法处理Transition的时候,该TransitionHandler的startAnimation才会被调用。

这段注释也即Transitions.playTransition方法内容的描述。

实现该接口的TransitionHandler子类有一二十个:

在这里插入图片描述

作用于不同场景下的过渡动画,这里只分析一个典型的DefaultTransitionHandler。

在这里插入图片描述

我们之前分析的场景是从Launcher启动Message,由于现在从Launcher启动App的动画一般都是自定义的,所以走的并不是DefaultTransitionHandler,而是RemoteTransitionHandler,所以为了想要DefaultTransitionHandler处理调起Message的动画(App切换),我这里通过adb命令从Launcher调起Message。

5.2.1 设置回调Runnable

在这里插入图片描述

设置一个Runnable,该Runnable将在动画结束的时候执行(后续分析会看到,这里先提一嘴),执行的则又是传参finishCallback的onTransitionFinished方法,再看我们分析的流程下,TransitionHandler.startAnimation方法被调用的两处地方,分别在Transitions.playTransition和Transitions.dispatchTransition,得知:

在这里插入图片描述

最终调用的是Transitions.onFinish方法。

5.2.2 加载动画

在这里插入图片描述

调用DefaultTransitionHandler.loadAnimation方法来加载动画样式:

在这里插入图片描述

如果对之前AppTransition和AppTransitionController中的动画逻辑有印象的话,那么看到这里应该会很熟悉,这里差不多就是AppTransition.loadAnimation,主要就是根据动画的类型,以及是否有自定义动画来选择动画的样式。

其中自定义动画一般由App通过ActivityOptions的各种makeXXXAnimation方法指定:

在这里插入图片描述

自定义动画还是少数,大部分情况下动画则是TRANSIT_OPEN、TRANSIT_TO_FRONT、TRANSIT_CLOSE以及TRANSIT_TO_BACK这几类,并且是没有自定义动画的,如我们分析的用adb调起Message App的场景,那么会继续调用TransitionAnimationHelper.loadAttributeAnimation来选择动画样式:

在这里插入图片描述

这里的逻辑一看就懂,分的也非常细,根据Wallpaper的可见性是否发生变化,是否有透明的App参与,参与者是Task还是Activity之类的,都有有相应的动画可以选择。比如我们分析的场景,启动的是Message App,那么TransitionInfo的type为TRANSIT_TO_FRONT,并且Message对应的Change的mode也为TRANSIT_TO_FRONT,那么最终选择的动画就是R.styleable.WindowAnimation_taskToFrontEnterAnimation。

最后要注意我们这里拿到的只是一个资源ID,最终还是要通过TransitionAnimation.loadDefaultAnimationAttr来将这个资源ID转化为Animation对象,大概过一下:

在这里插入图片描述

方法调用顺序为:

TransitionAnimation.loadDefaultAnimationAttr

-> TransitionAnimation.loadAnimationAttr

-> TransitionAnimation.loadAnimationAttr

-> TransitionAnimation.loadAnimationSafely

继续调用了AnimationUtils.loadAnimation方法:

在这里插入图片描述

方法调用顺序为:

AnimationUtils.loadAnimation

-> AnimationUtils.createAnimationFromXml

-> AnimationUtils.createAnimationFromXml

看到最终是调用了AnimationUtils.createAnimationFromXml方法,从指定的package中根据对应资源ID来解析相应的xml文件,然后将xml文件中的不同的标签解析成对应的Animation类型,这里看到有多个标签,“alpha”、“scale”、“rotate”和“translate”等等。

比如我们刚刚提到的,在我们分析的场景下,选取的动画资源ID为R.styleable.WindowAnimation_taskToFrontEnterAnimation。

它声明在”frameworks/base/core/res/res/values/attrs.xml“中:

在这里插入图片描述

定义在”frameworks/base/core/res/res/values/styles.xml“:

在这里插入图片描述

然后它的动画对应的就是”frameworks/base/core/res/res/anim/task_open_enter.xml“:

在这里插入图片描述

标签为”translate“,那么会解析为一个TranslateAnimation动画对象。

最后有一点要注意的就是,由于在我们分析的流程下,我们没有自定义动画,因此加载的是默认包名:

private static final String DEFAULT_PACKAGE = "android";

“android“下的动画,即google的原生动画。

如果我们有自定义动画,比如我用Activity.overridePendingTransition自定义Activity的进入和退出动画,就像:

        mPendingEnterRes = R.anim.edge_extension_right;mPendingExitRes = R.anim.alpha_0;overridePendingTransition(mPendingEnterRes, mPendingExitRes);

那么后续动画使用的就是我在”R.anim.edge_extension_right“和”R.anim.alpha_0“中定义的动画:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"><alpha android:interpolator="@android:interpolator/linear" android:duration="10000" android:fillBefore="true" android:fillAfter="true"android:startOffset="0" android:fromAlpha="1" android:toAlpha="1" android:fillEnabled="true"/><scale android:interpolator="@android:interpolator/linear" android:duration="10000" android:startOffset="0"android:fromXScale="0.5" android:toXScale="0.5" android:fromYScale="1" android:toYScale="1"/>
</set>

这些动画的资源文件保存在”app\src\main\res\anim\alpha_0.xml“:

在这里插入图片描述

但是要注意的是我这里的自定义动画并不属于远程动画,因为我没有指定一个RemoteTransition,看log,最终处理动画的还是DefaultTransitionHandler:

在这里插入图片描述

像Launcher这样的调用ActivityOptions.makeRemoteAnimation传入一个RemoteAnimationAdapter和RemoteTransition的才是:

在这里插入图片描述

log为:

在这里插入图片描述

这里不对远程动画进行展开。

5.2.3 构建作用于leash的animator

在这里插入图片描述

DefaultTransitionHandler.startAnimation创建了一个局部变量animations,用来收集动画参与者的animator,当我们在第二步通过DefaultTransitionHandler.loadAnimation为动画参与者创建了动画后,接下来就是调用DefaultTransitionHandler.buildSurfaceAnimation来创建相应的animator:

在这里插入图片描述

我们主要关注创建的这个ValueAnimator。

1)、调用ValueAnimator.addUpdateListener为这个ValueAnimator添加一个监听每一帧进行更新的ValueAnimator.AnimatorUpdateListener,在每一帧到来的时候,调用DefaultTransitionHandler.applyTransformation来对leash进行更新。

2)、DefaultTransitionHandler.applyTransformation这个方法也很简单,这里不再贴代码,主要是根据当前动画的播放时间来计算Transformation,然后对相应的leash(动画参与者的SurfaceControl)进行设置,从而完成每一帧的SurfaceControl更新。

3)、调用ValueAnimator.addUpdateListener为这个ValueAnimator添加一个监听动画生命周期的AnimatorListenerAdapter,这里主要是在动画正常结束或者异常取消的时候,调用传参finishCallback的run方法,这将调用DefaultTransitionHandler.startAnimation的传参finishCallback的onTransitionFinished方法,最终将调用Transitions.onFinish方法,这个我们之前也有提过。

4)、构建了这个ValueAnimator,并且设置完动画监听器后,就把这个ValueAnimator对象添加到DefaultTransitionHandler.startAnimation创建的这个局部变量animations中。

5.2.4 应用”start transaction“

在这里插入图片描述

这一步很简单,即调用”start transaction“的Transaction.apply方法,但是我们需要着重强调一下。

1)、回看:

【Android14 ShellTransitions】(七)Transition就绪ShellTransitions - 掘金

在1.2小节显示SurfaceControl中,由于这里我们使用的Transaction是通过WindowContainer.getSyncTransaction方法得到的,因此这个Transaction中收集到的Surface的数据何时会被应用,是由Transition动画流程决定的。

2)、再根据:

【Android14 ShellTransitions】(六)SyncGroup完成BLASTSyncEngine Sy - 掘金

后续当SyncGroup完成后,在SyncGroup.finishNow中,所有动画参与者的SyncTransaction都会被合并到一个名为merge的Transaction对象中,接着这个Transaction对象会被传入Transition.onTransactionReady中, 也就是我们所说的”start transaction“。

3)、后续走到WMShell的Transitions.onTransitionReady后,这个”start transaction“被保存在了ActiveTransition.mStartT中。

4)、直到此时,构建完animation以及animator,马上就要开始动画的时候,”start transaction“才会被apply。

这是很长的一段路,比如我们从Launcher启动Message的流程,Message的Surface真正显示的时间点并不是WindowSurfaceController.showRobustly,而是动画即将开始之前的这里。不过这似乎是没办法的事,如果Surface过早显示,然后由于SystemUI主线程卡顿,结果在Surface显示两三秒后又开始动画了,那不是更奇怪。

现在再回看分析TransitionPlayerImpl.onTransitionReady时我提到的,如果SystemUI主线程卡顿,那么从Binder线程切换到主线程就需要非常多的时间,也就是动画流程被延迟了,那么这也将极大的延迟”start transaction“被应用的时间,导致”start transaction“的内容无法被及时提交到SurfaceFlinger。

比如在我们分析的Launcher启动Messag场景中,”start transaction“中包含着对Message的Surface进行Transaction.show的设置,”start transaction“被延迟应用就意味着Message的Surface被延迟显示,如果超过5s还没应用,那么就有可能会引发无焦点窗口的ANR(在上层WMS窗口焦点已经切换到Message,但是在SF层Message的Layer由于没有显示因此在InputDispatcher侧无法作为焦点窗口),这种情况下一般还会伴随着以下log:

在这里插入图片描述

”sent“花费的时间不到1s,但是”finished“的时候总共用时5s以上,当时有这个log不能说一定是SystemUI主线程卡顿,只能说嫌疑比较大。

因此这类ANR问题,发生的瓶颈不在于App绘制速度,而在于SystemUI主线程卡顿。由于现在ShellTransitions的动画播放部分放到SystemUI进程处理,而SystemUI进程本身也有很多常驻窗口要显示,因此这对SystemUI的性能提出了过高的要求。

如果没有了解现在的动画流程的话,应该也想不到SystemUI主线程卡顿是此类ANR问题的原因吧,哈哈哈…(google你是真爱折腾啊,某种植物!)

5.2.5 开始动画

在这里插入图片描述

分析了那么久,正菜终于上了,其实也很简单,就调用了Animator.start方法来启动动画而已,一直以来都是这么干的,没啥好讲的,这就好比在厨房洗切炒叮铃咣啷的,一顿操作猛如虎,上桌一看吃红薯。

我这里把动画的生命周期打印了调用堆栈,大概看下:

1)、onAnimationStart:

在这里插入图片描述

2)、onAnimationEnd:

在这里插入图片描述

当动画结束的时候,回调的是Transitions.onFinish方法。

5.3 Transitions.dispatchTransition

在分析Transitions.onFinish之前,我们还剩最后一点关于Transitions.dispatchTransition的内容没讲:

在这里插入图片描述

根据之前Transitions.playTransition的内容,如果ActiveTransition.mHandler无法处理当前ActiveTransition的话,那么我们就要给Transitions.mHandlers中的其它TransitionHandler一个机会,看它们能不能处理这个ActiveTransition了,内容也很简单,不再赘述,唯一需要注意的是这里遍历的顺序和TransitionHandler添加的顺序相反:

在这里插入图片描述

如我这里的log,标蓝色的是添加的log,标黄的是遍历的log,可以看到DefaultMixedHandler是最后一个添加的,但是遍历的时候是先调用的DefaultMixedHandler的startAnimation方法。

6 Transitions.onFinish

在这里插入图片描述

这个方法的内容也不多:

1)、这里是”finish transaction“被apply的地方。

2)、调用WindowOrganizer.finishTransition,最终重新回到系统系统WMCore走Transition的finish流程。

3)、由于当前Transition的动画已经结束,那么继续调用Transitions.processReadyQueue来重新看看有没有准备好的Transition可以开始播放动画的。

下一篇文章继续分析Transition的finish流程。

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

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

相关文章

git快速合并代码dev->master

需求&#xff1a; 日常开发都是在dev分支进行开发&#xff0c;但是dev代码开发测试完成后&#xff0c;需要将dev代码合到master主分支上 开始合并代码&#xff1a; 一、场景 一个代码仓库&#xff0c;包含两个分支&#xff0c;一个是master&#xff0c;另一个是dev&#xff1b…

gitblit 学习-hook功能

hook功能 hook是什么 git hooks是git提供的&#xff0c;在发生特定事件时&#xff0c;允许用户添加自定义代码&#xff08;或操作&#xff09;的方式。 就像Vue中组件的生命周期钩子&#xff0c;比如&#xff0c;你想在vue组件创建后输出一行log, 你可能会这么写 有什么用&a…

Android Input的流程和原理

Android Input事件机制 Android系统是由事件驱动的&#xff0c;而Input是最常见的事件之一&#xff0c;用户的点击、滑动、长按等操作&#xff0c;都属于Input事件驱动&#xff0c;其中的核心就是InputReader和InputDispatcher。InputReader和InputDispatcher是跑在system_serv…

Jmeter基础篇(19)JSR223预处理器

前言 JSR223预处理器是Apache JMeter中的一个组件&#xff0c;它允许用户使用任何支持Java Scripting API (JSR 223) 的脚本语言来执行预处理任务。这个功能非常强大&#xff0c;因为它让测试人员能够利用如Groovy、JavaScript&#xff08;Nashorn引擎&#xff09;、BeanShell…

轻松实现金蝶与旺店通数据无缝对接的完整解决方案

【金蝶】采购入库单对接【旺店通】委外入库单_合并 在企业信息化系统中&#xff0c;数据的高效流转和准确对接是确保业务顺畅运行的关键。本文将分享一个实际案例&#xff0c;展示如何通过轻易云数据集成平台实现金蝶云星空与旺店通企业奇门之间的数据无缝对接。具体方案为&am…

基于SpringBoot的汽车票网上预订系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

前端好用的网站分享——CSS(持续更新中)

1.CSS Scan 点击进入CSS Scan CSS盒子阴影大全 2.渐变背景 点击进入color.oulu 3.CSS简化压缩 点击进入toptal 4.CSS可视化 点击进入CSS可视化 这个强推&#xff0c;话不多说&#xff0c;看图! 5.Marko 点击进入Marko 有很多按钮样式 6.getwaves 点击进入getwaves 生…

【K8S系列】Kubernetes 中 Service IP 分配 问题及解决方案【已解决】

在 Kubernetes 中&#xff0c;LoadBalancer 类型的 Service 允许用户轻松地将应用暴露给外部流量。它自动创建一个云负载均衡器并分配一个外部 IP 地址。然而&#xff0c;在某些情况下&#xff0c;LoadBalancer 类型的 Service 可能未能成功分配 IP 地址&#xff0c;导致外部无…

Tomcat servlet response关于中文乱码的经验

前言 最近修改老项目项目&#xff0c;使用zuul网关返回的中文内容乱码了&#xff0c;如果使用GBK或者GB2312编码确正常显示&#xff0c;稍微实验了一下&#xff0c;发现里面很多细节&#xff0c;毕竟Springboot对我们做了很多事情&#xff0c;而且当我们使用不同的模式会出现很…

微服务之间调用,OpenFeign传递用户(RequestInterceptor接口)

场景&#xff1a;微服务之黑马商城项目-登录拦截器在网关完成用户的校验&#xff0c;并将用户信息&#xff08;用户id&#xff09;存入请求头&#xff0c;假设将购物车里面的商品进行结算就会生成订单并清空购物车&#xff0c;这里涉及到了交易服务模块远程调用购物车模块&…

Java中String的length与Oracle数据库中VARCHAR2实际存储长度不一致的问题

目录 一、根本原因 二、解决方案 一、根本原因 Oracle数据库新增数据的时候报如下错误&#xff1a; 先给大家看个小案例&#xff0c;这样更好去理解&#xff0c;下面是一段测试代码&#xff1a; 这里面我分别列举了三种字符串&#xff0c;中文&#xff0c;英文和数字以及两种…

探索 CrewAI:引领多智能体协作的未来

探索 CrewAI&#xff1a;引领多智能体协作的未来 在人工智能领域&#xff0c;如何让多个智能体协同工作以解决复杂问题一直是一个热门话题。CrewAI 作为一个前沿的框架&#xff0c;正是为了解决这一挑战而生。它通过角色扮演和自主智能体的协作&#xff0c;赋予了智能体无缝合…

ViSual studio如何安装 并使用GeographicLib

在C的 Boost.Geometry、GDAL/OGR 和 GeographicLib。这些库都可以用于计算两个经纬度点之间的地面距离。 . Boost.Geometry 描述&#xff1a;Boost库的一部分&#xff0c;提供了几何计算功能&#xff0c;包括计算两点之间的地面距离。 优势&#xff1a;轻量级、易于集成到C项…

网站攻击,XSS攻击的类型

XSS&#xff08;跨站脚本&#xff09;攻击是一种网络安全攻击方式&#xff0c;攻击者通过在网站页面中注入恶意脚本&#xff0c;使脚本在其他用户的浏览器中执行&#xff0c;从而窃取用户信息、篡改页面内容或操控用户账户。这类攻击通常利用网站对输入数据的过滤不严格&#x…

接口测试加密了怎么测试

各位小伙伴&#xff0c;大家好&#xff0c;今天给大家带来的是接口测试加密了我们该怎么测试。 首先我们来了解一下什么是接口&#xff0c;在我们生活中&#xff0c;水管接口&#xff0c;管道接口等等&#xff0c;在我们软件中通常说的是客户端和服务端之间数据传输的接口。我…

尽可能连续的基于挤压的表面模型制造

&#x1f98c;&#x1f98c;&#x1f98c;宝子们好&#xff0c;今天我们来看一个关于外壳打印的路径优化问题。同样的&#xff0c;红色的是术语、橙色的是方法、绿色的是算法过程&#xff0c;这次多加了紫色&#xff0c;是文章的创新点或目标等。废话不多说&#xff0c;上论文 …

linux驱动—在自己的总线目录下创建属性文件

在总线目录下创建属性文件以扩展其功能。 通过创建属性文件&#xff0c; 我们可以为总线添加额外的信息和控制选项&#xff0c; 以便与设备和驱动进行交互。 简单就是&#xff0c;属性文件&#xff0c;可以完成用户空间和内核空间的数据交互&#xff0c; 比如在应用层快速修改g…

【Searxng】Searxng docker 安装

SearXNG将用户的查询请求分发至多个支持的搜索引擎&#xff0c;并收集返回的结果进行汇总处理。在这个过程中&#xff0c;它通过内置的过滤器功能屏蔽广告和其他不相关内容&#xff0c;确保搜索结果的纯净度。 一键部署 docker run \--name searxng \-p ????:8080 \-v ~/s…

gitlab不同账号间·仓库转移

背景&#xff1a;公司业务调整&#xff0c;原先在海外仓库的代码转移回国内 诉求&#xff1a;完整的保留项目记录 操作&#xff1a; 步骤一: 定位到需要迁移的原项目地址 步骤二&#xff1a;创建新项目 步骤三&#xff1a;打开命令行&#xff0c;创建好文件路径为需要clo…

Ubuntu 安装 npm

1. 升级apt sudo apt-get update 2. 安装nodejs sudo apt install nodejs 3. 安装npm sudo apt-get install npm 4. 查看版本 node -v npm -v 完成安装&#xff01;