当前位置: 首页 > news >正文

【Android14 ShellTransitions】(六)SyncGroup完成

在这里插入图片描述

这一节的内容在WMCore中,回想我们的场景,是在Launcher启动某一个App,那么参与动画的就是该App对应Task(OPEN),以及Launcher App对应的Task(TO_BACK)。在确定了动画的参与者后,接下来我们就需要等待动画参与者绘制完成。一个基本的逻辑是,参与动画的主体要绘制出来了才能开始动画,不然动画都开始执行了,窗口还没有绘制出来,那对于用户来说屏幕上是没有任何变化的,这不就有点尴尬了。

ShellTransitions之前,检查动画是否可以开始的逻辑是在AppTransitionController.handleAppTransitionReady中,通过调用transitionGoodToGo来检查窗口是否绘制完成的,现在则是在BLASTSyncEngine.onSurfacePlacement中,通过调用BLASTSyncEngine.SyncGroup.tryFinish不断检查所有动画参与者是否已经全部同步完毕。一旦所有的动画参与者完成同步,则视为SyncGroup完成,或者说Transition就绪,就会调用BLASTSyncEngine.SyncGroup.finishNow,最终走到Transition.onTransactionReady,具体的调用堆栈为:

RootWindowContainer.performSurfacePlacementNoTrace

-> BLASTSyncEngine.onSurfacePlacement

-> BLASTSyncEngine.SyncGroup.tryFinish

-> BLASTSyncEngine.SyncGroup.finishNow

-> Transition.onTransactionReady

我们这一节先分析检查SyncGroup是否完成(BLASTSyncEngine.SyncGroup.tryFinish)以及SyncGroup完成(BLASTSyncEngine.SyncGroup.finishNow)的内容,Transition就绪的内容即Transition.onTransactionReady放到单独一章分析。

1 BLASTSyncEngine.onSurfacePlacement

BLASTSyncEngine.onSurfacePlacement由RootWindowContainer.performSurfacePlacementNoTrace调用,代码为:

在这里插入图片描述

BLASTSyncEngine的成员变量mActiveSyncs之前已经介绍过了,当我们创建Transition的时候,也会创建一个SyncGroup,来收集参与动画的WindowContainer,创建的SyncGroup则保存在了BLASTSyncEngine.mActiveSyncs。

这里则是从BLASTSyncEngine.mActiveSyncs中拿出SyncGroup,调用SyncGroup.tryFinish来检查SyncGroup是否完成。

2 SyncGroup.tryFinish

在这里插入图片描述

1)、如果SyncGroup.mReady为false,则直接返回。之前我们分析Transition的启动流程时,知道了只有当WMShell侧创建了一个ActiveTransition后切换回WMCore并且调用Transition.start,Transition才算正式启动,正是在Transition.start中,调用了SyncGroup.setReady方法将SyncGroup的mReady设置为了true。从这里我们就看到了SyncGroup.mReady的作用,如果Transition没有启动,那么这里是不会去检查其对应的SyncGroup是否完成了的,而是直接返回false,也就是说如果SyncGroup没有ready,那么Transition将无法走到下一个阶段。

2)、SyncGroup.mRootMembers则保存了参与动画的WindowContainer,我们这里则是为每一个WindowContainer调用WindowContainer.isSyncFinished来检查这个WindowContainer是否完成同步/绘制,只要有一个没有完成同步,那么就直接返回false,不需要往下走了,等待下一次RootWindowContainer.performSurfacePlacementNoTrace到来的时候再检查看看有没有完成同步。

3)、如果所有参与动画的WindowContainer都已经完成同步了,那么就继续调用SyncGroup.finishNow来将当前SyncGroup结束掉。

我们先分析WindowContainer.isSyncFinished,再去分析SyncGroup.finishNow。

2.1 WindowContainer.isSyncFinished

首先再回顾一下,当把WindowContainer添加SyncGroup的时候,会为每一个WindowContainer调用prepareSync方法,结果是:

1)、WindowState类型的WindowContainer,其mSyncState被置为SYNC_STATE_WAITING_FOR_DRAW。

2)、非WindowState类型的WindowContainer,其mSyncState被置SYNC_STATE_READY。

再看WindowContainer.isSyncFinished的内容:

在这里插入图片描述

1)、首先一个基本的规则是,如果一个WindowContainer请求的是不可见的,那么将其视为同步完成。这个其实也很好理解,如果这个WindowContainer在完成动画的时候是不希望被显示的,那么就不需要等待它绘制完成了。

2)、对于非WindowState类型的WindowContainer,比如Task或者ActivityRecord,由于它们的mSyncState一开始就被设置为了SYNC_STATE_READY,因此它们主要是检查它们的mChildren是否同步完成,最终检查的就是其中的WindowState是否完成同步/绘制。一旦有一个WindowState:

  • 同步完成。
  • 请求可见。
  • 和父容器大小相等。

