【Android14 ShellTransitions】(七)Transition就緒

Transition.onTransactionReady的內容比較長,我們挑重點的部分逐段分析(跳過的地方并非不重要,而是我柿子挑軟的捏)。

1 窗口繪制狀態的流轉以及顯示SurfaceControl

注意我們這里的SurfaceControl特指的是WindowSurfaceController的mSurfaceControl,如果對這個不是很了解的,可以回顧一下之前寫的關于SurfaceControl的文章:

【基礎】2、Surface的創建【Android 12】 - 掘金 (juejin.cn)

接著分析代碼:

在這里插入圖片描述

先跳過Transition.commitVisibleActivities,看到首先是將Transition.mState置為STATE_PLAYING,這意味著動畫馬上就要執行了。

然后是為Transition的兩個成員變量mStartTransaction以及mFinishTransaction賦值,mFinishTransaction不用多說,看到mStartTransaction被賦值為傳參transaction,傳參即我們上一篇分析中的在SyncGroup.finishNow創建的一個Transaction,局部變量merged:

在這里插入圖片描述

一個“start transaction”和一個“finish transaction”,我按照個人的理解,舉個例子說明一下,如果我們從ActivityA上啟動了一個ActivityB:

1)、對于ActivityA來說,它相關的SurfaceControl(準確一點說則是WindowSurfaceController.mSurfaceControl)需要在動畫結束的時候再隱藏,如果它在動畫開始前就隱藏,那么就無法看到ActivityA的動畫效果了(向右平移退出或者淡出之類的動畫)。

2)、對于ActivityB來說,它相關的SurfaceControl需要在動畫開始的時候就顯示出來,如果它在動畫開始的時候還沒有顯示,那么同樣也無法看到ActivityB的動畫效果了(向右平移進入或者淡入之類的動畫)。

從以上分析可知,ActivityA和ActivityB相關的SurfaceControl可見性變化的時機是不同的,那么這個行為通過一次Transacton.apply是無法做到的,所以就需要兩個Transaction,即“start transaction”和“finish transaction”。“start transaction”在動畫開始前調用apply,用于在動畫開始執行前提前將ActivityB進行顯示,“finish transaction”則是在動畫結束的時候調用apply,用于在動畫結束的時候再將ActivityA隱藏。

最重要的是要弄清楚“start transaction”和“finish transaction”這兩個Transaction調用apply方法的時機,在以后的Transition流程中會分析到。

再來看Transition.commitVisibleActivities方法的內容:

在這里插入圖片描述

如該方法的注釋所說,當前Transition已經準備好執行動畫了,這里先讓“start transaction”把相關需要顯示的SurfaceControl顯示出來。

Transition.mParticipants是參與動畫的WindowContainer集合,那么這個方法就是遍歷這個集合:

1)、調用ActivityRecord.commitVisibility設置相關ActivityRecord的為可見。

2)、調用ActivityRecord.commitFinishDrawing進一步設置相關SurfaceControl為可見。

ActivityRecord.commitVisibility方法內容比較多,主要是用來ActivityRecord的可見性,即其成員變量mVisible,除此之外還有很多別的邏輯,但是和我們要分析的Transition內容無關,只需要知道這里設置了ActivityRecord的可見性即可,不去多說。我們主要看下ActivityRecord.commitFinishDrawing:

在這里插入圖片描述

很簡單,為每一個child,即WindowState調用commitFinishDrawing方法:

在這里插入圖片描述

1)、調用WindowStateAnimator.commitFinishDrawingLocked方法,繼續將窗口對應的WindowStateAnimator的mDrawState,即繪制狀態進行流轉。

2)、調用WindowStateAnimator.prepareSurfaceLocked,設置SurfaceControl的可見性。

這兩個方法都比較重要,我們接下來分別進行分析。

1.1 窗口繪制狀態的流轉

