Android Framework(八)WMS-窗口动效概述

文章目录

  • 动画简述
    • 本地、远端动画的定义
    • 什么是“leash”图层
    • “leash”图层的命令与创建
  • Winscope流程
    • 小结
  • 动画流程概览分析
  • Activity启动app_transition 动画的主要事件
    • 触发动画执行的套路
    • 动画真正执行
    • 动画的结束回调
    • 触发远端动画的Target

动画简述

  • 1、动画的原理也是利用了视觉停留原理,控制时间点“对象”显示,来组成动画效果。

  • 2、这个系列说的虽然是 framework 层的动画,但是本质上和应用内写动画一样的,要么是加载动画的 xml 文件,要么是通过 Animator 对象。

  • 3、所以后面的分析重点其实不在动画本身,更重要的动画的播放流程和时机。

  • 4、动画的目的,为了提升用户体验,有一个自然的过渡,所以他本身不应该干预业务逻辑,也就是说就算把动画这段代码移掉,业务逻辑也应该正在执行。

本地、远端动画的定义

对于 framework 层来说,动画类型可以分为2类:

  • 1、本地动画 (LocalAnimation)
  • 2、远端动画(RemoteAnimation)

本地和远端值得是播放动画是在哪个进程执行的,这个本地和远端的概念是相对的,不过既然是 framework 层的角度去分析,所以本地动画值得就是在 "system_service " 进程播放的动画。 而远端动画,指的就是在非 "system_service " 进程播放的动画。

这2个点还是挺重要的,比如当前场景视觉明显可见的点击图标后,图标开始放大铺满屏幕的动效,就是在 launcher 进程播放的,所以也是远端动画。

什么是“leash”图层

既然是动画,肯定是对某个“对象”不停的修改其相关属性来达到动画效果的,比如写 APP 的时候写的动画“对象”会是一个 View 。

而当前分析的 framework 层动画的“对象”则是一个 Surface (图层)。

但是前面说了,动画只是为了提升过渡体验不应该干预实际业务。
比如 APP 里有个很复杂的 View 需要做动画,避免对业务的干扰最好的方式就是在外面嵌套一个 View,然后对这个外面的 View 做动画就好了。

AOSP 的设计也确实如此,会创建一个 “leash” 图层,然后把需要做动画的图层挂载到其下面,再对 “leash” 图层做动画。
Leash 丢给百度翻译,解释为:(牵狗的)皮带。(瞬间就有了一个人用皮带牵着一条狗的画面了),其实这个就是 AOSP 单独为动画创建了一个图层,然后把需要做动画的容器图层挂载到这个图层下,那么就也有了动画效果。如果看到 leash 图层这个名词,指的就是做动画的那个图层了。

结合实际 leash 图层创建前后的层级关系看看:
leash图层创建前:

在这里插入图片描述
leash图层创建后:

在这里插入图片描述
对比可以看到要开始做动画的时候,Task 上面出现了一个name为 “animation-leash of app_transition ”的图层,这个就是 “leash”图层。
前面的 “animation-leash of” 是固定的,后面的 “ app_transition ” 是这次动画的类型这些后面都会看到代码的定义,目前有个了解即可。
画个图总结一下动画前后的图层改变:

在这里插入图片描述

这种设计符合六大原则的单一原则,需要动画就单独拿一个图层来做动画,动画结束后再恢复到原来的层级结构。

当前这只是举个例子,不是说每次 leash 图层都是在 Task 上的,这个是需要看具体情况的, AOSP 有个方法会对不同场景计算出这个 leash 图层需要创建在哪里。
比如远端动画就会对创建在 Taks 上面,对整个 Task 做动画,而 window_animation 一般就是在 WindowToken 或者 WindowState 上做动画。

“leash”图层的命令与创建

