【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流程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ELK + Filebeat + Spring Boot:日志分析入门与实践(二)

目录 一、环境 1.1 ELKF环境 1.2 版本 1.3 流程 二、Filebeat安装 2.1 安装 2.2 新增配置采集日志 三、logstash 配置 3.1 配置输出日志到es 3.2 Grok 日志格式解析 3.2 启动 logstash ​3.3 启动项目查看索引 一、环境 1.1 ELKF环境 springboot项目&#xff1a;w…

C#实现word和pdf格式互转

1、word转pdf 使用nuget&#xff1a; Microsoft.Office.Interop.Word winform页面&#xff1a; 后端代码&#xff1a; //using Spire.Doc; //using Spire.Pdf; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using Sy…

成都睿明智科技有限公司抖音电商服务的领航者

在这个短视频风起云涌的时代&#xff0c;抖音电商以其独特的魅力迅速崛起&#xff0c;成为无数商家争夺流量与销量的新战场。在这片红海之中&#xff0c;如何脱颖而出&#xff0c;实现销售额的飞跃&#xff1f;今天&#xff0c;就让我们一同走进成都睿明智科技有限公司&#xf…

力扣hot100-->递归/回溯

目录 递归/回溯 1. 17. 电话号码的字母组合 2. 22. 括号生成 3. 39. 组合总和 4. 46. 全排列 5. 78. 子集 递归/回溯 1. 17. 电话号码的字母组合 中等 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到…

快速遍历包含合并单元格的Word表格

Word中的合并表格如下&#xff0c;现在需要根据子类&#xff08;例如&#xff1a;果汁&#xff09;查找对应的品类&#xff0c;如果这是Excel表格&#xff0c;那么即使包含合并单元格&#xff0c;也很容易处理&#xff0c;但是使用Word VBA进行查找&#xff0c;就需要一些技巧。…

js 获取当前时间与前一个月时间

// 获取当前时间的毫秒数 var currentTimeMillis new Date().getTime();// 获取前一个月的Date对象 var dateLastMonth new Date(); dateLastMonth.setMonth(dateLastMonth.getMonth() - 1);// 获取前一个月的毫秒数 var timeMillisLastMonth dateLastMonth.getTime();conso…

C++之多态的深度剖析

目录 前言 1.多态的概念 2.多态的定义及实现 2.1多态的构成条件 2.1.1重要条件 2.1.2 虚函数 2.1.3 虚函数的重写/覆盖 2.1.4 选择题 2.1.5 虚函数其他知识 协变&#xff08;了解&#xff09; 析构函数的重写 override 和 final关键字 3. 重载&#xff0c;重写&…

【linux网络编程】| socket套接字 | 实现UDP协议聊天室

前言&#xff1a;本节内容将带友友们实现一个UDP协议的聊天室。 主要原理是客户端发送数据给服务端。 服务端将数据再转发给所有链接服务端的客户端。 所以&#xff0c; 我们主要就是要实现客户端以及服务端的逻辑代码。 那么&#xff0c; 接下来开始我们的学习吧。 ps:本节内容…

【PTA】4-2 树的同构【数据结构】

给定两棵树 T1​ 和 T2​。如果 T1​ 可以通过若干次左右孩子互换就变成 T2​&#xff0c;则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的&#xff0c;因为我们把其中一棵树的结点A、B、G的左右孩子互换后&#xff0c;就得到另外一棵树。而图2就不是同构的。 图一…

「Mac畅玩鸿蒙与硬件13」鸿蒙UI组件篇3 - TextInput 组件获取用户输入

在鸿蒙应用开发中,TextInput 组件用于接收用户输入,适用于文本、密码等多种输入类型。本文详细介绍鸿蒙 TextInput 组件的使用方法,包括输入限制、样式设置、事件监听及搜索框应用,帮助你灵活处理鸿蒙应用中的用户输入。 关键词 TextInput 组件用户输入输入限制事件监听搜索…