SurfaceControl最終的顯示和窗口的繪制狀態密切相關,所以我感覺這里有必要看一下WindowStateAnimator.mDrawState這個狀態是如何切換的,并且我自己對這個窗口的繪制狀態也是不求甚解,也希望借著這個機會了解一下。

先分析一下代碼,回頭再試著總結一下。

·1.1.1 WindowStateAnimator.commitFinishDrawingLocked

在這里插入圖片描述

首先將WindowState.mDrawState設置為READY_TO_SHOW。

然后如果當前WindowStateAnimator相關的WindowState滿足以下條件之一,則繼續調用WindowStateAnimator.performShowLocked:

1)、沒有對應的ActivityRecord,即是一個非Activity窗口:

activity == null

2)、有對應的ActivityRecord,并且此時已經可以顯示窗口了:

activity.canShowWindows()

在這里插入圖片描述

重要是就是看這個ActivityRecord的mSyncState是不是SYNC_STATE_WAITING_FOR_DRAW,如果是這個值,那么就說明這個ActivityRecord是處于動畫中的。

但是有一個問題是,ActivityRecord的mSyncState是不會被設置為SYNC_STATE_WAITING_FOR_DRAW的,只有WindowState才會,那豈不是每次走到這里判斷ActivityRecord是否drawn,都將一直是true。

3)、是一個TYPE_APPLICATION_STARTING類型的窗口,即SplashScreen或者Snapshot:

mWin.mAttrs.type == TYPE_APPLICATION_STARTING

總而言之,如果這個WindowState滿足了繪制了條件,那么將繼續調用WindowState.performShowLocked。

1.1.2 WindowState.performShowLocked

在這里插入圖片描述

如果WindowStateAnimator.mDrawState不是READY_TO_SHOW,那么返回false,否則將其置為HAS_DRAWN,并且返回true,這將使得我們可以下一步繼續調用WindowStateAnimator.prepareSurfaces方法。

從WindowStateAnimator.commitFinishDrawingLocked以及WindowState.performShowLocked這兩個方法都能看到,窗口的繪制狀態是循序漸進的,必須是狀態A -> 狀態B -> 狀態C,不存在狀態A直接到狀態C之類的。

1.1.3 窗口繪制狀態小結

首先是mDrawState在WindowStateAnimator的定義,以及幾個取值:

在這里插入圖片描述

結合著Activity啟動的一般流程,我大致總結一下:

1)、NO_SURFACE:當沒有Surface的就置為這個狀態。

這個很好理解,一般窗口銷毀相關的流程,會將WindowStateAnimator.mDrawState設置為NO_SURFACE,比如:

WindowState.removeImmediately

-> WindowStateAnimator.destroySurfaceLocked

-> WindowStateAnimator.destroySurface

此階段沒有窗口,也沒有Surface。

2)、DRAW_PENDING:當Surface被創建之后,窗口被添加但還沒有開始繪制之前,就會置為這個狀態。在這個時期,Surface是隱藏的。這表明Surface正等待應用程序繪制窗口的內容。

當窗口被添加,接著App側開始走measure、layout以及draw流程,在draw之前,會將窗口在WMS側進行relayout,經過:

WMS.relayoutWindow

-> WMS.createSurfaceControl

-> WindowStateAnimator.createSurfaceLocked

-> WindowStateAnimator.resetDrawState

會將WindowStateAnimator.mDrawState設置為DRAW_PENDING。

這個流程我們也很熟悉,即之前分析創建WindowSurfaceController的SurfaceControl的流程。

此階段窗口被添加但還沒繪制出來,SurfaceControl也是隱藏的。

3)、COMMIT_DRAW_PENDING:當窗口的繪制操作完成,但是這個Surface還沒有顯示出來之前,狀態會設置為此值。這個Surface會在下次layout過程中顯示出來。

當窗口繪制完成,App側調用ViewRootImpl.reportDrawFinished后,就會調用IWindowSession的對端,經過:

Session.finishDrawing

-> WMS.finishDrawingWindow

-> WindowState.finishDrawing

-> WindowStateAnimator.finishDrawingLocked