上面看到的图层命令,是需要通过看前面的Surface Name 才能确定是哪个窗口的图层,也就是出现在前面的 “Surface(name= XXX” 这个图层的名字,这个看窗口三部曲的时候提过,都是在 SurfaceControl::setName 进行设置的。

至于后面的 " - animation-leash of " 固定的,然后就是 “app_transition” ,这个是根据动画类型定义的,映射的方法如下:

# SurfaceAnimatorstatic String animationTypeToString(@AnimationType int type) {switch (type) {case ANIMATION_TYPE_NONE: return "none";case ANIMATION_TYPE_APP_TRANSITION: return "app_transition"; // 应用间切换动画case ANIMATION_TYPE_SCREEN_ROTATION: return "screen_rotation"; // 屏幕旋转动画case ANIMATION_TYPE_DIMMER: return "dimmer"; // 调光动画case ANIMATION_TYPE_RECENTS: return "recents_animation"; // 最近任务动画(没发现具体场景,不是从最近任务列表点击A)case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation"; // 窗口动画,比如窗口移除case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation"; // 插入动画,但是官方注释说这其实不是一个动画case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform"; // 动画类型转换case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal"; // 窗口要显示前的动画default: return "unknown type:" + type;}}

这段代码里看到是根据传入的type值返回一个字符串,看看使用的地方:

# SurfaceAnimatorstatic SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,Transaction t, @AnimationType int type, int width, int height, int x, int y,boolean hidden, Supplier<Transaction> transactionFactory) {// 自己加的堆栈android.util.Log.e("biubiubiu", "SurfaceAnimator  createAnimationLeash: "+animationTypeToString(type), new Exception());// 日志ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);final SurfaceControl.Builder builder = animatable.makeAnimationLeash().setParent(animatable.getAnimationLeashParent()) //设置父节点.setName(surface + " - animation-leash of " + animationTypeToString(type)) //命名.setHidden(hidden).setEffectLayer().setCallsite("SurfaceAnimator.createAnimationLeash");......return leash; // 返回leash图层}

这个方法后面还会单独详细分析,当前只看setName这一块,看得出来 Winscope 信息中的信息和这里的格式是匹配上的。前面是“surface”的名字,然后拼上一个 “animation-leash of” ,最后面就是根据type返回一个类型,比如"app_transition"。
这里也有响应的日志来说明创建了哪个窗口的 leash 图层。

Winscope流程

先使用 Winscope 工具观察图层的改变,提取关键点的截图如下:

在这里插入图片描述
这个是默认状态,点击图标后,就会有以下改变:
在这里插入图片描述
可以看到出现了3个动画

  • 1、壁纸的 window_animation
  • 2、launcher 对应 Task 的 app_transition(退出)
  • 3、“电话” 对应 Task 的 app_transition(打开)

这3个动画从 Winscope 看几户是同一帧出现的,稍后从日志上看,也几户是同时执行的。(所以不必纠结这3个的先后顺序)

这3个动画里,最关心的是 “电话” 的打开动画,可以在左边看到下部分已经有一个小矩形出现,结合右边点击的 Visiable 可以确定这快 surface 显示的可见内容其实是
Splash Screen 的 Window。(应用窗口这会还没添加上来。)

根据之前的源码,是先出现Task, 再挂载 ActivityRecord 然后出现 Splash Screen 。

后面一段都是动画执行的过程,主要是这个 Splash Screen 的内容是从小放大到全屏,这个和用户实际看到的视觉效果也是匹配的。

在这里插入图片描述

这里只是截取了其中的一个过程。

这个时候是动画执行的过程截取的图,可以看到 Splash Screen 的 window 已经很大了,即将铺满全屏。

等动画结束后(当前抓到的是3个动画在同一帧结束),就剩下“电话” 对应 Task 相关的图层了,其他的都不可见了。

在这里插入图片描述
在这里插入图片描述
这个时候出现了3个图层–动画的leash图层–窗口容器图层,以及窗口真正的显示图层。

所以可以知道这个 starting_reveal 动画是真正要显示内容前出现的:

从可见性上,starting_reveal 这个动画图层和 Splash Screen 是同级的,但是这个时候 Splash Screen 的窗口从容器顺序上是盖在 starting_reveal 图层上面的

然后就是执行一段时间的 starting_reveal 动画。这个动画结束后说明应用窗口已经要显示了,那么就需要移除 Splash Screen 了,于是开始StartWindow移除动画。

这只是我当前这次抓取的 Winscope 信息,不过不是每次都是这样的。 但是大致流程是一样的,具体的动画出现和结束的时机可能会有点区别,比如某一帧 window_animation 图层已经出现了,但是starting_reveal 图层还在,下一帧才移除。这种1,2帧的差距很正常。

最后的这个状态就是应用已经完全启动展示最后的 Activity 的样子了,其他的窗口都不可见了(最重要的是动画结束后Splash Screen 也移除了)。

在这里插入图片描述

小结

上面截取了各个关键节点的图,发现一共出现了5个动画,关于壁纸和 launcher 的可以先不关注,就启动的这个应用来说分为以下几步:

  • 1、有一个 app 打开的动画,app_transition ,这个时候显示的内容是并不是应用窗口,而是 Splash Screen 的这个 STtartWindow 。

  • 2、app_transition 动画结束后不久,应用窗口绘制后将要显示了,这个时候显示的是 starting_reveal 动画

  • 3、starting_reveal 动画结束后,开始的是移除 Splash Screen 的 window_animation 动画

  • 4、最终显示的是应用的窗口

在这里插入图片描述
不过也可能会有一两帧是几个通话同时存在的,所以也可能抓到的 Winscope 是下面这种图:

在这里插入图片描述

动画流程概览分析

前面的分析提过动画会创建 leash 图层,也就是会执行 SurfaceAnimator::createAnimationLeash 方法,我本地代码加上了堆栈。

然后需要过滤掉 “insets_animation” 类型的动画,因为官方注释也说了这个其实不是动画。然后上一节看 Winscope 也确实没看到 “insets_animation” 相关的图层。 过滤后可以得到下面这些日志:

biubiubiu: SurfaceAnimator  createAnimationLeash: app_transition
biubiubiu: SurfaceAnimator  createAnimationLeash: app_transition
biubiubiu: SurfaceAnimator  createAnimationLeash: window_animation
biubiubiu: SurfaceAnimator  createAnimationLeash: starting_reveal
biubiubiu: SurfaceAnimator  createAnimationLeash: window_animation

看到依次有这5个动画的创建,这个和上面分析的 Winscope 看到的也是对应上了。

这5个动画具体的体现,需要在开发者选项放慢动画时间,才能看的比较清楚,整理了一下对应的动画效果如下:

SurfaceAnimator  createAnimationLeash: app_transition     dialer Task 》dialer的打开动画     从小放大
SurfaceAnimator  createAnimationLeash: app_transition     home   Task 》launcher的关闭动画   略微放大
SurfaceAnimator  createAnimationLeash: window_animation   Wallpaper 的动画, 但没看到具体的效果
SurfaceAnimator  createAnimationLeash: starting_reveal    应用窗口WindowState 要显示时的动画
SurfaceAnimator  createAnimationLeash: window_animation   StartWindow 移除动画

上面的这个5个动画主要分为3大部分:

  • 1、Activity启动最新出现的 app_transition 动画。
    这个阶段会出现3个动画:app_transition(应用),app_transition(桌面),window_animation(壁纸)

  • 2、应用窗口WindowState 要显示时的 starting_reveal 动画

  • 3、移除 StartWindow 的 window_animation 动画

第一部分重点介绍的是应用的 app_transition 动画,这个过程中也会涉及到另外2个。 其中桌面的关闭动画也是 app_transition 类型,所以流程和应用的启动 app_transition 动画流程大致一样的。只有最后 launcher 开始动画的时候做了一下区别。

不过壁纸的 window_animation 在 WallpaperAnimationAdapter 这个专门给壁纸做动画的 Adapter 并没有看到真正的动画执行,然后这边也看到其作为 wallpaperTargets 也传递到 launcher 了,但是最后也没发现有做动画的地方。这点就很奇怪,不过在 Winscope 也只是看到 有 window_animation 的图层,但是也没看到有相关数值的改变,所以个人觉得壁纸的 window_animation 可能并没有什么实际的动画。

在分析应用启动 app_transition 流程的代码前先看看下面的这几个事件,这个只是我个人在撸完整个动画流程,从代码执行顺序上列出来的9个节点。目前不知道是啥没关系,毕竟这只是我个人整理的,不是什么权威的关键节点,不过接下来的代码分析也会一个个的看到。

Activity启动app_transition 动画的主要事件

  • 1、launcher 进程构建 RemoteAnimationAdapter,AppLaunchAnimationRunner

  • 2、prepareAppTransition 流程

  • 3、executeAppTransition,AppTransition.setReady 流程

  • 4、 GOOD TO GO 打印

  • 5、system_service 创建动画leash 图层

  • 6、goodToGo()流程,真正触发远端动画执行

  • 7、launcher 开始远端动画

  • 8、launcher 具体动画的update

  • 9、launcher 动画结束,回调到system_service

后面的2部分相对简单,看对应的具体分析即可。

触发动画执行的套路

不管什么类型的动画都会执行:

WindowContainer::startAnimationSurfaceAnimator::startAnimation

会在 SurfaceAnimator::startAnimation 方法中创建动画 leash 图层,并通过 Adapter 来开始动画。然后才是适配器模式各自动画的 Adapter 做自己的处理。

一般:

  • 本地动画就是直接开始执行
  • 远端动画则是会在 goodToGo 触发远端执行

动画真正执行

动画的执行是适配器模式,但是真正干活的也不会是这个 Adapter 。
一般都是 Adapter + Runner 模式,真正干活的是这个 Runner 。
比如本地动画就是 LocalAnimationAdapter + SurfaceAnimationRunner
而分析的应用启动动画是远端动画,它们的组合是是 RemoteAnimationAdapter + LauncherAnimationRunner

动画的结束回调

目前看到的本地动画和远端动画,都会执行到 SurfaceAnimator::startAnimation 。而每个窗口构建的时候都会创建一个 SurfaceAnimator ,并且专递一个动画结束回调(WindowContainer::onAnimationFinished)过去。这个回调被封装成在 mInnerAnimationFinishedCallback 变量了。

所以本地动画和远端动画的结束回调,执行都是 WindowContainer::onAnimationFinished 方法。而这个方法最终又会执行到 WindowManagerService::onAnimationFinished

触发远端动画的Target

远端动画执行前会打印这次操作了哪些图层: 这段日志会打印动画的 Target,输出如下:

// 触发远程动画,打印传递过去的3个类型的leash图层数量
D WindowManager: goodToGo(): onAnimationStart, transit=TRANSIT_OLD_WALLPAPER_CLOSE, apps=2, wallpapers=1, nonApps=0D WindowManager: startAnimation(): Notify animation start:
I WindowManager: Starting remote animation
// 传递到远端的动画图层打印I WindowManager: container=Task{cdcc410 #1 type=home ?? U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}
// 桌面的
I WindowManager: Target:
I WindowManager:   mode=1 taskId=8 isTranslucent=false clipRect=[0,0][0,0] contentInsets=[0,70][0,84] prefixOrderIndex=16 position=[0,0] sourceContainerBounds=[0,0][720,1600] screenSpaceBounds=[0,0][720,1600] localBounds=[0,0][720,1600]
I WindowManager:   windowConfiguration={ mBounds=Rect(0, 0 - 720, 1600) mAppBounds=Rect(0, 70 - 720, 1516) mMaxBounds=Rect(0, 0 - 720, 1600) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=home mAlwaysOnTop=undefined mRotation=ROTATION_0}
I WindowManager:   leash=Surface(name=Surface(name=Task=1)/@0x1842ced - animation-leash of app_transition)/@0x21da6b6
I WindowManager:   taskInfo=TaskInfo{userId=0 taskId=8 displayId=0 isRunning=true baseIntent=Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10208000 cmp=com.android.launcher3/.uioverrides.QuickstepLauncher } baseActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher} topActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher} origActivity=null realActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher} numActivities=1 lastActiveTime=500834 supportsSplitScreenMultiWindow=true supportsMultiWindow=true resizeMode=2 isResizeable=true minWidth=-1 minHeight=-1 defaultMinSize=220 token=WCT{RemoteToken{d94d7b1 Task{512e747 #8 type=home I=com.android.launcher3/.uioverrides.QuickstepLauncher U=0 rootTaskId=1 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}}} topActivityType=2 pictureInPictureParams=null shouldDockBigOverlays=false launchIntoPipHostTaskId=0 displayCutoutSafeInsets=Rect(0, 70 - 0, 0) topActivityInfo=ActivityInfo{5fdf496 com.android.launcher3.uioverrides.QuickstepLauncher} launchCookies=[] positionInParent=Point(0, 0) parentTaskId=-1 isFocused=false isVisible=false isSleeping=false topActivityInSizeCompat=false topActivityEligibleForLetterboxEducation= false locusId=LocusId[17_chars] displayAreaFeatureId=1 cameraCompatControlState=hidden}
I WindowManager:   allowEnterPip=true
I WindowManager:   windowType=-1  hasAnimatingParent=false  backgroundColor=0container=Task{3a0b2b8 #13 type=standard A=10140:com.google.android.dialer U=0 visible=true visibleRequested=true mode=fullscreen translucent=false sz=1}// “电话”应用的
I WindowManager: Target:
I WindowManager:   mode=0 taskId=13 isTranslucent=false clipRect=[0,0][0,0] contentInsets=[0,70][0,84] prefixOrderIndex=20 position=[0,0] sourceContainerBounds=[0,0][720,1600] screenSpaceBounds=[0,0][720,1600] localBounds=[0,0][720,1600]
I WindowManager:   windowConfiguration={ mBounds=Rect(0, 0 - 720, 1600) mAppBounds=Rect(0, 70 - 720, 1516) mMaxBounds=Rect(0, 0 - 720, 1600) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0}
I WindowManager:   leash=Surface(name=Surface(name=Task=13)/@0xd0a4264 - animation-leash of app_transition)/@0x62f9fb7
I WindowManager:   taskInfo=TaskInfo{userId=0 taskId=13 displayId=0 isRunning=true baseIntent=Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 pkg=com.google.android.dialer cmp=com.google.android.dialer/.extensions.GoogleDialtactsActivity } baseActivity=ComponentInfo{com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity} topActivity=ComponentInfo{com.google.android.dialer/com.android.dialer.main.impl.MainActivity} origActivity=ComponentInfo{com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity} realActivity=ComponentInfo{com.google.android.dialer/com.android.dialer.main.impl.MainActivity} numActivities=1 lastActiveTime=500849 supportsSplitScreenMultiWindow=true supportsMultiWindow=true resizeMode=2 isResizeable=true minWidth=-1 minHeight=-1 defaultMinSize=220 token=WCT{RemoteToken{37af324 Task{3a0b2b8 #13 type=standard A=10140:com.google.android.dialer U=0 visible=true visibleRequested=true mode=fullscreen translucent=false sz=1}}} topActivityType=1 pictureInPictureParams=null shouldDockBigOverlays=false launchIntoPipHostTaskId=0 displayCutoutSafeInsets=Rect(0, 70 - 0, 0) topActivityInfo=ActivityInfo{346ae8d com.google.android.dialer.extensions.GoogleDialtactsActivity} launchCookies=[android.os.BinderProxy@c557842] positionInParent=Point(0, 0) parentTaskId=-1 isFocused=true isVisible=true isSleeping=false topActivityInSizeCompat=false topActivityEligibleForLetterboxEducation= false locusId=null displayAreaFeatureId=1 cameraCompatControlState=hidden}
I WindowManager:   allowEnterPip=true
I WindowManager:   windowType=-1  hasAnimatingParent=false  backgroundColor=0

这些日志里有很多关键信息,可以在遇到问题的时候看,数据太多就不一一介绍了,继续看日志是在哪里控制的吧。

触发远端动画的流程在 RemoteAnimationController::goodToGo 这些日志的打印也在这,忽略掉无关代码再看一下这个方法:

# RemoteAnimationController// 远端动画的Adapterprivate final RemoteAnimationAdapter mRemoteAnimationAdapter;void goodToGo(@WindowManager.TransitionOldType int transit) {// 打印goodToGo(),表现这才是真正的触发了goodToGo()逻辑ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");......// 打印日志,真正开始触发动画ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart,"+ " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",AppTransition.appTransitionOldToString(transit), appTargets.length,wallpaperTargets.length, nonAppTargets.length);// 重点* 3. 这里是触发远端动画真正执行的地方mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,wallpaperTargets, nonAppTargets, mFinishedCallback);......// 日志处理if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");writeStartDebugStatement();}......}

可以看到前面2个打印都在这,后续的打印是 RemoteAnimationController::writeStartDebugStatement 方法里触发的。

# RemoteAnimationControllerprivate void writeStartDebugStatement() {ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Starting remote animation");// 打印内容final StringWriter sw = new StringWriter();final FastPrintWriter pw = new FastPrintWriter(sw);for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {// 触发每个适配器的dumpmPendingAnimations.get(i).mAdapter.dump(pw, "");}pw.close();ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "%s", sw.toString());}

可以看到整理的打印是从 mPendingAnimations 下的对象取出对应的 RemoteAnimationRecord 相关的变量然后dump的。
mPendingAnimations 在应用启动动画-app_transition-3 的时候看到,是构建一个 RemoteAnimationRecord 对象就会把其添加进 mPendingAnimations 。
这里也有打印,比如当前场景的日志输出为:

D WindowManager: createAnimationAdapter(): container=Task{8d3f87a #20 type=standard A=10140:com.google.android.dialer U=0 visible=true visibleRequested=true mode=fullscreen translucent=false sz=1}
D WindowManager: createAnimationAdapter(): container=Task{7bfafb5 #1 type=home ?? U=0 visible=false visibleRequested=false mode=fullscreen translucent=true sz=1}

这个和之前的分析是一样的, 一个是新启动应用的Task 一个是 launcher 的。所以只有2个,注意,没有壁纸的,前面的日志也没看壁纸的Target。 所以壁纸动画到底是本地还是远端呢?有点迷了。

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

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

相关文章

思科dhcp的配置

以路由器为例 让pc3 自动获取ip地址并获取的网段为172.16.4.100-172.16.4.200 配置如下&#xff1a; R1(config)#interface GigabitEthernet0/2 R1(config)#ip address 172.16.4.254 255.255.255.0 R1(config)# no shutdown R1(config)#ip dhcp pool 4_pool //创建dhcp地址池…

如何使用 Python 读取数据量庞大的 excel 文件

使用 pandas.read_excel 读取大文件时&#xff0c;的确会遇到性能瓶颈&#xff0c;特别是对于10万行20列这种规模的 .xlsx 文件&#xff0c;常规的 pandas 方法可能会比较慢。 要提高读取速度&#xff0c;关键是找到更高效的方式处理 Excel 文件&#xff0c;特别是在 Python 的…

毕业设计项目——基于transformer的中文医疗领域命名实体识别(论文/代码)

完整的论文代码见文章末尾 以下为核心内容 摘要 近年来&#xff0c;随着深度学习技术的发展&#xff0c;基于Transformer和BERT的模型在自然语言处理领域取得了显著进展。在中文医疗领域&#xff0c;命名实体识别(Named Entity Recognition, NER)是一项重要任务&#xff0c;旨…

uniapp实战教程:如何封装一个可复用的表单组件

在uniapp开发过程中&#xff0c;表单组件的使用场景非常广泛。为了提高开发效率&#xff0c;我们可以将常用的表单组件进行封装。本文将带你了解如何在uniapp中封装一个表单组件&#xff0c;让你只需要通过属性配置轻松实现各种表单&#xff0c;效果图如下&#xff1a; 一、准备…

如何利用免费音频剪辑软件制作出精彩音频

现在有许多免费的音频剪辑软件可供选择&#xff0c;它们为广大用户提供了丰富的功能和便捷的操作体验&#xff0c;让音频编辑变得更加轻松和有趣。接下来&#xff0c;让我们一起走进这些免费音频剪辑软件的世界&#xff0c;探索它们的独特魅力和强大功能。 1.福昕音频剪辑 链…

【Nacos入门到实战十四】Nacos配置管理:集群部署与高可用策略

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

点云补全 学习笔记

目录 Depth completion with convolutions and vision transformers 依赖项&#xff1a; DCNv2 softpoolnet Depth completion with convolutions and vision transformers Zhang, Y., Guo, X., Poggi, M., Zhu, Z., Huang, G., Mattoccia, S.: Completionformer: Depth co…

docker运行arm64架构的镜像、不同平台镜像构建

背景 Docker 允许开发者将应用及其依赖打包成一个轻量级、可移植的容器&#xff0c;实现“一次构建&#xff0c;到处运行”的目标。然而&#xff0c;不同的操作系统和硬件架构对容器镜像有不同的要求。例如&#xff0c;Linux 和 Windows 系统有不同的文件系统和系统调用&#…

【预备理论知识——2】深度学习:线性代数概述

简单地说&#xff0c;机器学习就是做出预测。 线性代数 线性代数是数学的一个分支&#xff0c;主要研究向量空间、线性方程组、矩阵理论、线性变换、特征值和特征向量、内积空间等概念。它是现代数学的基础之一&#xff0c;并且在物理学、工程学、计算机科学、经济学等领域有着…

css3-----2D转换、动画

2D 转换&#xff08;transform&#xff09; 转换&#xff08;transform&#xff09;是CSS3中具有颠覆性的特征之一&#xff0c;可以实现元素的位移、旋转、缩放等效果 移动&#xff1a;translate旋转&#xff1a;rotate缩放&#xff1a;scale 二维坐标系 2D 转换之移动 trans…

OpenGL笔记十九之相机系统

OpenGL笔记十九之相机系统 —— 2024-10-02 晚上 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记十九之相机系统1.运行1.1.游戏相机1.2.轨迹球相机 2.游戏相机与轨迹球相机切换3.博主笔记本要运行需要更改的文件更改1:28_OpenGL_CameraSystem/applicat…

C语言文件操作(下)(28)

文章目录 前言一、文件的打开和关闭打开打开模式相对路径和绝对路径 关闭 二、文件操作正确流程三、文件顺序读写函数fopenfclosefputcfgetcfputsfgetsfprintffscanfsprintfsscanffwritefread 四、文件随机读写函数fseekftellrewind 五、文件读取结束时候的判断feofferror具体例…

4个顶级的大模型推理引擎

LLM 在文本生成应用中表现出色&#xff0c;例如具有高理解度和流畅度的聊天和代码完成模型。然而&#xff0c;它们的庞大规模也给推理带来了挑战。基本推理速度很慢&#xff0c;因为 LLM 会逐个生成文本标记&#xff0c;需要对每个下一个标记进行重复调用。随着输入序列的增长&…

什么是 Tammann temperature

Tammann temperature (Tt_tt​) 是材料科学中一个重要的概念&#xff0c;它通常用于描述材料的热力学特性和相变行为。其定义与玻璃态和晶态材料的内部原子运动相关。Tammann 温度在研究材料的扩散、再结晶、以及玻璃化转变过程中具有重要意义。 1. Tammann 温度的定义 Tamma…

【AIGC】2022-NIPS-视频扩散模型

2022-NIPS-Video Diffusion Models 视频扩散模型摘要1. 引言2. 背景3. 视频扩散模型3.1. 重建引导采样以改进条件生成 4. 实验4.1. 无条件视频建模4.2. 视频预测4.3. 文本条件视频生成4.3.1 视频与图像建模的联合训练4.3.2 无分类器指导的效果4.3.3 更长序列的自回归视频扩展 5…

【多线程】详解 CAS 机制

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. CAS 是什么1.1 CAS 具体步骤1.2 CAS 伪代码 2. CAS 的应用2.1 实现原子类2.1.1 AtomInteger 类2.1.2 伪代…

word无法复制粘贴

word无法复制粘贴 使用word时复制粘贴报错 如下&#xff1a; 报错&#xff1a;运行时错误‘53’&#xff0c;文件未找到&#xff1a;MathPage.WLL 这是mathtype导致的。 解决方法 1&#xff09;在mathtype下载目录下找到"\MathType\MathPage\64"下的"mathpa…

Qt开发第一讲

一、Qt项目里面有什么&#xff1f; 对各个文件的解释&#xff1a; Empty.pro文件 QT core gui # 要引入的Qt模块&#xff0c;后面学习到一些内容的时候可能会修改这里 #这个文件相当于Linux里面的makefile文件。makefile其实是一个非常古老的技术了。 #qmake搭配.pr…

C++之模版进阶篇

目录 前言 1.非类型模版参数 2.模版的特化 2.1概念 2.2函数模版特化 2.3 类模板特化 2.3.1 全特化和偏特化 2.3.2类模版特化应用实例 3.模版分离编译 3.1 什么是分离编译 3.2 模板的分离编译 3.3 解决方法 4. 模板总结 结束语 前言 在模版初阶我们学习了函数模版和类…

【MySQL】Ubuntu环境下MySQL的安装与卸载

目录 1.MYSQL的安装 2.MySQL的登录 3.MYSQL的卸载 4.设置配置文件 1.MYSQL的安装 首先我们要看看我们环境里面有没有已经安装好的MySQL 我们发现是默认是没有的。 我们还可以通过下面这个命令来确认有没有mysql的安装包 首先我们得知道我们当前的系统版本是什么 lsb_…