国产精品chinese,色综合天天综合精品网国产在线,成午夜免费视频在线观看,清纯女学生被强行糟蹋小说

    <td id="ojr13"><tr id="ojr13"><label id="ojr13"></label></tr></td>
        • <source id="ojr13"></source>
            <td id="ojr13"><ins id="ojr13"><label id="ojr13"></label></ins></td>

            當(dāng)前位置:文章中心>技術(shù)教程
            公告通知 新聞快遞 技術(shù)教程 產(chǎn)品展示

            淘寶 Android 幀率采集分析與監(jiān)控詳解

            發(fā)布時(shí)間:2022-01-24 點(diǎn)擊數(shù):1283


            APM  供給幀率的相關(guān)數(shù)據(jù),即 FPS(Frames Per Second) 數(shù)據(jù)。FPS 在必定程度上反映了頁面流通程度,但 APM 供給的 FPS  并不是很準(zhǔn)確。恰逢手淘低端機(jī)功能優(yōu)化項(xiàng)目開啟,亟需相關(guān)方針來衡量對(duì)滑動(dòng)體會(huì)的優(yōu)化,幀率數(shù)據(jù)探究實(shí)踐就此擺開。

            在探究實(shí)踐中,咱們遇到了許多問題:

            1. 高刷手機(jī)占比相對(duì)不低,影響全體 FPS 數(shù)據(jù)
            2. 非人為滑動(dòng)數(shù)據(jù)參雜在 FPS 中,不能直接表現(xiàn)用戶操作體會(huì)
            3. 核算均勻數(shù)據(jù)時(shí),卡頓數(shù)據(jù)被淹沒在海量正常數(shù)據(jù)中,一次卡頓是否只影響一個(gè) FPS 值仍是一次用戶操作體會(huì)?

            經(jīng)過一段時(shí)刻的探究,咱們沉積下來了一些方針,其間包括:滑動(dòng)幀率、凍幀占比、scrollHitchRate、卡頓幀率。除了相關(guān)幀率方針之外,為了更好的指導(dǎo)功能優(yōu)化,APM 還供給了幀率主因剖析,一同為了更好的定位卡頓問題,也供給了卡頓倉庫。

            image.png

            下面是 APM 根據(jù)渠道的特性,對(duì)幀率相關(guān)探究實(shí)踐的詳細(xì)介紹,期望本文可以給咱們帶來一些協(xié)助。
            體系烘托機(jī)制

            在介紹方針的完成之前,首要需求了解體系是如何做烘托的,只要知曉體系烘托機(jī)制,才干協(xié)助咱們更好的進(jìn)行幀率數(shù)據(jù)核算處理。

            烘托機(jī)制是  Android 中重要的一部分,其間又牽扯甚廣,包括咱們常說的 measure/layout/draw  原理、卡頓、過度繪制等,都與其相關(guān)。在這兒咱們首要是對(duì)烘托流程進(jìn)行全體了解,知曉后續(xù)需求核算哪幾部分、經(jīng)過體系 API  得到了哪幾部分,以便核算出方針數(shù)據(jù)。

            ?  烘托流程

            image.png

            咱們都知道,當(dāng)觸發(fā)烘托后,會(huì)走到  ViewRootImpl 的 scheduleTraversals。這時(shí),scheduleTraversals  方法首要是向  Choreographer 注冊(cè)下一個(gè) VSync 的回調(diào)。當(dāng)下一個(gè) VSync 來暫時(shí),Choreographer 首要切到主線程(傳  VSync 上來的 native  代碼不運(yùn)轉(zhuǎn)在主線程),當(dāng)然它并不是直接給 Looper sendMessage,而是  msg.setAsynchronous(true) ,提高了 UI 的響應(yīng)速率。

            當(dāng)切到主線程后,Choreographer 開端履行一切注冊(cè)了這個(gè) VSync 的回調(diào),回調(diào)類型分為以下四種:

            1. CALLBACK_INPUT,輸入事情
            2. CALLBACK_ANIMATION,動(dòng)畫處理
            3. CALLBACK_TRAVERSAL,UI 分發(fā)
            4. CALLBACK_COMMIT

            Choreographer 會(huì)將一切的回調(diào)按類型分類,用鏈表來組織,表頭存在一個(gè)巨細(xì)固定的數(shù)組中(因?yàn)橹恢芜@四種回調(diào))。在 VSync 發(fā)送到主線程的音訊中,就會(huì)一條鏈表一條鏈表的取出順序履行并清空。

            而在  scheduleTraversals 注冊(cè)的便是 CALLBACK_TRAVERSAL 類型的 callback,這個(gè) callback  中履行的便是咱們最為熟悉的 ViewRootImpl#doTraversal() 方法,doTraversal 方法中調(diào)用了  performTraversals  方法,performTraversals 方法中最重要的便是調(diào)用了耳熟能詳?shù)? performMeasure、performLayout、performDraw 方法。

            詳細(xì)代碼可以翻看: android.view.Choreographer 和 android.view.ViewRootImpl

            從這兒咱們可以看到,想要上屏一幀數(shù)據(jù),至少包括:VSync 切到主線程的耗時(shí)、處理輸入事情的耗時(shí)、處理動(dòng)畫的耗時(shí)、處理 UI 分發(fā)(measure、layout、draw)的耗時(shí)。

            可是,當(dāng) draw 流程完畢,僅僅 CPU 核算部分完畢,接下來會(huì)把數(shù)據(jù)交給 RenderThread 來完成 GPU 部分作業(yè)。

            ?  屏幕改寫

            Android 4.1 引進(jìn)了 VSync 和三緩沖機(jī)制,VSync 給予開端 CPU 核算的機(jī)遇,以及 GPU 和 Display 交流的緩沖區(qū)的機(jī)遇,這樣有利于充分運(yùn)用時(shí)刻來處理數(shù)據(jù)和削減 jank。
            image.png

            上圖中 A、B、C 分別代表著三個(gè)緩沖區(qū)。咱們可以看到 CPU、GPU、顯現(xiàn)器都能盡快拿到 buffer,削減不必要的等候。假如顯現(xiàn)器和 GPU 現(xiàn)在都運(yùn)用著一個(gè) buffer,假如下一次烘托開端了,因?yàn)檫€有一個(gè) buffer 可以用于 CPU 數(shù)據(jù)的寫入,所以可以馬上開端下一幀數(shù)據(jù)的烘托,例如圖中第一個(gè) VSync。

            是不是引進(jìn)三緩沖機(jī)制就沒有任何問題呢,當(dāng)咱們仔細(xì)看上圖可發(fā)現(xiàn),數(shù)據(jù) A 在第三個(gè) VSync 來暫時(shí)就現(xiàn)已準(zhǔn)備好,隨時(shí)可以改寫到屏幕上,到實(shí)在刷到屏幕卻是第四個(gè) VSync 降臨。由此可知,三緩沖盡管有效運(yùn)用了等候 VSync 的時(shí)刻,削減了 jank,可是帶來了推遲。

            這兒僅僅簡略帶咱們回顧了這塊的知識(shí),建議咱們翻下發(fā)展的歷史,知其然亦要知其所以然。

            對(duì)幀數(shù)據(jù)信息的挖掘

            當(dāng)咱們知道了整個(gè)體系烘托的流程后,咱們需求監(jiān)控什么,怎樣監(jiān)控,這是一個(gè)問題。

            ?  業(yè)界計(jì)劃

            image.png

            • APM 原始計(jì)劃

            當(dāng)收到  Touch 事情后,APM 會(huì)收集頁面 1s 內(nèi) draw 的次數(shù)。這個(gè)計(jì)劃的長處是功能損耗低,可是存在致命缺點(diǎn)。假如頁面烘托總時(shí)長不足 1s  就停止改寫,會(huì)導(dǎo)致數(shù)據(jù)人為偏低。其次,觸碰屏幕不必定會(huì)帶來改寫,改寫也不必定是 Touch 事情帶來的。而以上狀況核算出來的都是臟數(shù)據(jù)。

            可是,Android 在 ViewRootImpl 完成了一個(gè)Debug 的 FPS 計(jì)劃,原理與上訴計(jì)劃類似,都是在 draw 時(shí)累積時(shí)長到 1s,所以,假如是想要一個(gè)低本錢功能無損的線下測驗(yàn) FPS,這不失為一個(gè)計(jì)劃。

            感興趣可以看 ViewRootImpl 的 trackFPS 方法。

            • Matrix

            在幀率這部分,Matrix  立異性的 hook 了 Choreographer 的 CallbackQueue,一同還經(jīng)過反射調(diào)用 addCallbackLocked  在每一個(gè)回調(diào)行列的頭部添加了自界說的 FrameCallback。假如回調(diào)了這個(gè) Callback,那么這一幀的烘托也就開端了,當(dāng)時(shí)在  Looper 中正在履行的音訊便是烘托的音訊。這樣除了監(jiān)控幀率外,還能監(jiān)控到當(dāng)時(shí)幀的各個(gè)階段耗時(shí)數(shù)據(jù)。

            除此之外,幀率回調(diào)和 Looper 的 Printer 結(jié)合運(yùn)用,可以在呈現(xiàn)卡頓幀的時(shí)分去 dump 主線程信息,便于事務(wù)方處理卡頓,可是頻繁拼接字符串會(huì)帶來必定的功能開支(println 方法調(diào)用時(shí)有字符串拼接)。

            • 常規(guī)

            運(yùn)用 Choreographer.FrameCallback 的 doFrame(frameTimeNanos: Long) 方法,在每一次的回調(diào)里核算兩幀之差,經(jīng)過核算可以得到 FPS。

            ?  滑動(dòng)幀率

            FPS 是業(yè)界簡略而又通用的一個(gè)方針,是 Frames Per Second 的簡寫,即每秒烘托幀數(shù),淺顯來講便是每秒烘托的畫面數(shù)。

            核算出 FPS 并不是咱們的方針,咱們一直期望核算出的是滑動(dòng)幀率,針對(duì) FPS,咱們更為關(guān)注的是用戶在交互進(jìn)程中的幀率,監(jiān)控這一類幀率才干更好反映用戶體會(huì)。

            首要,面臨之前的收集計(jì)劃,根本不能收集出契合界說的 FPS,所以原始的計(jì)劃就必須要進(jìn)行舍棄,需求進(jìn)行重新規(guī)劃。當(dāng)看到 Matrix 的計(jì)劃時(shí),覺得想法很棒,可是過分 hack,咱們更傾向于維護(hù)本錢更低、穩(wěn)定性高的體系開放 API。

            所以,在挑選上,咱們?nèi)允菦Q議運(yùn)用最一般的 Choreographer.FrameCallback 進(jìn)行完成。當(dāng)然,它不是最完美的,可是可以盡量在規(guī)劃上去避免這種缺點(diǎn)。

            那咱們?cè)鯓雍怂愠鲆粋€(gè) FPS 值呢?

            image.png

            Choreographer.FrameCallback 被回調(diào)時(shí),doFrame 方法都帶上了一個(gè)時(shí)刻戳,核算與上一次回調(diào)的差值,就可以將之視之為一幀的時(shí)刻。當(dāng)累加超越 1s 后,就可以核算出一個(gè) FPS 值。

            在這個(gè)進(jìn)程中,有個(gè)點(diǎn)要咱們知曉,doFrame 在什么機(jī)遇回調(diào):


            首要,咱們每一次回調(diào)后,都需求對(duì)  Choreographer 進(jìn)行 postFrameCallback 調(diào)用,而調(diào)用 postFrameCallback 便是鄙人一幀  CALLBACK_ANIMATION 類型的鏈表上進(jìn)行添加一個(gè)節(jié)點(diǎn)。所以,doFrame 回調(diào)機(jī)遇并不是這一幀開端核算,也不是這一幀上屏,而是 CPU 處理動(dòng)畫進(jìn)程中的一個(gè) callback。

            當(dāng)核算出一個(gè) FPS 值后,就需求在上面疊加以下狀況了:

            • View 滑動(dòng)幀率

            在最開端完成時(shí),View 只需滑動(dòng)就監(jiān)控幀率,一直幀率產(chǎn)出到不滑動(dòng)停止。根據(jù)需求,咱們的幀率收集就變成了如下這樣:

            image.png

            那怎樣監(jiān)控 View 是否有滑動(dòng)呢?那就需求介紹一下這個(gè) ViewTreeObserver.OnScrollChangedListener。畢竟只要了解完成原理,才干決議是否可用。

            // ViewRootImpl#draw private void draw(boolean fullRedrawNeeded) {  // ...  if (mAttachInfo.mViewScrollChanged) {  mAttachInfo.mViewScrollChanged = false;  mAttachInfo.mTreeObserver.dispatchOnScrollChanged();  }  // ...  mAttachInfo.mTreeObserver.dispatchOnDraw();  // ...  }

            咱們可以看到,在  ViewRootImpl#draw 中,判斷了 mAttachInfo 信息中 View  是否發(fā)生了滑動(dòng),假如發(fā)生滑動(dòng)就分發(fā)出來。那么什么時(shí)分設(shè)置的 View 位置變化(發(fā)生滑動(dòng))的呢?在 View 的 onScrollChanged  被調(diào)用的時(shí)分:

             // View#onScrollChanged  protected void onScrollChanged(int l, int t, int oldl, int oldt) {  // ...  final AttachInfo ai = mAttachInfo;  if (ai != null) {  ai.mViewScrollChanged = true;  }  // ...  }

            onScrollChanged 就直接連接著 View#scrollTo 和 View#scrollBy,在大多數(shù)場景下,現(xiàn)已滿足通用。

            根據(jù)咱們之前講解的烘托流程:咱們可以看到  ViewTreeObserver.OnScrollChangedListener 的回調(diào)是在 ViewRootImpl#draw 中,那么  Choreographer.FrameCallback 的回調(diào)先于  ViewTreeObserver.OnScrollChangedListener 的。

            關(guān)于單幀,就可以如下表示:

            image.png

            這樣,每一幀都帶上了是否滑動(dòng)的狀況,當(dāng)某一幀是滑動(dòng)的幀,就可以開端計(jì)數(shù),一直累積時(shí)刻到 1s,一個(gè)滑動(dòng)幀率數(shù)據(jù)核算出來就出來了。

            • 手指滑動(dòng)幀率

            View 滑動(dòng)幀率,在線下驗(yàn)證時(shí),與測驗(yàn)渠道出的數(shù)據(jù)共同,并且可以契合基本需求,檢驗(yàn)經(jīng)過。上線后,也開端了運(yùn)轉(zhuǎn),并可以承擔(dān)起幀率相關(guān)作業(yè)。

            可是,View 滾動(dòng)并不代表著是用戶操作導(dǎo)致,數(shù)據(jù)一直不全是用戶體會(huì)的效果。所以,咱們開端完成手指的滑動(dòng)幀率。

            手指滑動(dòng)幀率,首要咱們需求可以接納到手指的 Touch 行為。由于 APM 中已有對(duì) Callback 的 dispatchTouchEvent 接口的 hook,所以決議直接運(yùn)用此接口識(shí)別手指滑動(dòng)。

            image.png

            這個(gè)時(shí)分,咱們需求知道幾個(gè)機(jī)遇問題:

            1. 有 dispatchTouchEvent 不會(huì)立馬發(fā)生 doFrame
            2. 經(jīng)過 dispatchTouchEvent 核算移動(dòng)時(shí)刻/距離超越 TapTimeout/ScaledTouchSlop,不必定立馬發(fā)生 doFrame

            所以,經(jīng)過  dispatchTouchEvent 核算移動(dòng)時(shí)刻/距離超越 TapTimeout/ScaledTouchSlop 時(shí),只會(huì)給一個(gè)  flag,告訴后邊的 ViewTreeObserver.OnScrollChangedListener 的 doFrame  可以開端核算成手指滑動(dòng)幀率。

            • 功能優(yōu)化/滑動(dòng)次數(shù)識(shí)別

            咱們?cè)谑盏矫恳粠?doFrame 回調(diào)后,都需求重新 postFrameCallback。每一次 postFrameCallback 都會(huì)注冊(cè) VSync(假如沒有被注冊(cè)),當(dāng) Vsync 降臨后,會(huì)給主線程拋一個(gè)音訊,這勢(shì)必會(huì)給主線程帶來必定的壓力。

            眾所周知,體系在頁面靜止的時(shí)分是不會(huì)進(jìn)行烘托的,也就不會(huì)有 VSync 被注冊(cè)。那么在沒有烘托的時(shí)分,是否也需求 post 呢?不需求,沒有意義,是可以過濾掉的。根據(jù)這個(gè)理念,咱們對(duì)滑動(dòng)幀率的核算進(jìn)行了優(yōu)化。

            image.png

            需求削減非必要的幀回調(diào)與注冊(cè),就需求清晰幾個(gè)問題:

            1. 起點(diǎn)(什么時(shí)分開端 postFrameCallback):在第一次收到 scroll 事情的時(shí)分(onSrollChanged)
            2. 結(jié)尾(什么時(shí)分不再 postFrameCallback):在核算完一個(gè)手指滑動(dòng) FPS 后,假如下一幀不再滑動(dòng),那么就停止注冊(cè)下一幀的回調(diào)。

            假如細(xì)心的話,就會(huì)發(fā)現(xiàn),這兒的起點(diǎn)可以以為是手指帶來的滑動(dòng)的烘托起點(diǎn),這兒的結(jié)尾可以以為是手指帶來的滑動(dòng)的烘托結(jié)尾(包括了 Fling),這個(gè)數(shù)據(jù)很重要,咱們相當(dāng)于識(shí)別了一次手指滑動(dòng),并且可以供給每次手指滑動(dòng)的耗時(shí)等數(shù)據(jù)。

            這樣進(jìn)行優(yōu)化是否就完美無缺呢?其實(shí)不是的,仔細(xì)看上圖的核算開端時(shí)刻點(diǎn),就會(huì)發(fā)現(xiàn):損失了開端滑動(dòng)的第一幀數(shù)據(jù)。因?yàn)樵蹅兒怂愕氖莾纱? doFrame 回調(diào)的差值,即便知道當(dāng)時(shí)這一幀是需求核算的幀,可是沒有上一幀的時(shí)刻戳,也就無法核算出開端滑動(dòng)的這一幀實(shí)在的耗時(shí)。
            ?  凍動(dòng)占比

            凍幀是 Google 官方界說的一種幀:

            Frozen frames are UI frames that take longer than 700ms to render.

            凍幀作為一種特別的幀,不是被強(qiáng)烈建議不要呈現(xiàn)的幀,在華為等文檔中也被提及過。一旦呈現(xiàn)此類幀,頁面也就像凍住似的。所以,在 APM 中,也將這一類特別的幀納入監(jiān)控規(guī)模,核算出凍幀占比:

            凍幀占比 = 滑動(dòng)進(jìn)程中的凍幀數(shù)量 / 滑動(dòng)發(fā)生的幀數(shù)

            ?  scrollHitchRate

            scrollHitchRate 概念來自于 iOS,首要是用于描述滑動(dòng)進(jìn)程中,hitch 時(shí)長的占比。什么叫 hitch?可以簡略理解為單個(gè)幀耗時(shí)超越了烘托標(biāo)準(zhǔn)耗時(shí)的部分便是 hitch。image.png

            核算公式如圖所示:image.png

            這兒的分子是指整個(gè)滑動(dòng)進(jìn)程中,hitch 的累加值,這兒的分母便是整個(gè)滑動(dòng)耗時(shí)(包括 Fling)。

            咱們可能會(huì)問: 那為什么不必FPS? 不是可以用 fps 來檢測滑動(dòng)卡頓狀況么,為什么還要有一個(gè) Hitch rate ?
            這是因?yàn)? FPS 并不適用于一切的狀況。比如當(dāng)一個(gè)動(dòng)畫中有停頓時(shí)刻, FPS 就無法反應(yīng)該動(dòng)畫的流通程度,并且并不是一切的運(yùn)用都以達(dá)到 60  fps/120 fps 為方針,比如有些游戲只想以 30 fps 運(yùn)轉(zhuǎn)。而關(guān)于 Hitch rate 而言,咱們的方針永遠(yuǎn)是讓它達(dá)到 0。

            引進(jìn) scrollHitchRate 單純?yōu)榱颂幚砀咚⑹謾C(jī)的數(shù)據(jù)紛歧致問題嗎?不是的。咱們?cè)谑占揭粋€(gè) scrollHitchRate 數(shù)據(jù),還隱式的帶上了滑動(dòng)次數(shù)。例如,在手淘場景下,主頁同學(xué)咨詢過一個(gè)問題,會(huì)不會(huì)頁面越往下刷,卡得越嚴(yán)峻?當(dāng)收集到這個(gè)數(shù)據(jù)后,就可以進(jìn)行回答了。

            ?  幀率主因剖析


            無論是滑動(dòng)幀率,仍是凍幀,更多的仍是傾向于監(jiān)控?cái)?shù)據(jù),假如想要在數(shù)據(jù)上剖分出當(dāng)時(shí)幀率低的首要原因仍是沒有方法入手的。

            在之前烘托流程中,就講到烘托流程首要分紅哪幾步,假如可以將烘托流程的每一步都進(jìn)行監(jiān)控,那么咱們就可以以為:當(dāng)某一個(gè)異常幀呈現(xiàn)后,首要問題呈現(xiàn)在哪一個(gè)階段了,可是咱們?nèi)允瞧谕?strong>不要像 Matrix 那樣侵入體系代碼。根據(jù)這個(gè)思路,咱們發(fā)現(xiàn)體系供給了滿足咱們需求的 API:Window.OnFrameMetricsAvailableListener。Google Firebase 也同樣在運(yùn)用這個(gè) API 進(jìn)行幀數(shù)據(jù)監(jiān)控,也不太會(huì)有后續(xù)的兼容性問題。

            FrameMetrics,開發(fā)文檔見 https://developer.android.com/reference/android/view/FrameMetrics

            在異步回調(diào)給的 FrameMetrics 數(shù)據(jù)中,會(huì)告訴咱們每一幀每一個(gè)階段的耗時(shí),十分契合咱們的監(jiān)控訴求??墒侨匀挥袃蓚€(gè)問題值得注重:

            • FrameMetrics API 是在 Android 24 上供給的,查看手淘用戶數(shù)據(jù)可以發(fā)現(xiàn),可以滿足基本需求;
            • 一幀數(shù)據(jù)處理不及時(shí)會(huì)有丟數(shù)據(jù)的危險(xiǎn),但可以經(jīng)過接口知曉丟掉了幾幀數(shù)據(jù)。

            下面咱們就詳細(xì)查看下 FrameMetrics 數(shù)據(jù)中界說了哪些烘托階段:

            FrameMetrics 參數(shù)常量 含義
            UNKNOWN_DELAY_DURATION 等候主線程耗時(shí)(VSync來了需求切換線程)
            INPUT_HANDLING_DURATION 輸入事情處理耗時(shí)
            ANIMATION_DURATION 動(dòng)畫處理耗時(shí)
            LAYOUT_MEASURE_DURATION layout & measure 耗時(shí)
            DRAW_DURATION draw耗時(shí)
            SYNC_DURATION sync耗時(shí)
            COMMAND_ISSUE_DURATION issue耗時(shí)
            SWAP_BUFFERS_DURATION 交流行列耗時(shí)
            TOTAL_DURATION 總耗時(shí)

            摘錄自 Android 26。除上訴提及的字段此,還有幾個(gè)比較不錯(cuò)的時(shí)刻戳字段,也可以探究出一些新奇的玩法,咱們可以一同探究下。

            咱們有沒有發(fā)現(xiàn),跟烘托流程一模一樣。在盯梢了下相關(guān)源碼后,注冊(cè)一個(gè) listener,并沒有太多的功能損耗,F(xiàn)rameMetrics 內(nèi)部記載的時(shí)刻戳即便不注冊(cè)也會(huì)進(jìn)行收集,所以不會(huì)帶來額外的功能開支。

            首要咱們界說了一個(gè)需求進(jìn)行剖析的幀耗時(shí)閾值,超越這個(gè)閾值就可以以為需求核算原因。咱們界說:當(dāng)一幀某一個(gè)階段耗時(shí)超越閾值一半即為主因,反之則主因不存在。

            如此一來,針對(duì)某一個(gè) Activity 就可以剖分出是主線程卡頓導(dǎo)致幀率低,仍是布局問題導(dǎo)致 layout & measure 慢,亦或是 draw 有問題,在功能優(yōu)化時(shí),直接鎖定主因進(jìn)行優(yōu)化。

            ?  卡頓幀率
            首要咱們?cè)賮砘仡櫼幌氯搜鄣目D感知。原理上,高的幀率可以得到更流通、更逼真的動(dòng)畫,要生成平滑連貫的動(dòng)畫效果,幀速不能小于8FPS;每秒鐘幀數(shù)越多,所顯現(xiàn)的動(dòng)畫就會(huì)越流通。一般來說人眼能繼續(xù)保存其印象1/24秒左右的圖像,所以一般電影的幀速為24FPS。相關(guān)于游戲而言,無論幀率有多高,60幀或120幀,最終一般人能分辯到的不會(huì)超越30幀。電影盡管只要24幀每秒,但由于每兩幀之間的距離均為1/24秒,所以人眼不不會(huì)感覺到顯著的卡頓,游戲或許咱們界面的改寫即便達(dá)到30幀每秒,但假如這一秒鐘內(nèi),30幀不是均勻分配,就算是每秒60幀,其間59幀都十分流通,而有一幀延時(shí)超越1/24秒,仍然會(huì)讓咱們感覺到顯著的卡頓。

            這便是咱們界面上大部分狀況下都現(xiàn)已滑動(dòng)的十分流通,可是偶然仍是會(huì)察覺到卡頓的原因。依照1/24秒的話,幀時(shí)刻在41.6ms,假如中間有超越41.6ms的話,咱們是可以感覺到卡頓的,假如依照1/30的話,幀時(shí)刻在33.3ms,假如某一幀的推遲時(shí)刻超越了33.3ms,那么人眼就容易察覺到這個(gè)進(jìn)程,為了把這些卡頓的狀況反映出來,咱們需求在遇到這些幀的時(shí)分做一些記載??墒羌偃缭蹅儍H僅去記載進(jìn)程中那些耗時(shí)超越33.3ms的幀,這種狀況下,一方面會(huì)丟失掉時(shí)刻的要素,很難去衡量卡頓的嚴(yán)峻性(畢竟一段時(shí)刻內(nèi)不間斷的呈現(xiàn)卡頓,比偶然掉一幀要讓人顯著很多),另一方面,因?yàn)橛卸嘀鼐彌_區(qū)的影響,未必100%會(huì)掉幀,所以咱們僅僅取這個(gè)超越某一時(shí)刻的幀未必是準(zhǔn)確的。

            根據(jù)以上的考慮,這兒運(yùn)用了一個(gè)瞬時(shí)FPS的概念用于衡量卡頓,瞬時(shí)FPS便是在滑動(dòng)進(jìn)程中發(fā)生的一些耗時(shí)比較小的區(qū)間中核算的值。例如用戶滑動(dòng)了500ms,這個(gè)進(jìn)程可能會(huì)呈現(xiàn)幾個(gè)用戶核算的瞬時(shí)FPS。這個(gè)進(jìn)程是怎樣核算的?

            1. 滑動(dòng)進(jìn)程取得每一幀的時(shí)刻距離;
            2. 依照100(99.6ms,6幀的時(shí)刻)毫秒左右的時(shí)刻細(xì)化卡頓區(qū)間;
            3. 從時(shí)刻距離大于33.3毫秒的幀開端記載,作為區(qū)間起點(diǎn);
            4. 完畢點(diǎn)是從起點(diǎn)開端的幀耗時(shí)相加,達(dá)到99.6ms并且后邊的一幀耗時(shí)小于17毫秒(或許抵達(dá)最終一幀),否則會(huì)繼續(xù)尋找完畢點(diǎn);
            5. 這段時(shí)刻內(nèi)在核算幀率,是這兒要尋找的卡頓幀率。

            image.png

            可以看到有3幀顯著超出比較多。依照以前的核算方法,幀耗時(shí):1535ms,   幀數(shù)量是:83,那么這個(gè)界面的FPS是54。咱們可以看到幀率的FPS比較高,完全看不到卡頓了,即便前面有一些比較高的耗時(shí)幀,可是被后續(xù)耗時(shí)正常的幀給均勻掉了。所以以前的核算方法現(xiàn)已不能反映出這些卡頓問題。

            依照新的核算方法,應(yīng)該是從第7幀開端核算第一個(gè)瞬時(shí)FPS區(qū)間,從這一幀開端,核算至少99.6ms的時(shí)刻,那么69+16+15,現(xiàn)已達(dá)到了100ms,3幀,所以FPS是30,因?yàn)榈陀?0,所以這一次FPS會(huì)比記載,其間最大的幀耗時(shí)是69ms。
            第二次從17幀開端,5幀114ms,F(xiàn)PS為43ms,最大幀距離是61ms。
            第三次從26幀開端,98+10=108ms,可是后邊幀的耗時(shí)時(shí)刻為19ms,超越16.6ms,所以仍然會(huì)加入一同核算。3幀,127ms,F(xiàn)PS為23。最大幀距離是98。
            依照這次的核算,總共有3次卡頓FPS,分別是30,43,23,最大的幀耗時(shí)幀是98。
            ?  卡頓倉庫

            假如運(yùn)用主線程的  Looper Printer 來進(jìn)行卡頓倉庫 dump,會(huì)因?yàn)榇罅康淖址唇佣鴰砉δ軗p耗。在 Android 10 上,Looper  中新增 Observer,可以功能無損的回調(diào),但由于是 hide 的 API,則無法運(yùn)用。最終的方法只能是不斷向主線程 post  音訊,可每隔一段時(shí)刻就給主線程拋音訊又會(huì)給主線程帶來壓力。

            是否有更好的方法呢?有的,經(jīng)過 Choreographer postFrameCallback,自身就會(huì) post 主線程音訊,運(yùn)用兩次回調(diào)之間的差值高于某一個(gè)閾值,就可以以為是卡頓。并且這個(gè)識(shí)別的卡頓,仍是滑動(dòng)進(jìn)程中的卡頓。

            知道什么是卡頓,那什么時(shí)分  dump 呢?咱們運(yùn)用了 watchdog 的機(jī)制 dump 出卡頓倉庫,即在子線程 post 一個(gè) dump  主線程的音訊,假如單幀耗時(shí)超越閾值就進(jìn)行 dump,假如在規(guī)則時(shí)刻內(nèi)完成當(dāng)時(shí)幀,就取消 dump  的音訊。當(dāng)咱們收集上來倉庫后,咱們會(huì)將卡頓的倉庫進(jìn)行聚類,便于更好的決議首要矛盾、告警處理。

            對(duì)幀數(shù)據(jù)運(yùn)用的探究

            AB 與 APM 結(jié)合運(yùn)用

            上文首要仍是講解了咱們?cè)鯓雍怂愠鲆粋€(gè)方針、怎樣去排查問題,可是關(guān)于一個(gè)大盤方針而言,重之又重的當(dāng)然是需求用來衡量優(yōu)化效果的,那怎樣去衡量優(yōu)化呢?最好的手段是 AB。APM 方針數(shù)據(jù)與 AB 測驗(yàn)渠道打通,功能數(shù)據(jù)隨 APM 試驗(yàn)產(chǎn)出。

            這兒的AB渠道包括一休渠道、魔兔2渠道,一休渠道方針接入方法運(yùn)用的是自界說方針,幀率僅僅作為方針之一接入,啟動(dòng)、頁面等數(shù)據(jù)亦是其間之一。

            一休是阿里集團(tuán)一站式A/B試驗(yàn)的服務(wù)渠道,向各個(gè)事務(wù)供給了可視化的操作界面、科學(xué)的數(shù)據(jù)剖析、自動(dòng)化的試驗(yàn)陳述等一站式的試驗(yàn)流程;經(jīng)過科學(xué)的試驗(yàn)方法和實(shí)在的用戶行為來驗(yàn)證最佳處理計(jì)劃,然后驅(qū)動(dòng)事務(wù)增長。

            咱們?cè)谶M(jìn)行頁面功能優(yōu)化時(shí),可以直接運(yùn)用相關(guān)方針對(duì)基準(zhǔn)桶與優(yōu)化桶進(jìn)行對(duì)比,直接而又顯著的顯現(xiàn)對(duì)頁面功能的優(yōu)化。

            image.png

            寫在最終

            關(guān)于手淘功能監(jiān)控而言,幀率監(jiān)控、卡頓監(jiān)控僅僅功能監(jiān)控其間的一小環(huán),打磨好每一個(gè)細(xì)節(jié)也至關(guān)重要。相關(guān)數(shù)據(jù)除了與  AB  渠道搭配運(yùn)用之外,現(xiàn)已與全鏈路排查數(shù)據(jù)、輿情數(shù)據(jù)、版別發(fā)布功能關(guān)口相打通,借用后臺(tái)聚類、告警、自動(dòng)化郵件陳述等數(shù)據(jù)手段透出,專有數(shù)據(jù)渠道進(jìn)行承接。關(guān)于數(shù)據(jù)的態(tài)度,咱們不僅是要有,并且要全面而強(qiáng)壯。

            在一輪又一輪的技能迭代下,手淘的高可用表現(xiàn)也不斷完善與重構(gòu),期望在未來,手淘客戶端高可用相關(guān)數(shù)據(jù)可以更好的助力研制各個(gè)環(huán)節(jié),預(yù)防用戶體會(huì)腐化,協(xié)助不斷提升用戶體會(huì)。