會將WindowStateAnimator.mDrawState設置為COMMIT_DRAW_PENDING。

此階段窗口已經繪制完成,但是Surface由于一些原因還不能顯示。

4)、READY_TO_SHOW:這個狀態標識窗口的繪制操作已經提交,但Surface還沒有真正顯示。在一組窗口(例如屬于同一個應用的多個窗口)準備顯示時,系統會使用這個狀態來延遲顯示Surface,直到所有相關窗口都準備好一起顯示。

首先我們看到在動畫的流程中,窗口的繪制狀態被設置為READY_TO_SHOW的流程為:

Transition.onTransactionReady

-> Transition.commitVisibleActivities

-> ActivityRecord.commitFinishDrawing

-> WindowState.commitFinishDrawing

-> WindowStateAnimator.commitFinishDrawingLocked

結合注釋,我個人的理解是,繪制狀態被置為READY_TO_SHOW,表明此窗口已經繪制完了,可以準備顯示它的SurfaceControl了,但是它的SurfaceControl需要等待和其它的SurfaceContrl一起顯示,或者說等待動畫走到特定階段才能顯示,因此我們這里推遲其SurfaceControl的顯示時間,將窗口的繪制狀態設置為READY_TO_SHOW。

如果不考慮和其它窗口一起顯示,那么我想在這一步就可以將繪制狀態設置為HAS_DRAWN了,即READY_TO_SHOW這個狀態值是不必要的。

5)、HAS_DRAWN:當窗口首次在屏幕上顯示時,就會設置為此狀態。

這個值在WindowState.performShowLocked方法中被設置,緊跟著WindowStateAnimator.commitFinishDrawingLocked方法。

嚴謹一點的話注釋的說法其實是不準確的,當窗口繪制狀態被設置為HAS_DRAWN的時候,只是說明SurfaceControl接下來可以顯示了,但是SurfaceControl仍然沒有顯示,屏幕上是看不見的。

6)、總結一下,從以上分析可知,這些狀態值不只涉及了窗口的繪制流程,還涉及了SurfaceControl的顯示流程:

  • NO_SURFACE:沒窗口,也沒SurfaceControl。
  • DRAW_PENDING:有窗口,但沒開始繪制。有SurfaceControl,但不能顯示。
  • COMMIT_DRAW_PENDING,窗口剛剛繪制完,SurfaceControl還不能顯示。
  • READY_TO_SHOW:窗口已經繪制完了,SurfaceControl可以顯示了,但沒必要,再等等。
  • HAS_DRAWN:窗口已經繪制完了,SurfaceControl也可以顯示了。

1.2 顯示SurfaceControl

回到WindowState.commitFinishDrawing,在調用WindowStateAnimator.commitFinishDrawingLocked將窗口的繪制狀態走完后,接下來就是調用WindowStateAnimator.prepareSurfaceLocked來顯示SurfaceControl了。

注意這個方法被調用的地方有兩處:

在這里插入圖片描述

還有一處調用的地方在WindowState.prepareSurfaces,這個是更通用的流程,但是動畫流程下,則稍微不同,即我們分析的這個流程。

看代碼:

在這里插入圖片描述

我們只看和顯示SurfaceControl相關的部分:

1)、如果窗口不在屏幕上,則調用WindowStateAnimator.hide -> WindowSurfaceController.hide來隱藏SurfaceControl。

2)、如果窗口在屏幕上,那么進一步判斷窗口的繪制狀態,只有窗口的繪制狀態為HAS_DRAWN,才能繼續調用WindowSurfaceController.showRobustly來顯示SurfaceControl:

在這里插入圖片描述

關鍵的就那一句,調用Transaction.show來顯示相關SurfaceControl,但是要注意的是這里并沒有調用Transaction.apply,所以這個時候窗口還是沒有顯示。

窗口的最終顯示則是和這個傳參Transaction對象有關,這個Transaction對象則是之前說的”start transaction“,那么這個Transaction的apply方法的調用時機則是跟Transition的流程相關,以后的分析會看到。