单元测试详解

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 为什么需要单元测试&#xff1f; 从产品角度而言&#xff0c;常规的功能测试、系统测试都是站在产品局部或全局功能进行测试&#xff0c;能够很好地与用户的需…

如何从PPT中导出600dpi的高清图

Step1. 修改PPT注册表 具体过程&#xff0c;参见如下链接&#xff1a;修改ppt注册表&#xff0c;导出高分辨率图片 Step2. 打开PPT&#xff0c;找到自己想要保存的图&#xff0c;选中图像&#xff0c;查看图像尺寸并记录 Step3. 重新新建一个PPT&#xff0c;并根据记录的图片…

「Mac畅玩鸿蒙与硬件7」鸿蒙开发环境配置篇7 - 使用命令行工具和本地模拟器管理项目

本篇将讲解在 macOS 上配置 HarmonyOS 开发环境的流程&#xff0c;聚焦 hvigorw 命令行工具的使用。我们将以创建 HelloWorld 项目为例&#xff0c;演示使用 hvigorw 进行项目构建、清理操作&#xff0c;并通过 DevEco Studio 的本地模拟器进行预览&#xff0c;帮助提升项目开发…

Linux基础—基础命令及相关知识5(ubuntu网络配置)

网络的配置方法 centos网络配置 centos的网卡位置 /etc/sysconfig/network-scripts/ifcfg-ens33(centos网卡文件) bootproto表示获得IP地址的方式是静态的还是动态 onboot表示启动系统时是否激活该网络接口 设置IP地址&#xff0c;子网掩码&#xff0c;网关&#xff0c;dns…

LabVIEW开发的控制阀监控与维护系统

LabVIEW开发一套自动测试软件&#xff0c;用于控制阀的实时监控、数据采集、维护管理以及报警通知。此系统的目标是通过便捷的操作界面、可靠的通信接口和高效的数据管理&#xff0c;为工厂设备管理提供全面的支持。 1. 项目需求 目标是实现一个控制阀管理系统&#xff0c;能够…

第7章 利用CSS和多媒体美化页面作业

2.用表格布局页面&#xff0c;利用CSS技术&#xff0c;及添加多媒体&#xff0c;制作并美化“心灵之音”页面。 浏览效果如下&#xff1a; 实例代码如下&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>心灵…

Python中的数据可视化:Matplotlib基础与高级技巧

Python中的数据可视化&#xff1a;Matplotlib基础与高级技巧 数据可视化是数据分析和数据科学中不可或缺的一部分。通过图表&#xff0c;我们可以更直观地观察数据的分布和趋势。Matplotlib作为Python最基础、也是最广泛使用的绘图库之一&#xff0c;不仅支持多种常用图表&…

‘cmd‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

报错描述&#xff1a; 我在使用python执行一个spark任务时&#xff0c;一直报如下错误&#xff0c;检查电脑上所有的环境变量后发现都配置正确&#xff0c;但还是一直报cmd 不是内部或外部命令&#xff0c;也不是可运行的程序这个错误&#xff0c;如果你也有这样的情况&#x…

【网络】传输层协议TCP

目录 四位首部长度 序号 捎带应答 标记位 超时重传机制 连接管理机制&#xff08;RST标记位&#xff09; 三次握手及四次挥手的原因 TCP的全称是传输控制协议&#xff08;Transmission Control Protocol&#xff09;&#xff0c;也就是说&#xff0c;对于放到TCP发送缓冲…

【优选算法篇】前缀之序,后缀之章:于数列深处邂逅算法的光与影

文章目录 C 前缀和详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;前缀和基础应用1.1 一维前缀和模板题解法&#xff08;前缀和&#xff09;图解分析C代码实现易错点提示代码解读题目解析总结 1.2 二维前缀和模板题解法&#xff08;二维前缀和&#xff09;图解分析C…