只要找到一个符合以上条件的WindowState,那么就可以认为这个WindowContainer已经完成了同步。

3)、对于WindowState,则只有一个标准,即其mSyncState是否是SYNC_STATE_READY(暂不考虑子窗口的情况)。

2.2 WindowContainer.onSyncFinishedDrawing

再看下什么时候WindowState的mSyncState什么被设置为SYNC_STATE_READY。

WindowState的mSyncState被设置为SYNC_STATE_READY的地方只有一处,在WindowContainer.onSyncFinishedDrawing:

在这里插入图片描述

从注释也能看出,当WindowContainer完成绘制其内容的时候,这个方法会被调用。

具体调用的地方则是:

在这里插入图片描述

很明显,当窗口完成绘制时,会调用WindowState.finishDrawing,进而将WindowState的mSyncState设置为SYNC_STATE_READY。

而一旦Task/ActivityRecord中的WindowState绘制完成,那么该Task/ActivityRecord就会被视为同步完成。

这部分的内容之前分析WindowContainerTransaction的文章有过更加详细的介绍:

4【Android 12】【WCT的同步】BLASTSyncEngine - 掘金 (juejin.cn)

更多的详细内容可以看下当时的分析。

3 SyncGroup.finishNow

一旦SyncGroup中所有的动画参与者都同步完成,那么就调用SyncGroup.finishNow来结束掉这个SyncGroup。

这个方法是我们本篇文章的重点,且内容较多,我们分段去看。

3.1 合并Transaction

在这里插入图片描述

这里从对象工厂中拿到一个Transaction对象,局部变量merged,对于所有参与动画的WindowContainer,将它们在动画期间发生的同步操作都合并到这个局部变量merged中。

这一点主要是通过对所有参与动画的WindowContainer调用WindowContainer.finishSync方法来完成的,indowContainer.finishSync方法内容为:

在这里插入图片描述

1)、将WindowContainer.mSyncTransaction中收集到的对当前WindowContainer对应的SurfaceControl的修改(同步操作)合并到传参outMergedTransaction中,即我们上面提到的SyncGroup.finishNow中的局部变量merged。

2)、递归调用所有子WindowContainer的finishSync方法,最终的结果是将这个WindowContainer以及所有子WindowContainer的同步操作都收集到传参outMergedTransaction中。

3)、最后由于同步工作已经结束,那么将这个WindowContainer的mSyncState以及mSyncGroup之类的成员变量进行重置。

这里所说的同步操作主要是针对WindowContainer.mSyncTransaction来说的,其实之前也看到过“sync”这个字眼了,我们这里来大致说明一下“同步”这个概念。

3.1.1 “同步”的概念

其实最早的时候,BLASTSyncEngine以及SyncGroup并不是用于动画,而是和WindowContainerTransaction一起结合使用,主要是用于分屏。

分屏由于将屏幕一分为二以供两个App同时显示,那么一旦分屏发生变化(进退分屏,调整分割线位置等),那么最起码就会有两个可见的SurfaceControl参与了变化,即参与分屏的这两个App下的SurfaceControl。

那么一个很明显能够想到的问题是,如何做到这两个分屏的App界面能够一起改变呢,比如我调整分屏分割线的位置,我肯定不希望看到上分屏的App界面改变之后,下分屏的App界面没有跟着一起改变,而是又过了几百毫秒才开始变化。这是一个肯定会遇到的问题,App界面改变是在窗口绘制完成之后,而WMS无法控制窗口绘制的时间,因此如果WMS不加以控制,那么就会出现由于两个窗口的绘制时间不同,导致用户看到的两个界面先后进行了改变(异步),而非同一时间进行了改变(同步)。

因此当时引出了BLASTSyncEngine以及SyncGroup的概念,这套机制最开始就是用来保证使用WindowContainerTransaction的模块(比如分屏)可以做到SurfaceControl的同步。

同步的大致做法,则是创建一个统一的Transaction对象(即SyncGroup.finishNow中创建的那个Transaction类型的局部变量merged),来收集所有参与到分屏的SurfaceControl的变化,并且只有等到所有参与分屏的窗口都绘制完成后,才对这个Transaction对象调用apply方法,这样就保证了所有的SurfaceControl变化在一次Transaction.apply中进行了提交。

从以上介绍可以看到要实现同步,有两个比较重要点,一是有一个统一的Transaction来收集所有SurfaceControl的变化,二是当所有参与同步的窗口绘制完成后再调用Transaction.apply。

更多的内容可以去看下之前分析WindowContainerTransaction的系列文章。

3.1.2 WindowContainer.mSyncTransaction

回到现在Android14的ShellTransitions中来,现在是动画也开始用BLASTSyncEngine这一套逻辑了,接下来看下现在动画是如何实现同步的。

从上面的介绍中,我们知道同步的两个重要的点是:

1)、有一个统一的Transaction来收集所有SurfaceControl的变化。

2)、当所有参与同步的窗口绘制完成后再调用这个统一的Transaction对象的Transaction.apply方法。

可知和这个Transaction是有很大的关系。

第二点放到后面再说,这一节我们分析一下第一点,即有一个统一的Transaction来收集所有SurfaceControl的变化。

之前看SyncGroup.finishNow,我们知道了这个统一的Transaction就是这里创建的Transaction类型的局部变量merged,它合并了所有参与动画的WindowContainer的mSyncTransaction中收集的内容,那么WindowContainer.mSyncTransaction又是什么?

再举一个例子,来看一个经典的对SurfaceControl进行显示的操作,为WindowSurfaceController.showRobustly:

在这里插入图片描述

根据以往的系列文章知WindowSurfaceController.mSurfaceControl对应的是SurfaceFlinger侧的BufferStateLayer。

调用Transaction.show的时候,只是将对SurfaceControl的操作暂存在了Transaction中(更准确的说,是native层的layer_state_t结构体中),只有当调用Transaction.apply的时候,这个对SurfaceControl的操作才算真正提交到了SurfaceFlinger端,进而作用到了Layer上。

那么我们再看下这个传参Transaction对象是从哪里拿到的,一般来说,调用堆栈为:

WindowState.prepareSurfaces

-> WindowStateAnimator.prepareSurfaces

-> WindowSurfaceController.showRebustly

看到是在WindowState.prepareSurfaces中,通过WindowContainer.getSyncTransaction拿到的:

在这里插入图片描述

WindowContainer.getSyncTransaction为:

在这里插入图片描述

如果WindowContainer.mSyncTransactionCommitCallbackDepth大于0,或者WindowContainer.mSyncTransaction不为SYNC_STATE_NONE,说明此时WindowContainer仍然处于需要同步的场景中,因此返回WindowContainer.mSyncTransaction,否则返回WindowContainer.getPendingTransaction。

WindowContainer.getPendingTransaction为:

在这里插入图片描述

一般返回的是DisplayContent的mPendingTransaction。

再看下mSyncTransaction和mPendingTransaction的定义以及初始化:

在这里插入图片描述

看到mSyncTransaction和mPendingTransaction其实都是一个普通的Transaction对象,本质上没有区别,区别在于它们的使用方式:

1)、pendingTransaction基本上每次RootWindowContainer.performSurfacePlacementNoTrace就apply一次:

在这里插入图片描述

可以认为是使用pendingTransaction对SurfaceControl操作后,很快就会调用Transaction.apply,也就是说使用pendingTransaction对SurfaceControl进行的操作很快就能见到效果。

2)、syncTransaction的apply方法的调用时机则是和Transition的流程密切相关,只有走到特定的阶段才会调用Transaction.apply方法,以后的分析中我们会看到。

最后一句话总结一下pendingTransaction和syncTransaction的区别就是,需要WindowContaienr同步的场景使用syncTransaction,不需要WindowContainer同步的场景则使用pendingTransaction…怎么有点像废话呢

3.2 注册TransactionCommittedListener回调以及超时处理

在这里插入图片描述

主要内容是:

1)、为所有参与到动画的WindowContainer调用waitForSyncTransactionCommit方法.

2)、定义一个CommitCallback的类,这个类有一个自定义的onCommitted方法,以及复写Runnable的run方法。

3)、创建一个CommitCallback类的对象,callback。

4)、调用Transaction.addTransactionCommittedListener方法注册TransactionCommittedListener回调,回调触发的时候执行这个callback的onCommitted方法。

5)、Handler.postDelayed将这个callback添加到了MessageQueue中,5000ms超时之后执行这个callback的run方法。

接下来分别介绍,并不一定按照顺序。

3.2.1 注册TransactionCommittedListener回调

            CommitCallback callback = new CommitCallback();merged.addTransactionCommittedListener(Runnable::run,() -> callback.onCommitted(new SurfaceControl.Transaction()));

看到这里为merged调用了Transaction.addTransactionCommittedListener方法:

在这里插入图片描述

从注释来看,TransactionCommittedListener的onTransactionCommitted回调方法会在Transaction被apply的时候调用,另外这个回调被执行的时候也说明当前Transaction将不会被一个新的Transaction对象复写。

那么再结合SyncGroup.finishNow的代码,也就是说,当merged这个Transaction对象被apply后,Transaction.addTransactionCommittedListener这段代码将被执行:

executor.execute(listener::onTransactionCommitted)

也就是异步执行传参listener的onTransactionCommitted方法,即SyncGroup.finishNow中的这段代码:

callback.onCommitted(new SurfaceControl.Transaction())

即当merged这个Transaction对象被apply后,这里定义的CommitCallback类的onCommitted方法将会被执行。