2 計算動畫目標

這一節的內容是調用Transition.calculateTargets來計算動畫的目標:

在這里插入圖片描述

Transition的成員變量mTargets定義為:

在這里插入圖片描述

之前收集到的動畫參與者提升后的最終的動畫目標,也就是說最終執行動畫的主體并非是之前收集到的動畫參與者,而是這一步用動畫參與者計算得到的動畫目標。

Transition.calculateTargets的內容為:

在這里插入圖片描述

大致的內容為:

1)、創建一個Transition.Targets類型的局部變量targets,來收集動畫目標。

2)、遍歷Transition.mParticipants,從Transition.mChanges中取出對應的ChangeInfo對象放到Transition.Targets.mArray中,但是跳過WindowState類型的動畫參與者,以及跳過那些根據ChangeInfo.hasChanged得出前后沒有發生變化的動畫參與者。

3)、調用Transition.tryPromote嘗試提升targets中保存的動畫目標的級別。

我們這一節主要來看下這個Transition.tryPromote。

”promote“,提升的動畫目標在WindowContainer層級結構中的級別,這個邏輯之前在AppTransitionController.getAnimationTargets也用到了,思想都是類似的。比如一個Task中有兩個ActivityRecord,并且這兩個ActivityRecord要分別執行一段動畫,也就是動畫執行的主體是ActivityRecord。如果這兩個ActivityRecord剛好都想向左平移同樣的距離,那么我們就不需要為這兩個ActivityRecord分別應用一段平移的動畫,而是直接將這個平移的動畫應用到它們共同的父容器Task上,并且實現的效果是一樣的。這也就是”promote“的含義,動畫的目標主體從ActivityRecord”提升“到了更高一級的Task上。

接著看代碼,Transition.tryPromote。

2.1 Transition.tryPromote

在這里插入圖片描述

主要邏輯為遍歷Targets.mArray中的每一個ChangeInfo對象,調用Transition.canPromote方法來判斷他們是否能夠提升為父容器。

1)、如果不能,直接跳過該ChangeInfo對象,判斷下一個。

2)、如果能,就說明提升成功。此外還要調用Transition.reportIfNotTop來繼續判斷它是否是organized(我的理解就是這個WindowContainer是否是系統開機后自動創建的,不是需要的時候再去創建的)。如果不是,那么將當前WindowContainer對應的ChangeInfo從局部變量targets中移除,然后把它的父WindowContainer對應的ChangeInfo加如到targets中。如果是,那么在不移除當前WindowContainer對應的ChangeInfo的前提下,把它的父WindowContainer對應的ChangeInfo加如到targets中。這里應該是針對organized的WindowContainer的特殊處理,確保organized的WindowContainer的變化也能夠報告到WMShell那邊。

因此重點其實是Transition.canPromote邏輯。

2.2 Transition.canPromote

在這里插入圖片描述

感覺這段代碼還是比較重要的,我們逐行分析。

2.2.1 片段1

在這里插入圖片描述

1)、對應WindowContainer.canCreateRemoteAnimationTarget方法,目前只有TaskDisplayArea、TaskFragment以及ActivityRecord會返回true,其它類型的WindowContainer都會返回false,也就是說父容器不是這幾類的WindowContainer將無法得到提升,那么目前只有這幾種提升:WindowState到ActivityRecord,ActivityRecod到TaskFragment,TaskFragment到TaskFragment(因為TaskFragment存在嵌套,比如Home類型的TaskFragment),以及TaskFragment到TaskDisplayArea。另外從Transition.calculateTargets的邏輯我們看到了執行動畫的target至少是WindowToken這一級的,并且看收集的邏輯,似乎也沒有看到過直接收集WindowState的,因此實際上提升只存在以下幾種情況:

  • ActivityRecod到TaskFragment。
  • TaskFragment到TaskFragment。
  • TaskFragment到TaskDisplayArea。

