Android11 窗口动画

窗口进入动画
应用端窗口绘制完成之后,调用finshDraw告知WMS,WMS这边最后就会调用WindowSurfacePlacer的performSurfacePlacement方法,最终调用到 WindowStateAnimator的commitFinishDrawingLocked方法

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.javaboolean commitFinishDrawingLocked() {//省略mDrawState = READY_TO_SHOW;//READY_TO_SHOW状态boolean result = false;final ActivityRecord activity = mWin.mActivityRecord;if (activity == null || activity.canShowWindows()|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {result = mWin.performShowLocked();}return result;}

对于系统窗口,直接调用WindowState的performShowLocked 方法

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
boolean performShowLocked() {//省略mWinAnimator.applyEnterAnimationLocked();//动画mWinAnimator.mDrawState = HAS_DRAWN;//HAS_DRAWN状态//省略
}

applyEnterAnimationLocked

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
void applyEnterAnimationLocked() {//省略final int transit;if (mEnterAnimationPending) {mEnterAnimationPending = false;transit = WindowManagerPolicy.TRANSIT_ENTER;//走这个分支} else {transit = WindowManagerPolicy.TRANSIT_SHOW;}// We don't apply animation for application main window here since this window type// should be controlled by AppWindowToken in general.if (mAttrType != TYPE_BASE_APPLICATION) {applyAnimationLocked(transit, true);//动画}//省略}

最后调用applyAnimationLocked去播放动画。

窗口退出动画
应用端remove,导致WMS的removeWindow被调用

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javavoid removeWindow(Session session, IWindow client) {synchronized (mGlobalLock) {WindowState win = windowForClientLocked(session, client, false);if (win != null) {win.removeIfPossible();return;}// Remove embedded window map if the token belongs to an embedded windowmEmbeddedWindowController.remove(client);}}

removeIfPossible

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
@Overridevoid removeIfPossible() {super.removeIfPossible();removeIfPossible(false /*keepVisibleDeadWindow*/);immediatelyNotifyBlastSync();}private void removeIfPossible(boolean keepVisibleDeadWindow) {//省略
if (wasVisible) {final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;//transit 为TRANSIT_EXIT // Try starting an animation.if (mWinAnimator.applyAnimationLocked(transit, false)) {mAnimatingExit = true;//省略}
}
//省略

和窗口进入动画一样,也是调用WindowStateAnimator的applyAnimationLocked去处理动画相关的工作,只是传入的参数不一致。窗口进入时,transit为TRANSIT_ENTER,退出时,transit为TRANSIT_EXIT

applyAnimationLocked

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
boolean applyAnimationLocked(int transit, boolean isEntrance) {//省略if (mWin.mToken.okToAnimate()) {int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);//对于系统窗口,返回0Animation a = null;if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {//省略    } else {//进这个分支switch (transit) {case WindowManagerPolicy.TRANSIT_ENTER:attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;break;case WindowManagerPolicy.TRANSIT_EXIT:attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;break;case WindowManagerPolicy.TRANSIT_SHOW:attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;break;case WindowManagerPolicy.TRANSIT_HIDE:attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;break;}if (attr >= 0) {a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(mWin.mAttrs, attr, TRANSIT_NONE);//加载动画}}if (a != null) {if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");mWin.startAnimation(a);//开始动画Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);mAnimationIsEntrance = isEntrance;}//省略
}

首先根据不同的transit去加载不同的XML文件并创建对应的Animation 对象,然后调用WindowState的startAnimation 开始动画

//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
void startAnimation(Animation anim) {//省略final DisplayInfo displayInfo = getDisplayInfo();anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),displayInfo.appWidth, displayInfo.appHeight);anim.restrictDuration(MAX_ANIMATION_DURATION);anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());final AnimationAdapter adapter = new LocalAnimationAdapter(new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,0 /* windowCornerRadius */),mWmService.mSurfaceAnimationRunner);//创建LocalAnimationAdapterstartAnimation(getPendingTransaction(), adapter);commitPendingTransaction();}

首先创建LocalAnimationAdapter对象,LocalAnimationAdapter对象中的mSpec成员是WindowAnimationSpec对象,WindowAnimationSpec对象中的mAnimation成员是上一步创建的Animation 对象。LocalAnimationAdapter对象中的mAnimator指向的是WMS中的mSurfaceAnimationRunner。创建好LocalAnimationAdapter对象后,继续调用startAnimation处理

//frameworks/base/services/core/java/com/android/server/wm/WindowState.javaprivate void startAnimation(Transaction t, AnimationAdapter adapter) {startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION);}

调用父类WindowContainer的startAnimation方法

//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,@AnimationType int type,@Nullable OnAnimationFinishedCallback animationFinishedCallback) {// TODO: This should use isVisible() but because isVisible has a really weird meaning at// the moment this doesn't work for all animatable window containers.mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,mSurfaceFreezer);}void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,@AnimationType int type) {startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);}

SurfaceAnimator.startAnimation

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,@AnimationType int type,@Nullable OnAnimationFinishedCallback animationFinishedCallback,@Nullable SurfaceFreezer freezer) {cancelAnimation(t, true /* restarting */, true /* forwardCancel */);mAnimation = anim;//mAnimation 就为之前创建的LocalAnimationAdapter对象mAnimationType = type;mAnimationFinishedCallback = animationFinishedCallback;final SurfaceControl surface = mAnimatable.getSurfaceControl();//mAnimatable为窗口的WindowStatemLeash = freezer != null ? freezer.takeLeashForAnimation() : null;if (mLeash == null) {mLeash = createAnimationLeash(mAnimatable, surface, t, type,mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,0 /* y */, hidden, mService.mTransactionFactory);//创建leash图层mAnimatable.onAnimationLeashCreated(t, mLeash);}mAnimatable.onLeashAnimationStarting(t, mLeash);mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);//继续处理}

首先创建leash图层,然后调用LocalAnimationAdapter的startAnimation继续处理,注意传入的mInnerAnimationFinishedCallback参数,后续动画完成后会回调里面的方法。高版本的动画都是基于leash图层来做的

createAnimationLeash

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,Transaction t, @AnimationType int type, int width, int height, int x, int y,boolean hidden, Supplier<Transaction> transactionFactory) {if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");final SurfaceControl.Builder builder = animatable.makeAnimationLeash().setParent(animatable.getAnimationLeashParent())//将leash图层挂载到WindowState父亲的图层下.setName(surface + " - animation-leash")// TODO(b/151665759) Defer reparent calls// We want the leash to be visible immediately because the transaction which shows// the leash may be deferred but the reparent will not. This will cause the leashed// surface to be invisible until the deferred transaction is applied. If this// doesn't work, you will can see the 2/3 button nav bar flicker during seamless// rotation..setHidden(hidden).setEffectLayer().setCallsite("SurfaceAnimator.createAnimationLeash");final SurfaceControl leash = builder.build();t.setWindowCrop(leash, width, height);t.setPosition(leash, x, y);t.show(leash);t.setAlpha(leash, hidden ? 0 : 1);t.reparent(surface, leash);//将windowState的图层挂载在leash下return leash;}

注意这这只是对surface图层的关系进行操作,WMS这边的层级树是没有改变的。此时,图层的关系如下:
在这里插入图片描述
LocalAnimationAdapter.startAnimation

//frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java@Overridepublic void startAnimation(SurfaceControl animationLeash, Transaction t,@AnimationType int type, OnAnimationFinishedCallback finishCallback) {mAnimator.startAnimation(mSpec, animationLeash, t,() -> finishCallback.onAnimationFinished(type, this));//前面说过,mAnimator为SurfaceAnimationRunner对象,mSpec为WindowAnimationSpec对象}

SurfaceAnimationRunner.startAnimation

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,Runnable finishCallback) {synchronized (mLock) {final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,finishCallback);//创建RunningAnimation对象mPendingAnimations.put(animationLeash, runningAnim);//放入map中,注意map中也放入了leash图层if (!mAnimationStartDeferred) {mChoreographer.postFrameCallback(this::startAnimations);//请求vsync}//省略}}

又创建了一个RunningAnimation对象,RunningAnimation中的mAnimSpec为WindowAnimationSpec对象,这个WindowAnimationSpec对象中是有之前创建的Animation 对象的。然后将RunningAnimation对象保存到mPendingAnimations Map中。然后请求vsync信号。当vsync信号来了的时候,调用startAnimations方法

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
private void startAnimations(long frameTimeNanos) {synchronized (mLock) {startPendingAnimationsLocked();}mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
}@GuardedBy("mLock")private void startPendingAnimationsLocked() {for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {//遍历mPendingAnimationsstartAnimationLocked(mPendingAnimations.valueAt(i));}mPendingAnimations.clear();}

startAnimationLocked就是去执行动画的播放了。

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@GuardedBy("mLock")private void startAnimationLocked(RunningAnimation a) {final ValueAnimator anim = mAnimatorFactory.makeAnimator();//创建ValueAnimator // Animation length is already expected to be scaled.anim.overrideDurationScale(1.0f);anim.setDuration(a.mAnimSpec.getDuration());anim.addUpdateListener(animation -> {synchronized (mCancelLock) {if (!a.mCancelled) {final long duration = anim.getDuration();long currentPlayTime = anim.getCurrentPlayTime();if (currentPlayTime > duration) {currentPlayTime = duration;}applyTransformation(a, mFrameTransaction, currentPlayTime);//播放动画}}// Transaction will be applied in the commit phase.scheduleApplyTransaction();//请求下一个vsync信号});//省略

applyTransformation

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);}

WindowAnimationSpec.apply

//frameworks/base/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@Overridepublic void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {final TmpValues tmp = mThreadLocalTmps.get();tmp.transformation.clear();mAnimation.getTransformation(currentPlayTime, tmp.transformation);tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);t.setAlpha(leash, tmp.transformation.getAlpha());boolean cropSet = false;if (mStackClipMode == STACK_CLIP_NONE) {if (tmp.transformation.hasClipRect()) {t.setWindowCrop(leash, tmp.transformation.getClipRect());cropSet = true;}} else {mTmpRect.set(mStackBounds);if (tmp.transformation.hasClipRect()) {mTmpRect.intersect(tmp.transformation.getClipRect());}t.setWindowCrop(leash, mTmpRect);cropSet = true;}// We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,// since it doesn't have anything it's relative to.if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {t.setCornerRadius(leash, mWindowCornerRadius);}}

可以看出播放动画就是对leash图层进行操作。

以一张图来总结下窗口动画的播放流程
在这里插入图片描述
leash图层的移除
在播放动画时,创建了leash图层,动画结束后需要移除该图层。
在上面SurfaceAnimationRunner的startAnimationLocked方法中,除了有动画更新的回调,当动画结束时也是有回调的

private void startAnimationLocked(RunningAnimation a) {//省略anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {synchronized (mCancelLock) {if (!a.mCancelled) {// TODO: change this back to use show instead of alpha when b/138459974 is// fixed.mFrameTransaction.setAlpha(a.mLeash, 1);}}}@Overridepublic void onAnimationEnd(Animator animation) {//动画结束synchronized (mLock) {mRunningAnimations.remove(a.mLeash);synchronized (mCancelLock) {if (!a.mCancelled) {// Post on other thread that we can push final state without jank.mAnimationThreadHandler.post(a.mFinishCallback);}}}}});//省略
}

动画结束时,post了一个Runnable,了解了前面动画的播放流程的话,就知道这个Runnable是 SurfaceAnimator的 mInnerAnimationFinishedCallback,mInnerAnimationFinishedCallback的定义如下

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
SurfaceAnimator(Animatable animatable,@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,WindowManagerService service) {//省略mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}private OnAnimationFinishedCallback getFinishedCallback(@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {return (type, anim) -> {synchronized (mService.mGlobalLock) {final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);if (target != null) {target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);return;}if (anim != mAnimation) {return;}final Runnable resetAndInvokeFinish = () -> {// We need to check again if the animation has been replaced with a new// animation because the animatable may defer to finish.if (anim != mAnimation) {return;}final OnAnimationFinishedCallback animationFinishCallback =mAnimationFinishedCallback;reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);//移除leash//省略};// If both the Animatable and AnimationAdapter requests to be deferred, only the// first one will be called.if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)|| anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {resetAndInvokeFinish.run();//执行上面的Runnable }}};}

可以看出,动画结束时,调用reset去移除leash图层

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
private void reset(Transaction t, boolean destroyLeash) {mService.mAnimationTransferMap.remove(mAnimation);mAnimation = null;mAnimationFinishedCallback = null;mAnimationType = ANIMATION_TYPE_NONE;if (mLeash == null) {return;}SurfaceControl leash = mLeash;mLeash = null;final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);if (scheduleAnim) {mService.scheduleAnimationLocked();}}

removeLeash

//frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,boolean destroy) {boolean scheduleAnim = false;final SurfaceControl surface = animatable.getSurfaceControl();//得到windowstate的图层final SurfaceControl parent = animatable.getParentSurfaceControl();//windowstate对应父节点的图层// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.// Note that we also set this variable to true even if the parent isn't valid anymore, in// order to ensure onAnimationLeashLost still gets called in this case.final boolean reparent = surface != null;if (reparent) {if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);// We shouldn't really need these isValid checks but we do// b/130364451if (surface.isValid() && parent != null && parent.isValid()) {t.reparent(surface, parent);//将windowstate的图层重新挂载到父节点的图层下scheduleAnim = true;}}if (destroy) {t.remove(leash);//移除leash图层scheduleAnim = true;}if (reparent) {// Make sure to inform the animatable after the surface was reparented (or reparent// wasn't possible, but we still need to invoke the callback)animatable.onAnimationLeashLost(t);scheduleAnim = true;}return scheduleAnim;}

所以,在动画播放和结束的流程中,图层有以下变化
在这里插入图片描述

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

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

相关文章

OJhelper一款帮助你获取各大oj信息的软件

项目地址 应用功能 目前应用支持&#xff1a;查询、自定义、收藏各大oj比赛信息&#xff0c;跳转比赛界面。查询各大oj的Rating分以及题量&#xff0c;查看题量饼状图。 应用环境 windows和安卓端 应用预览&#xff1a; 维护概况 后期会提供持续更新&#xff0c;具体可以…

7.9数据结构

思维导图 作业 doubleloop.h #ifndef __DOUBLELOOP_H__ #define __DOUBLELOOP_H__#include <stdio.h> #include <stdlib.h>typedef int datatype; typedef struct node {union{int len;datatype data;};struct node *pri;//前驱指针struct node *next;//后继指针…

全终端自动化测试框架wyTest

突然有一些觉悟&#xff0c;程序猿不能只会吭哧吭哧的低头做事&#xff0c;应该学会怎么去展示自己&#xff0c;怎么去宣传自己&#xff0c;怎么把自己想做的事表述清楚。 于是&#xff0c;这两天一直在整理自己的作品&#xff0c;也为接下来的找工作多做点准备。接下来…

Linux | 安装lb-toolkits 1.2.4库

Linux | 安装 lb-toolkits 最近又需要下载葵花的数据&#xff0c;之前分享过一次代码。今天发现之前的环境不小心被我删了&#xff0c;而运行相关的代码需要安装lb-toolkits这个库&#xff0c;今天正好记录了一下安装lb-toolkits的过程。 这里安装的版本是1.2.4&#xff0c;别…

53-4 内网代理6 - frp搭建三层代理

前提:53-3 内网代理5 - frp搭建二级代理-CSDN博客 三级网络代理 在办公区入侵后,发现需要进一步渗透核心区网络(192.168.60.0/24),并登录域控制器的远程桌面。使用FRP在EDMZ区、办公区与核心区之间建立三级网络的SOCKS5代理,以便访问核心区的域控制器。 VPS上的FRP服…

压缩感知3——重构算法正交匹配追踪算法

算法流程 问题的实质是&#xff1a;AX Y 求解&#xff08;A是M维&#xff0c;Y是N维且N>>M并且稀疏度K<M&#xff09;明显X有无穷多解&#xff0c;重构过程是M次采样得到的采样值升维的过程。OMP算法的具体步骤&#xff1a;(1)用X表示信号&#xff0c;初始化残差e0 …

计算给定数字的阶乘

1 问题 计算给定数字的阶乘. 2 方法 使用while循环。使用for循环。使用函数。 通过实验、实践等证明提出的方法是有效的&#xff0c;是能够解决开头提出的问题。 代码清单 1 使用while循环numberint(input(请输入一个数字:))factorial1i1while i<number: factorialfactor…

【论文速读】| JADE:用于大语言模型的基于语言学的安全评估平台

本次分享论文&#xff1a;JADE : A Linguistics-based Safety Evaluation Platform for Large Language Models 基本信息 原文作者&#xff1a;Mi Zhang, Xudong Pan, Min Yang 作者单位&#xff1a;Whitzard-AI, System Software and Security Lab Fudan University 关键…

『大模型笔记』GraphRAG:用于复杂数据发现的新工具现已在GitHub上发布

GraphRAG:用于复杂数据发现的新工具现已在GitHub上发布 文章目录 一. GraphRAG:用于复杂数据发现的新工具现已在GitHub上发布1. 评估和结果2. 研究见解和未来方向二. 参考文献一. GraphRAG:用于复杂数据发现的新工具现已在GitHub上发布 下载 GraphRAG今年早些时候,我们介绍…

倒计时 2 周!CommunityOverCode Asia 2024 IoT Community 专题部分

CommunityOverCode 是 Apache 软件基金会&#xff08;ASF&#xff09;的官方全球系列大会&#xff0c;其前身为 ApacheCon。自 1998 年以来&#xff0c;在 ASF 成立之前&#xff0c;ApacheCon 已经吸引了各个层次的参与者&#xff0c;在 300 多个 Apache 项目及其不同的社区中探…

百度文心4.0 Turbo开放,领跑国内AI大模型赛道!

百度文心4.0 Turbo开放&#xff0c;领跑国内AI大模型赛道&#xff01; 前言 文心一言大模型 就在7月5日&#xff0c;在2024世界人工智能大会 (WAIC) 上&#xff0c;百度副总裁谢广军宣布文心大模型4.0 Turbo正式向企业客户全面开放&#xff01;这一举动直接引发了业界的关注。那…

springboot中@bean注解的创建和使用

bean的创建顺序 在Spring Boot中&#xff0c;当一个配置类&#xff08;使用Configuration注解的类&#xff09;中定义了多个bean时&#xff0c;这些bean的创建顺序并不完全由它们在类中的声明顺序决定。Spring框架在创建和管理bean时&#xff0c;遵循了复杂的依赖注入和生命周…

在Windows中使用开源高性能编辑器Zed(持续更新)

简介 “Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter. It’s also open source.” “Zed是一款高性能的支持多人协作的代码编辑器&#xff0c;由Atom和Tree-sitter的创建者开发。它也是开源的。” Zed主打“高性能”&…

为什么选择开放式耳机?悠律凝声环开放式耳机体验

开放式耳机相对于其他传统耳机优势有以下几点&#xff1a; 配戴舒适性更好&#xff1a;由于开放式耳机的背面是开放的&#xff0c;空气可以自由流动&#xff0c;减少了耳朵的闷热感。长时间佩戴时更加舒适。&#xff0c;而传统入耳式耳机一般都是塞入耳道&#xff0c;久戴会胀…

Java中的 this 关键字是什么意思? this() 又是什么?

目录 问题问题一&#xff1a;什么是this关键字?问题二&#xff1a;什么是this()&#xff1f; 问题 问题一&#xff1a;什么是this关键字? 定义&#xff1a;this 代表当前对象。这个定义比较抽象&#xff0c;举例来回答。 思考一个问题&#xff1a;如果没有 this 会怎样&…

鸿蒙开发学习笔记2

一、class 类 类是用于 创建对象模版。同时类声明也会引入一个 新类型&#xff0c;可定义其 实例属性、方法 和 构造函数。 // 类名 首字母大写&#xff08;规范&#xff09; class 类名 {// 1、实例属性&#xff08;字段&#xff09;// 2、构造函数// 3、方法 }1、属性&…

如何追踪ping连接中的所有路由器的数量和IP

如何快速判断ping连接经过的路由器个数和IP&#xff1f; 方法一&#xff1a; ping命令会返回一个TTL&#xff0c;TTL&#xff08;Time To Live&#xff09;存活时间&#xff0c;一般初始值为64&#xff0c;每经过一个路由器就减一&#xff0c;当TTL为0时丢弃网络包&#xff0…

“Numpy数据分析与挖掘:高效学习重点技能“

目录 # 开篇 # 补充 zeros & ones eye 1. numpy数组的创建 1.1 array 1.2 range 1.3 arange 1.4 常见的数据类型 1.5 astype 1.6 random.random() & round 2. numpy数组计算和数组计算 2.1 reshape 2.2 shape 2.3 将一维数组变成多维数组 2.4 指定一维…

0010基于免疫遗传算法的配送中心选址

免疫优化算法&#xff08;Immune Optimization Algorithm, IOA&#xff09;在物流配送中心选址中的应用是通过模拟免疫系统的进化过程来解决选址优化问题。物流配送中心选址问题涉及到如何在给定区域内选择最优的位置&#xff0c;以最大化服务覆盖并最小化运输成本。 免疫优化…

我的FPGA

1.安装quartus 2.更新usb blaster驱动 3.新建工程 1.随便找一个文件夹&#xff0c;里面新建demo文件夹&#xff0c;表示一个个工程 在demo文件夹里面&#xff0c;新建src&#xff08;源码&#xff09;&#xff0c;prj&#xff08;项目&#xff09;&#xff0c;doc&#xff…