分析了注册TransactionCommittedListener回调后,我们可以再回过头来看CommitCallback类的定义,即它的onCommitted方法和run方法。

3.2.2 CommitCallback.onCommitted

先看onCommitted方法,从上面的分析我们知道这个方法将会在merged被apply的时候调用,作用为:

1)、将CommitCallback从MessageQueue中移除,即merged在规定的5000ms内得到apply了,那么就不需要触发超时了。

2)、将ran这个变量置为true,因为Transaction有可能在5000ms超时后才apply,那么onCommitted方法就有可能走两次。

3)、调用WindowContainer的onSyncTransactionCommitted方法,onSyncTransactionCommitted方法要和waitForSyncTransactionCommit结合着来看:

在这里插入图片描述

正好WindowContainer.waitForSyncTransactionCommit方法也是在上面被调用了,感觉这两个方法主要是对mSyncTransactionCommitCallbackDepth这个成员变量进行操作,而mSyncTransactionCommitCallbackDepth作用的地方也在我们之前看过的WindowContainer.getSyncTransaction中:

在这里插入图片描述

我自己的看法是,这个变量用来继续延长WindowContainer的同步状态。

如我们之前第一节合并Transaction中提到的,这里会为所有参与同步的WindowContainer调用WindowContainer.finishSync方法,这将会使得WindowContainer的mSyncState重置为SYNC_STATE_NONE,那么假如没有mSyncTransactionCommitCallbackDepth,此时调用WindowContainer.getSyncTransaction将会返回pendingTransaction,而非syncTransaction,也就是说WindowContainer的同步状态在走到SyncGroup.finishNow的时候就结束了。

而加入了mSyncTransactionCommitCallbackDepth之后,WindowContainer的同步状态的结束将会被延迟到merged被apply的时候。

为什么要这么做呢,因为此时SyncGroup.finishNow距离merged被apply还有一段时间,而且这个时间其实可能会超过5000ms,即上面规定的超时时限。假如在merged被apply之前,WindowContainer又发生了变化,那么如果没有mSyncTransactionCommitCallbackDepth的存在,此时WindowContainer将使用pendingTransaction,并且pendingTransaction如果再在merged被apply之前就apply,就会出现新的Transaction(pendingTransaction)的内容被旧的Transaction(syncTransaction)内容覆盖的情况。

4)、调用WindowContainer.onSyncTransactionCommitted,将所有参与动画的WindowContainer.mSyncTransaction收集到Transaction类型传参t中,集中进行一次apply。

如之前所说,此时距离merged被apply还有一段时间,在这段时间内参与到动画的WindowContainer是有可能继续发生变化的,而syncTransaction合并到merged的操作已经结束了,为了让这个时间段的变化也能够被应用,所以这里调用WindowContainer.mSyncTransaction,将收集到变化的syncTransaction都合并到一个Transaction中,然后调用apply。

但是这样不就是后发生变化的WindowContainer的Transaction先被apply了吗,这样不是还会出现上面提到的Transaction被覆盖的情况?目前我暂时没有碰到过这种情况,但是这个逻辑我感觉是有问题的。

3.2.3 CommitCallback.run

根据我们的分析,我们知道了这个方法将会在5000ms超时后调用,主要的内容是:

1)、调用TransactionReadyListener.onTransactionCommitTimeout,通知关心方超时的情况。

2)、调用CommitCallback.onCommitted方法,应该是想让syncTransaction收集到的变化得到应用,但是之前合并到merged那部分变化则是永久丢失掉了,这部分应该才是最重要的。

3)、这里打印了一条log:

                    Slog.e(TAG, "WM sent Transaction to organized, but never received" +" commit callback. Application ANR likely to follow.");

打印了这个条log的时候,我们已经知道是处于5000ms超时的情况了,那么可能会出现本来应该显示的Layer,在5000ms的时间内得不到显示,那么屏幕上就可能会出现没有任何一个输入窗口可以作为焦点窗口的情况(输入窗口能够接收焦点,需要其Layer为可见),如果此时再来一个KeyEvent事件,那么就会发生无焦点窗口的ANR。

3.3 调用Transition.onTransactionReady

只剩下最后一点内容了,一起来看下:

在这里插入图片描述

1)、调用TransactionReadyListener类型的mListener的onTransactionReady方法。

2)、将当前SyncGroup从BLASTSyncEngine.mActiveSyncs中移除。

3)、将其成员变量mOnTimeout从MessageQueue中移除。

有关其成员变量mListener以及mOnTimeout的部分,在之前创建SyncGroup对象的时候漏说了,现在大概过一遍。

首先看下SyncGroup的构造方法:

在这里插入图片描述

1)、int类型的mSyncId成员变量保存该SyncGroup的ID。

2)、TransactionReadyListener类型的成员变量mListener保存与该SyncGroup一一对应的Transition对象,TransactionReadyListener定义为:

在这里插入图片描述