2)、如果找不到父WindowContainer對應的ChangeInfo,則不提升,返回false。

3)、如果父WindowContainer有ChangeInfo,但是此時的狀態和收集開始時的狀態沒有變化,則不提升,返回false。

2.2.2 片段2

在這里插入圖片描述

1)、如果當前要提升的WindowContainer是Wallpaper類型的,則不提升,返回false。

2)、如果當前WindowContainer前后的父WindowContainer不一致,即發生reparent了,則不提升,返回false。

2.2.3 片段3

在這里插入圖片描述

遍歷父WindowContainer的所有子WindowContainer:

1)、如果姊妹WindowContainer在Transition.mChanges中找不到一個對應的ChangeInfo對象,或者有這么一個ChangeInfo對象,但是該ChangeInfo對象不在Targets.mArray中,這種情況一共可以理解為這個姊妹WindowContainer沒有參與到本次動畫,那么還需要繼續判斷:

----1.1)、如果該姊妹WindowContainer可見,那么就不提升,直接返回false,當前WindowContainer無法提升到父WindowContainer。畢竟該姊妹WindowContainer是沒有參與到動畫中的,并且是可見的,如果你提升了,那后續動畫執行的時候用戶不是會看到該姊妹WindowContainer跟著一起動了嘛,這肯定是不對的。

----1.2)、如果該姊妹WindowContainer不可見,那么就跳過對這個WindowContainer的檢查。不可見的姊妹WindowContainer對于本次動畫也沒有太大影響,即使跟著一起進行動畫用戶也看不到,直接跳過檢查下一個姊妹WindowContainer就好了。

2)、如果姊妹WindowContainer從Transition.mChanges中能找到一個對應的ChangeInfo對象,并且該ChangeInfo對象也在局部變量targets中,那么認為該姊妹WindowContainer也參與了本次動畫,那么分別為他們的TransitionMode調用Transition.reduceMode方法來看它們動畫的大方向是否是一致的,首先是根據ChangeInfo.getTransitMode拿到各自的TransitionMode:

在這里插入圖片描述

TransitionMode定義在TransitionInfo中:

在這里插入圖片描述

看到Transition模式其實就是定義在WindowManager中的Transition類型的子集。

ChangeInfo.getTransitMode的內容也比較簡單:

TRANSIT_CHANGE:收集階段的可見性和Transition就緒階段的可見性沒有發生變化。

TRANSIT_OPEN:存在發生了變化,且當前可見,即從無到有。

TRANSIT_CLOSE:存在發生了變化,且當前不可見,即從有到無。

TRANSIT_TO_FRONT:存在沒有發生變化,且當前可見,說明從后臺移動到了前臺,從不可見變為了可見。

TRANSIT_TO_BACK:存在沒有發生變化,且當前不可見,說明從前臺移動到了后臺,從可見變為了不可見。

再根據Transition.reduceMode的邏輯:

在這里插入圖片描述

  • TRANSIT_TO_BACK和TRANSIT_CLOSE是一類的。
  • TRANSIT_TO_FRONT和TRANSIT_OPEN是一類的。
  • TRANSIT_CHANGE單獨一類。

如果動畫的大方向是一致的,那么即使TRANSIT_TO_BACK和TRANSIT_CLOSE的動畫有點差別,但是為了大局考慮,各別同志也不是不能適當調整一下來實現集體上的一致。

如果動畫的大方向都不一致,那么它們中的無論哪個肯定都是不能提升為它們的父容器的。比如TaskA想向左平移,TaskB想向右平移,那么如果擅自提升為父容器TaskDisplayArea,不管TaskDisplayArea向左還是向右平移肯定都不合適,這種矛盾就屬于不可調和了,那父容器TaskDisplayArea就不用管了,也就是別提升了,讓沖突的TaskA和TaskB自己玩去吧。