用来通知Transition同步完成以及Transaction提交超时。

所以这里调用的是Transition.onTransactionReady,Transition.onTransactionReady的内容比较多,需要单独开一篇分析。

3)、mOnTimeout则是一个Runnable,用来在超时的时候触发BLASTSyncEngine.onTImeout方法:

在这里插入图片描述

主要内容就是遍历一下参与同步的WindowContainer,看下是哪个WindowContainer没有同步完成,以及在方法的最终调用SyncGroup.finishNow,这个一点也很好理解,毕竟我们不能无限等待某一个WindowState绘制完成。

注意区分一下这里的超时和我们上面提到的超时。

这里的超时是在SyncGroup刚刚创建,或者说Transition刚开始收集的时候,开始计时的,防止某一个窗口迟迟没有完成绘制,从而无限等待这个窗口绘制完成的情况。

上面提到的超时是在SyncGroup.finishNow的时候开始计时的,防止merged这个Transaction迟迟没有得到apply(Transition没有走到下一个阶段),从而syncTransaction的收集的变化内容无法被apply的情况。

相关文章:

【Android14 ShellTransitions】(六)SyncGroup完成

这一节的内容在WMCore中,回想我们的场景,是在Launcher启动某一个App,那么参与动画的就是该App对应Task(OPEN),以及Launcher App对应的Task(TO_BACK)。在确定了动画的参与者后&#x…...

技术管理转型之战:决策之道-管理中的智慧与策略