3)、最后總結一下檢查姊妹WindowContainer的這段邏輯,其實就是檢查所有的姊妹WindowContainer中,有沒有和當前WindowContainer沖突的姊妹WindowContainer,至于是否沖突則看是否滿足了以下條件之一:

  • 檢查所有沒有參與動畫的姊妹WindowContainer,看能否找到一個可見的。
  • 檢查所有參與了動畫的姊妹WindowContainer,看能否找到一個動畫的大方向和當前WindowContainer不一致。

只要找到了這么一個姊妹WindowContainer,我們就無法提升動畫的主體。

3 構建TransitionInfo對象

在這里插入圖片描述

這一節我感覺其實沒有什么好說的,大概介紹一下TransitionInfo以及它的內部類Change。

在這里插入圖片描述

1)、TransitionInfo,實現了Parcelable,結合注釋,用來收集WMCore這邊的Transition信息,用來同步給WMShell的TransitionPlayer。成員變量大概有這些:

在這里插入圖片描述

2)、TransitionInfo.Change,同樣實現了Parcelable,代表了WindowContainer在一個Transition期間的變化。看其成員變量,保存的信息還是挺多的,還有一個RunningTaskInfo的對象:

在這里插入圖片描述

再結合Transition.calculateTransitionInfo方法,很明顯就大概能弄懂這兩個類的作用:

1)、TransitionInfo,對應一個Transition對象,用來收集WMShell感興趣的Transition的信息,后續同步給WMShell。

2)、TransitionInfo.Change,對應一個Transition.ChangeInfo對象,用來收集WMShell感興趣的Transition.ChangeInfo的信息,后續同步給WMShell。

順便一提,google為啥不將Transition中ChangeInfo的命名為”Change“,將TransitionInfo中的Change命名為”ChangeInfo“呢,強迫癥犯了。

4 Transition移動到PLAYING狀態

在這里插入圖片描述

其實在Transition.onTransactionReady方法的開頭已經將Transition.mState狀態置為STATE_PLAYING,這里又調用了一個TransitionController.moveToPlaying方法,看下是干啥的:

在這里插入圖片描述

其實也非常簡單:

1)、開始動畫了,意味著當前Transition已經不能收集了,所以將TransitionController.mCollectingTransition置空。特別的,如果有其它Transition在排隊,那么就繼續將TransitionController.mCollectingTransition賦值為排隊隊列隊首的那個Transition,我播我的動畫,你收集你的WindowContainer,互不干擾。

2)、將當前Transition添加到TransitionController.mPlayingTransitions:

在這里插入圖片描述

一個當前處于playing狀態的Transition的隊列,也就是說playing的Transition可以有多個。

5 切換到WMShell:onTransitionReady

在這里插入圖片描述

在Transition.onTransactionReady方法的最后,調用了ITransitionPlayer.onTransitionReady方法將切換到了WMShell:

在這里插入圖片描述

切換到WMShell意味著Transition就緒階段已經結束,正式進入Transition的playing階段,Transitions.TransitionPlayerImpl.onTransitionReady就是我們下一篇文章的起點。

最后稍微看一下調用ITransitionPlayer.onTransitionReady方法之前調用的Transition.buildFinishTransaction方法:

在這里插入圖片描述

傳入的Transaction對象為Transition.mFinishTransaction,如該方法的注釋所說,這里對”finish transaction“的操作保證了動畫結束后,所有的”reparent“操作或者是Layer的變化將會得到重置,特別是Layer的幾何信息(位置、縮放、旋轉這些)。如果你的Layer在動畫結束的時候在Layer的這些信息上的確有變化,那就要注意不要讓這個方法把你對Layer的操作重置了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/46049.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/46049.shtml
英文地址,請注明出處:http://en.pswp.cn/web/46049.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Laravel API資源全解析:打造高效響應的秘訣

Laravel API資源全解析:打造高效響應的秘訣 在構建RESTful API時,資源的表示和管理是核心部分。Laravel框架通過API資源(API Resources)提供了一種優雅的方式來格式化和轉換模型數據,以適應API的響應需求。本文將深入…

Flink底層原理解析:案例解析(第37天)

系列文章目錄 一、flink架構 二、Flink底層原理解析 三、Flink應用場景解析 四、fink入門案例解析 文章目錄 系列文章目錄前言一、flink架構1. 作業管理器(JobManager)2. 資源管理器(ResourceManager)3. 任務管理器(Ta…

Laravel的魔法面具:深入解析門面(Facades)的神秘力量

Laravel的魔法面具:深入解析門面(Facades)的神秘力量 在Laravel的世界中,門面(Facades)是一種將類靜態方法調用轉換為動態方法調用的機制。它們提供了一種優雅的方式來與底層的Laravel服務容器進行交互&am…

Baum-Welch算法,也被稱為前向-后向算法,是一種用于訓練隱馬爾可夫模型(Hidden Markov Model, HMM)的重要算法。

Baum-Welch算法詳解與Python代碼示例 一、算法詳解 Baum-Welch算法,也被稱為前向-后向算法,是一種用于訓練隱馬爾可夫模型(Hidden Markov Model, HMM)的重要算法。HMM是一種統計模型,用于描述一個含有隱含未知參數的…

Java中創建對象的幾種方式

Java中創建對象的幾種方式 1. 使用new關鍵字 這是最直接、最常見的創建對象的方式。通過new關鍵字,Java會調用類的構造函數(Constructor)來初始化新創建的對象。 ClassName obj new ClassName();2. 使用clone()方法 如果類實現了Cloneab…

JavaScript統計字符串中出現次數最多的字符

思路&#xff1a;使用charAt()方法可以通過for循環來依次遍歷出字符串中的字符 將遍歷出來的字符作為一個空對象的屬性 如果該對象中該屬性沒有值則對其賦值為1 如果該對象中已經有值了則在原基礎上加1 最后通過for in循環比較對象中個屬性值大大小然后將其打印出來 <!DOCT…

liunx面試題目

如何看當前Linux系統有幾顆物理CPU和每顆CPU的核數&#xff1f; 查看物理cup&#xff1a; cat /proc/cpuinfo|grep -c ‘physical id’ 查看每顆cup核數 cat /proc/cpuinfo|grep -c ‘processor’ 若希望自動實現軟件包的更新&#xff0c;可以使用yum-cron并啟動該服務 yum -y …

網絡請求之urllib.request的使用(Get方式)

urllib.request用于打開和讀取url&#xff0c;模擬瀏覽器發送一個http請求&#xff0c;并獲取響應結果。 urllib.request.urlopen的語法格式&#xff1a; urlopen(url,dataNone,[timeout,]*,cafileNone,capathNone,cadefaultFalse,contextNone) 參數說明&#xff1a; url url…

C++ std::lock_guard和 std::unique_lock

二者都是 C 標準庫中用于管理互斥鎖&#xff08;mutex&#xff09;的 RAII&#xff08;Resource Acquisition Is Initialization&#xff09;機制的類。這些類可以確保互斥鎖在構造時被獲取&#xff0c;在析構時被釋放&#xff0c;從而避免死鎖和資源泄漏問題。不過&#xff0c…

Python基礎語法篇(上)

Python基礎語法&#xff08;上&#xff09; 一、基知二、基本數據類型&#xff08;一&#xff09;標準數據類型&#xff08;二&#xff09;數據類型轉換 三、字符串基本操作&#xff08;一&#xff09;字符串的索引和切片&#xff08;二&#xff09;字符串的拼接 三、運算符四、…

web安全之跨站腳本攻擊xss

定義: 后果 比如黑客可以通過惡意代碼,拿到用戶的cookie就可以去登陸了 分類 存儲型 攻擊者把惡意腳本存儲在目標網站的數據庫中(沒有過濾直接保存)&#xff0c;當用戶訪問這個頁面時&#xff0c;惡意腳本會從數據庫中被讀取并在用戶瀏覽器中執行。比如在那些允許用戶評論的…

【Karapathy大神build-nanogpt】Take Away Notes

B站翻譯LINK Personal Note Andrej rebuild gpt2 in pytorch. Take Away Points Before entereing serious training, he use Shakespear’s work as a small debugging datset to see if a model can overfit. Overfitging is a should thing.If we use TF32 or BF32, (by…

Ansys Zemax|探索OS中的物理光學傳播

概述 物理光學傳播 (Physical Optics Propagation, POP) 分析是OpticStudio序列模式中的一個強大的分析工具&#xff0c;它可以用來分析光束的傳播和光纖耦合的效率。這篇文章旨在介紹這一分析工具的功能&#xff0c;并向您展示一些具體的應用示例。本文同時為您介紹了如何使用…

有關電力電子技術的一些相關仿真和分析:⑦三相橋式電壓型PWM逆變器與直接/間接法控制單相全橋結構PWM整流器(MATLAB/Siumlink仿真)

1.1 題目一要求 以三相橋式電壓型PWM逆變器為對象,研究其在不同調制度下,輸出電壓的頻譜成分變化,依據仿真波形分析其工作時序。 參數要求:三相橋式逆變電路,直流側電壓800V,調制波頻率50HZ,開關頻率10kHZ,阻感負載R=10Ω,L=5mH。 1.2 題目二要求 以單相全橋結構P…

高效接入電商訂單API,掌握這些技巧輕松實現

受全渠道大趨勢的影響&#xff0c;很多實體商家紛紛開展電商業務&#xff0c;為了提升業務管理效率&#xff0c;想要在原有管理系統的基礎上通過接入電商訂單API接口&#xff0c;方便將線上線下的訂單進行統一管理&#xff0c;但各個電商平臺的電商訂單API接口那么多&#xff0…

allure_pytest:AttributeError: ‘str‘ object has no attribute ‘iter_parents‘

踩坑記錄 問題描述&#xff1a; 接口自動化測試時出現報錯&#xff0c;報錯文件是allure_pytest庫 問題分析&#xff1a; 自動化測試框架是比較成熟的代碼&#xff0c;報錯也不是自己寫的文件&#xff0c;而是第三方庫&#xff0c;首先推測是allure_pytest和某些庫有版本不兼…

彩電上自帶的推箱子游戲是什么編程語言開發的?

2000年左右的廈新彩電上&#xff0c;自帶了推箱子、華容道游戲。界面如下&#xff1a; 在線版推箱子游戲&#xff0c;網址&#xff1a;https://www.tuixiangzi.cn/ BASIC&#xff0c;全稱是Beginners All-purpose Symbolic Instruction Code&#xff0c;含義是初學者通用符號…

Ubuntu 添加gcc頭文件搜索路徑

對個人用戶生效 sudo vim ~/.bashrc在該文件末尾添加 #gcc C_INCLUDE_PATH$C_INCLUDE_PATH:your_path export C_INCLUDE_PATH #g CPLUS_INCLUDE_PATH$CPLUS_INCLUDE_PATH:your_path export CPLUS_INCLUDE_PATH最后&#xff0c;重啟終端即可生效 可用以下命令查看搜索路徑 ec…

深入了解Memcached:緩存技術的利器

文章目錄 深入了解Memcached&#xff1a;緩存技術的利器一、Memcached簡介什么是Memcached&#xff1f;Memcached的特點 二、Memcached的工作原理緩存機制分布式緩存 三、Memcached的架構客戶端與服務器數據存儲 四、Memcached的安裝與配置安裝Memcached在Linux上安裝在macOS上…

【IEEE出版順利申請中】2024年第四屆電子信息工程與計算機科學國際會議(EIECS 2024)

2024年第四屆電子信息工程與計算機科學國際會議(EIECS 2024) 2024 4th International Conference on Electronic Information Engineering and Computer Science 中國延吉 | 2024年9月27-29日 電子信息的出現與計算機技術、通信技術和高密度存儲技術的迅速發展并在各個領域里…