文章目录 引言一、决策的重要性二、常见的决策方式1. 理性决策(Rational Decision Making)2. 有限理性(Bounded Rationality)3. 直觉决策(Intuitive Decision Making)4. 循证管理(Evidence-Base…...

Shell脚本:条件语句(if、case)

目录 硬编码 硬编码的缺点 条件判断 $? 命令行语句 判断指定目录是否存在 判断指定文件是否存在 判断指定对象是否存在 表达式形式语句 判断对象是否存在 判断对象是否有权限 与、或、非 运算 与运算 或运算 非运算 比较大小 判断磁盘利用率实验步骤 字符串…...

在Linux上为Windows目标配置Qt交叉编译

问题描述 我想使用Linux x86_64主机为Windows x86_64目标交叉编译Qt库(最终也包括我的应用程序)。我觉得自己已经接近成功了,但可能对整个过程有一些基本的误解。 我从在我的Fedora机器上安装所有mingw包开始,并修改了win32-g的…...

Introduction to linear optimization 第 2 章课后题答案 11-15

线性规划导论 Introduction to linear optimization (Dimitris Bertsimas and John N. Tsitsiklis, Athena Scientific, 1997), 这本书的课后题答案我整理成了一个 Jupyter book,发布在网址: https://robinchen121.github.io/manual-introdu…...

Java——包

一、包 1、简要介绍 在Java编程语言中,包(Package) 是一种用来组织和管理类(Class)和接口(Interface)的机制。包为开发者提供了一种逻辑分组的方式,使代码更加模块化、结构化和易于…...

Pipeline知识小记

在scikit-learn(通常缩写为sklearn)中,Pipeline是一个非常重要的工具,它允许你将多个数据转换步骤(如特征选择、缩放等)和估计器(如分类器、回归器等)组合成一个单一的估计器对象。这…...

postman国内外竞争者及使用详解分析

一、postman简介 Postman 是一款广泛使用的 API 开发和测试工具,适用于开发人员和测试人员。它提供了一个直观的界面,用于发送 HTTP 请求、查看响应、创建和管理 API 测试用例,以及自动化 API 测试工作流程。以下是 Postman 的主要功能和特点…...

人工智能对决:ChatGLM与ChatGPT,探索发展历程

图: a robot is writing code on a horse, By 禅与计算机程序设计艺术 目录 ChatGLM:...

探索Python元类的奥秘及其应用场景

探索Python元类的奥秘及其应用场景 一、引言 在Python中,元类(Metaclasses)是一个相对高级且容易被忽视的主题。然而,对于深入理解Python的面向对象编程模型以及进行高级框架和库的设计来说,元类是一个不可或缺的工具…...

C语言基础关键字的含义和使用方法

​关键字在C语言中扮演着非常重要的角色,它们定义了语言的基本构造和语法规则,通过使用关键字,开发者可以创建变量、定义数据类型、控制程序流程(如循环和条件判断)、声明函数等。由于这些字是保留的,所以编…...

【Golang - 90天从新手到大师】Day09 - string

系列文章合集 Golang - 90天从新手到大师 String 一个字符串是一个不可改变的字节序列。字符串可以包含任意的数据,但是通常是用来包含人类可读的文本。 len()返回字符串字节数目(不是rune数)。 通过索引可以访问某个字节值,0…...

网络安全与区块链技术:信任与安全的融合

# 网络安全与区块链技术:信任与安全的融合 在网络空间,信任是一种宝贵而稀缺的资源。区块链技术以其独特的分布式账本、加密算法和共识机制,为构建网络安全提供了新的解决方案。本文将探讨网络安全与区块链技术如何融合,以增强信…...

MySQL之复制(九)

复制 复制管理和维护 确定主备是否一致 在理想情况下,备库和主库的数据应该是完全一样的。但事实上备库可能发生错误并导致数据不一致。即使没有明显的错误,备库同样可能因为MySQL自身的特性导致数据不一致,例如MySQL的Bug、网络中断、服务…...

【面试干货】 Java 中的 HashSet 底层实现

【面试干货】 Java 中的 HashSet 底层实现 1、HashSet 的底层实现2、 HashSet 的特点3、 总结 💖The Begin💖点点关注,收藏不迷路💖 HashSet 是 Java 集合框架中的一个重要成员,它提供了不存储重复元素的集合。但是&am…...

爬虫经典案例之爬取豆瓣电影Top250(方法二)

在上一篇文章的基础上,改进了代码质量,增加了多个正则表达式匹配,但同事也增加了程序执行的耗时。 from bs4 import BeautifulSoup import requests import time import re from random import randint import pandas as pdurl_list [https…...

如何优化React应用的性能?

优化React应用的性能是一个多方面的过程,涉及到代码的编写、组件的设计、资源的管理等多个层面。以下是一些常见的性能优化策略: 避免不必要的渲染: 使用React.memo、useMemo和useCallback来避免组件或其子组件不必要的重新渲染。 代码分割: 使用React.…...

css文字镂空加描边

css文字镂空加描边 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>文字镂空</title><style>/* 公用样式 */html,body{width: 100%;height: 100%;position: relative;}/* html{overflow-y: scroll;} */*{margi…...

python数据分析与可视化

Python 在数据分析和可视化方面有着广泛的应用,并且拥有众多强大的库和工具来支持这些任务。以下是一些常用的 Python 库和它们的主要用途: 数据分析 Pandas: Pandas 是 Python 中用于数据处理和分析的主要库。 它提供了数据框(DataFrame)和序列(Series)两种数据结构…...

webkit 的介绍

WebKit 是一个开源的网页浏览器引擎&#xff0c;它是 Safari 浏览器和许多其他应用程序的基础。WebKit 最初由苹果公司开发&#xff0c;并在2005年作为开源项目发布。WebKit 的核心组件包括 WebCore 和 JavaScriptCore。以下是 WebKit 的详细介绍&#xff1a; ### WebKit 的主…...

make与makefile

目录 一、make的默认目标文件与自动推导 二、不能连续make的原因 执行原理 touch .PHONY伪目标 make指令不回显 makefile多文件管理 简写依赖方法 三、回车与换行 四、缓冲区 一、make的默认目标文件与自动推导 假设这是一个makefile文件&#xff0c;make的时候默认生…...

深度神经网络一

文章目录 深度神经网络 (DNN)1. 概述2. 基本概念3. 网络结构 深度神经网络的层次结构详细讲解1. 输入层&#xff08;Input Layer&#xff09;2. 隐藏层&#xff08;Hidden Layers&#xff09;3. 输出层&#xff08;Output Layer&#xff09;整体流程深度神经网络的优点深度神经…...

Pnpm:包管理的新星,如何颠覆 Npm 和 Yarn

在探索现代 JavaScript 生态系统时&#xff0c;我们常常会遇到新兴技术的快速迭代和改进。其中&#xff0c;包管理工具的发展尤为重要&#xff0c;因为它们直接影响开发效率和项目性能。最近&#xff0c;pnpm 作为一种新的包管理工具引起了广泛关注。它不仅挑战了传统工具如 np…...

汽车IVI中控开发入门及进阶(三十二):i.MX linux开发之Yocto

前言: 对于NXP的i.mx,如果基于linux开发,需要熟悉以下文档: IMX_YOCTO_PROJECT_USERS_GUIDE.pdf IMX_LINUX_USERS_GUIDE.pdf IMX_GRAPHICS_USERS_GUIDE.pdf 如果基于android开发,需要熟悉一下文档: Android_Auto_Quick_Start_Guide.pdf ANDROID_USERS_GUIDE.pdf …...

tessy 编译报错:单元测试时,普通桩函数内容相关异常场景

目录 1&#xff0c;失败现象 2&#xff0c;原因分析 1&#xff0c;失败现象 1&#xff0c;在 step 桩函数正常的情况下报错。 2&#xff0c;测试代码执行的数据流 和 step 桩函数内容不一致。 2&#xff0c;原因分析 桩函数分为 test object, test case, test step 三种类别。…...

计算机专业是否仍是“万金油”

作为一名即将参加高考的学生&#xff0c;我站在人生的分岔路口上&#xff0c;面临着选择大学专业的重大抉择。在这个关键节点&#xff0c;计算机相关专业是否仍是炙手可热的选择&#xff1f;  首先&#xff0c;从行业的角度来看&#xff0c;计算机相关专业确实在近年来持续火…...

雷池社区版自动SSL

正常安装雷池&#xff0c;并配置站点&#xff0c;暂时不配置ssl 不使用雷池自带的证书申请。 安装&#xff08;acme.sh&#xff09;&#xff0c;使用域名验证方式生成证书 先安装git yum install git 或者 apt-get install git 安装完成后使用 git clone https://gitee.com/n…...

怎样减少徐州服务器租用的成本?

服务器租用的出现&#xff0c;十分便于网络行业的发展&#xff0c;但是随着服务器租用的广泛应用&#xff0c;整体还是有着一定的成本的吗&#xff0c;不同的服务器类型在价格方面也是不同的&#xff0c;那么企业在选择服务器租用后&#xff0c;怎样才能减少服务器租用的成本呢…...

【性能优化】表分桶实践最佳案例

分桶背景 随着企业的数据不断增长&#xff0c;数据的分布和访问模式变得越来越复杂。我们前面介绍了如何通过对表进行分区来提高查询效率&#xff0c;但对于某些特定的查询模式&#xff0c;特别是需要频繁地进行数据联接查或取样的场景&#xff0c;仍然可能面临性能瓶颈。此外…...

数据仓库的挑战

建设数据仓库是一个复杂且资源密集的过程&#xff0c;需要考虑多个方面。以下是建设数据仓库时常见的挑战及其详细解释&#xff1a; 1. 数据集成 挑战&#xff1a; 数据来源多样&#xff1a;数据来自不同的系统、数据库、文件格式&#xff08;如CSV、JSON、XML&#xff09;、…...

基于ResNet-18的简单分类(新手,而且网络效果不咋滴,就是学个流程)

引言 先看问题&#xff1a; 我手边有一数据集&#xff0c;然后我想分分类&#xff01;~~ 咳咳&#xff0c;最近刚做了一个&#xff1a;训练集有1143张&#xff0c;分为5类&#xff0c;里面图片是打乱的。测试集有248张&#xff0c;想把它分分类看看咋样。 再看一下效果: …...

自动化测试:Autorunner的使用

自动化测试&#xff1a;Autorunner的使用 一、实验目的 1、掌握自动化测试脚本的概念。 2、初步掌握Autorunner的使用 二、Autorunner的简单使用 autoRunner使用方法 新建项目 a) 在项目管理器空白区域,右键鼠标,选择新建项目 b) 输入项目名后,点击[确定]. 在初次打开aut…...

时序预测 | Matlab基于CNN-BiLSTM-Attention多变量时间序列多步预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于CNN-BiLSTM-Attention多变量时间序列多步预测&#xff1b; 2.多变量时间序列数据集&#xff08;负荷数据集&#xff09;&#xff0c;采用前96个时刻预测的特征和负荷数据预测未来96个时刻的负荷数据&…...

软考 系统架构设计师系列知识点之杂项集萃(42)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;41&#xff09; 第67题 Windows操作系统在图形界面处理方面采用的核心架构风格是&#xff08; &#xff09;风格。Java语言宣传的“一次编写&#xff0c;到处运行”的特性&#xff0c;从架构风格…...

FastBoot刷机获取root权限(Magisk)

1.首先要下载ADB、Fastboot等工具。 1.ADB、Fastboot工具 https://developer.android.com/studio/releases/platform-tools 2.安装FastBoot的USB驱动 https://developer.android.com/studio/run/oem-usb 2.下载对应的镜像 https://developers.google.com/android/images?…...

信息检索(43):SPLADE: Sparse Lexical and Expansion Model for First Stage Ranking

SPLADE: Sparse Lexical and Expansion Model for First Stage Ranking 摘要1 引言2 相关工作3 方法3.1 SparTerm3.2 SPLADE&#xff1a;稀疏词汇和扩展模型 4 实验5 结论 发布时间&#xff08;2021&#xff09; 标题&#xff1a;稀疏词汇 扩展模型 摘要 稀疏的优点&#xf…...

DockerHub 镜像加速

Docker Hub 作为目前全球最大的容器镜像仓库&#xff0c;为开发者提供了丰富的资源。Docker Hub 是目前最大的容器镜像社区&#xff0c;DokcerHub的不能使用,导致在docker下pull镜像无法下载,安装kubernetes镜像也受到影响,下面请看解决方式。 1.加速原理 Docker下载加速的原理…...

Oracle 迁移 Mysql

-- Oracle->MySQL -- 使用时改一下where条件的owner和table_name -- 字段数据类型映射时会将Oracle中的浮点NUMBER转换为decimal(65,8)定点数 -- 可以识别主键约束、非空约束&#xff0c;但无法识别外键约束、唯一约束、自定义check -- 对于Oracle字符串长度为4000的&#x…...

vue3父子组件通信

一&#xff0c;父传子——defineProps 方法&#xff1a; 在父组件的模板中使用子组件标签&#xff0c;并且给标签自定义属性和属性名&#xff0c;即通过v-bind绑定数值&#xff0c;而后传给子组件&#xff1b;子组件则通过defineProps接收使用。 父组件&#xff1a; <tem…...

CSS中使用应用在伪元素中的计数器属性counter-increment

在CSS中&#xff0c;counter-increment 是一个用于递增计数器值的属性。它通常与 counter-reset 和 content 属性一起使用&#xff0c;以在文档中的特定位置&#xff08;如列表项、标题等&#xff09;插入自动生成的数字或符号。 counter-increment 基本用法&#xff1a; 使…...

【SkiaSharp绘图08】SKPaint方法:自动换行、是否乱码、字符偏移、边界、截距、文本轮廓、测量文本

文章目录 SKPaint方法BreakText 计算指定宽度内可绘制的字符个数ContainsGlyphs字体是否包含文本字符(是否会乱码)GetGlyphOffsets 字符偏移量GetGlyphPositions 偏移坐标GetGlyphWidths 每个字符的宽度与边界GetHorizontalTextIntercepts 轮廓截距GetPositionedTextIntercepts…...

深入理解Servlet Filter及其限流实践

引言 在Java Servlet技术中&#xff0c;Filter是一个拦截器&#xff0c;它允许开发者在请求到达目标资源之前或响应发送给客户端之后&#xff0c;对请求或响应进行拦截和处理。这种机制为实现诸如身份验证、日志记录、请求修改等功能提供了极大的灵活性。 Filter基础 Filter…...

使用cv2对视频指定区域进行去噪

视频去噪其实和图象一样&#xff0c;只是需要现将视频截成图片&#xff0c;在对图片进行去噪&#xff0c;将去噪的图片在合成视频就行。可以利用cv2.imread()、imwrite()等轻松实现。 去噪步骤 1、视频逐帧读成图片 2、图片指定区域批量去噪 2、去噪后的图片写入视频 1、视频逐…...

AI在创造还是毁掉音乐?

AI对音乐产业的影响是复杂而多维的&#xff0c;既有创造性的贡献也存在潜在的挑战。我们可以从以下几个角度来分析这个问题&#xff1a; ### 创造性贡献 1. **音乐创作**&#xff1a;AI可以帮助音乐家创作新的旋律和和声&#xff0c;甚至生成完整的音乐作品。例如&#xff0c…...

【2023年全国青少年信息素养大赛智能算法挑战赛复赛真题卷】

目录 2023全国青少年信息素养大赛智能算法挑战赛初中组复赛真题 2023全国⻘少年信息素养⼤赛智能算法挑战复赛⼩学组真题 2023全国青少年信息素养大赛智能算法挑战赛初中组复赛真题 1. 修复机器人的对话词库错误 【题目描述】 基于人工智能技术的智能陪伴机器人的语言词库被…...

Android系统揭秘(一)-Activity启动流程(上)

public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread (IApplicationThread) contextThread; … try { … int result …...

使用Java实现哈夫曼编码

前言 哈夫曼编码是一种经典的无损数据压缩算法&#xff0c;它通过赋予出现频率较高的字符较短的编码&#xff0c;出现频率较低的字符较长的编码&#xff0c;从而实现压缩效果。这篇博客将详细讲解如何使用Java实现哈夫曼编码&#xff0c;包括哈夫曼编码的原理、具体实现步骤以…...

IDEA、PyCharm等基于IntelliJ平台的IDE汉化方式

PyCharm 或者 IDEA 等编辑器是比较常用的&#xff0c;默认是英文界面&#xff0c;有些同学用着不方便&#xff0c;想要汉化版本的&#xff0c;但官方没有这个设置项&#xff0c;不过可以通过插件的方式进行设置。 方式1&#xff1a;插件安装 1、打开设置 File->Settings&a…...

visual studio 创建c++项目

目录 环境准备&#xff1a;安装 visual studiovisual studio 创建c项目Tips&#xff1a;新建cpp文件注释与取消注释代码 其他初学者使用Visual Studio开发C和C时常遇到的3个坑 环境准备&#xff1a;安装 visual studio 官网&#xff1a;https://visualstudio.microsoft.com/zh…...

MGV电源维修KUKA机器人电源模块PH2003-4840

MGV电源维修 库卡电源模块维修 机器人电源模块维修 库卡控制器维修 KUKA电源维修 库卡机器人KUKA主机维修 KUKA驱动器模块维修 机械行业维修&#xff1a;西门子系统、法那克系统、沙迪克、FIDIA、天田、阿玛达、友嘉、大宇系统&#xff1b;数控冲床、剪板机、折弯机等品牌数控…...