1. 程式人生 > >【Bugly乾貨分享】那些年我們用過的顯示效能指標

【Bugly乾貨分享】那些年我們用過的顯示效能指標

注:Google 在自己文章中用了 Display Performance 來描述我們常說的流暢度,為了顯得有文化,本文主要用“顯示效能”一詞來代指“流暢度”(雖然兩者在概念上有細微差別)。

從 Android 誕生的那一刻起,流暢度就為眾人所關注。一時之間,似乎所有人都在討論 Android 和 iOS 誰的流暢度更好。但是,毫不誇張的說,流暢度絕對是 Android 眾多效能維度中最為奇葩的一個。因為,為了刻畫這一效能維度,業界設計了各式各樣的指標來對其進行衡量。可以說弄清了這些指標我們就明白了什麼是流暢度,可是這似乎並不太容易。

筆者簡單蒐集了一些業界中提及的顯示效能指標,大家可以來品評一下:

指標名稱:Jankiness count、Max accumulated frames、Frame rate
相關資料JankTestBase.java

指標名稱:SM、Skipped frames
相關資料Android應用效能評測調優

面對如此之多的顯示效能指標,想必大家也會跟筆者一樣,心中難免疑惑叢生。其實,我們只需要依次弄清楚以下三個哲學問題,所有的問題也許就會迎刃而解:

  • 你是誰——這些指標具體反映了什麼問題
  • 你從哪兒來——這些指標數值是怎麼得到的
  • 你要到哪兒去——這些指標如何落地來指導優化

因此,本文將嘗試依次從上訴三個問題來逐步分析和探討各個顯示效能指標。

Step 1:你是誰——這些指標具體反映了什麼問題

總所周知,脫離了具體的應用背景,所有的指標都是沒有意義的。所以,為了徹底弄清楚各個顯示效能指標的具體身份,我們勢必得從 Android 的影象渲染流程說起。

具體展開之前,首先需要說明的是,為了降低複雜程度和本章篇幅,在這個環節之中,我們只討論影象渲染流程中的各個具體環節所對應的指標有哪些。而指標的具體定義,由第二章《你從哪兒來——這些指標數值是怎麼得到的》進行討論。

Android 影象渲染流程

下圖是筆者結合各類資料(主要是是原始碼及官方文件),在根據自己的理解梳理出的幾種常見場景下的影象渲染流程:

PS 1:筆者個人技術水平有限,若存在理解有誤的地方還望指正。
PS 2:本文主要討論的 Android 原始碼為 Android 6.0

bugly

備註:基於 OpenGL 的應用可以使用 Choreographer 中的 VSYNC 訊號來進行影象渲染工作的安排。

上面這幅圖涉及的概念較多,要完全吃透估計得費不少時間。不過好在我們只是想弄明白顯示效能各種指標的含義,所以我們只需要理清下面兩大關係即可:

  • SurfaceFlinger、HWComposer與Surface的關係

    • Surface:可以理解為Android系統中的一個基本顯示單元。只要使用Android任意一種API繪圖,繪製的結果都將反映在Surface上。
    • SurfaceFlinger:服務執行在System程序中,用來統一管理系統的幀緩衝區裝置,其主要作用是將系統中的大部分Surface進行合成。SurfaceFlinger主要使用GPU進行Surface的合成,合成的結果將形成一個FrameBuffer。
    • HWComposer:即Hardware Composer HAL,其作用是將SurfaceFlinger通過GPU合成的結果與其他Surface一起最終形成BufferQueue中的一個Buffer。此外,HWComposer可以協助SurfaceFlinger進行Surface的合成,但是否進行協助是由HWComposer決定的。
    • 值得注意的是,有的Surface不由WindowManager管理,將直接作為HWComposer的輸入之一與SurfaceFlinger的輸出做最後的合成。
  • Choreographer、SurfaceFlinger、HWComposer與VSYNC的關係

    • VSYNC:Vertical Synchronization的縮寫,它的作用是使GPU的渲染頻率與顯示器的重新整理頻率(一般為固定值)同步從而避免出現畫面撕裂的現象。
    • HWComposer:VSYNC訊號主要由HWComposer通過硬體觸發。
    • Choreographer:當收到VSYNC訊號時,Choreographer將按優先順序高低依次去呼叫使用者通過postCallback提前設定的回撥函式,它們分別是:優先順序最高的CALLBACK_INPUT、優先順序次高的CALLBACK_ANIMATION以及優先順序最低的CALLBACK_TRAVERSAL。
    • SurfaceFlinger:Surface的合成操作也時基於VSYNC訊號進行的。

簡單來說,Android 影象渲染流程主要由以下特徵:

  1. 我們可以簡單把 Android 影象渲染架構分為應用(Surface)、系統(SurfaceFlinger)、硬體(Screen)三個層級,其中繪製在應用層,合成及提交上屏在系統層,顯示在硬體層;
  2. 無論應用(Surface)、系統(SurfaceFlinger)、硬體(Screen)都是當且僅當繪製內容發生改變,才會對繪製內容進行處理;
  3. 系統中的 SurfaceFlinger 以及絕大部分 Surface 都是按照 VSYNC 訊號的節奏來安排自己的任務;
  4. 目前,絕大部分 Surface 都屬於 Hardware Rendering。

各個指標在 Android 影象渲染流程所代表的意義

大致梳理了 Android 的影象渲染流程之後,我們需要做的一件事情,就是看看上面提到的指標,都對應了渲染流程的哪些階段,這樣對於我們瞭解各個指標所反映的具體物理意義及其優勢劣勢都有極大幫助。再次強調,在這個環節之中,我們的討論僅限於只討論指標所對應的渲染流程的具體階段,各指標的具體定義由第二章具體展開。

系統層級(SurfaceFlinger)的顯示效能指標

  • 基礎資料:SurfaceFlinger 合成次數
  • 指標意義:
    • 系統合成幀率:FPS
  • 特別說明:
    • SurfaceFlinger 僅在顯示區域內的 Surface 有提交內容更新時才會進行合成(上屏),因此,系統合成幀率低並不一定意味著影象顯示效能差,有可能是因為當前並沒有任何的內容更新所導致。
    • 若顯示區域內的某個待測 Surface 持續進行更新時, SurfaceFlinger的合成(上屏)的頻率可以在某種程度上反映該 Surface 的顯示效能,但從理論上分析該指標並不一定準確。這是因為,若顯示區域內尚存在其他 Surface,它們也會影響 SurfaceFlinger 的合成(上屏)的行為,從而干擾結果。
    • 若某個 Surface 的合成不在 SurfaceFlinger 中進行(如 Camera Preview),則該 Surface 的顯示效能無法用這類指標進行衡量。

應用層級(Surface)的顯示效能指標

  • 基礎資料:繪製過程中每一幀的關鍵時間點(如開始繪製時間、結束繪製時間等)
  • 指標意義:
    • 應用繪製幀率:Frame rate
    • 應用繪製輪詢頻率:SM
    • 應用繪製超時(跳幀)的次數:Aggregate frame stats、Jankiness count、Skipped frames
    • 應用繪製超時(跳幀)的幅度:Aggregate frame stats、Max accumulated frames、Skipped frames
  • 特別說明:
    • 與 SurfaceFlinger 類似, Surface也僅在有內容更新時才會進行繪製,因此,繪製頻率低並不一定意味著影象顯示效能差,有可能是因為當前並沒有任何的內容更新所導致。
    • 如 SM、Skipped frames 這類指標,由於其基礎資料取自 Choreographer,若 某些 Surface 的繪製不依賴於 Choreographer ,則這些指標無法衡量該 Surface 的顯示效能。
    • 如 Aggregate frame stats、Jankiness count、Max accumulated frames、Frame rate 這類指標, 由於其基礎資料僅在硬體繪製(Hardware Rendering)過程中進行統計,屬於 HWUI 的功能,所以非硬體繪製的 Surface 自然無法使用這類指標進行衡量。

小結

評價顯示效能的各個指標,可以按其在影象渲染流程中的作用,分為以下兩類:

  1. 系統層級的指標僅有 FPS 一根獨苗,它的限制是 Surface 的和合成需要在 SurfaceFlinger中進行;
  2. 應用層級的指標較多,它們之中又可以分為兩類:
    1) SM、Skipped frames 需要 Surface 依賴 Choreographer進行繪製,才能正常工作;
    2) Aggregate frame stats、Jankiness count、Max accumulated frames、Frame rate 屬於 HWUI 的功能, 需要 Surface 的繪製由 HWUI 進行才能進行分析。

Step 2:你從哪兒來——這些指標數值是怎麼得到的

第一章的內容僅僅是站在整個影象繪製流程的高度來簡單分析各個指標的,本章將進一步分析各個指標的基礎資料來源以及具體計算方式。

基礎資料:系統層級(SurfaceFlinger)的合成(上屏)的次數

前面說到,在 Android 系統中,SurfaceFlinger 扮演了系統中所有 Surface 的管理者的角色,當應用程式所對應的 Surface 更新之後,絕大多數的 Surface 都將在 SurfaceFlinger 之中完成了合併的工作之後,最終才會在 Screen 上顯示出來。

當然, SurfaceFlinger 的執行也是由 VSYNC 訊號驅動的,這也決定了每秒鐘合成次數的上限就是 60 次。當 SurfaceFlinger 接收到 Surface 更新通知的時候,將會由 SurfaceFlinger::handleMessageRefresh 函式進行處理,其中包含重建可見區域、初始化、合成等步驟。這裡,我們主要關注 SurfaceFlinger::doComposition() 這個方法。

void SurfaceFlinger::handleMessageRefresh() {
  ...
  if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
    // Latch buffers, but don't send anything to HWC, then signal another
    // wakeup for the next vsync
    preComposition();
    repaintEverything();
  } else {
    preComposition();
    rebuildLayerStacks();
    setUpHWComposer();
    doDebugFlashRegions();
    doComposition(); //重點關注物件
    postComposition();
  }
  ...
}

在 doComposition 中,完成 Surface 的合成之後,都會呼叫 DisplayDevice::flip(),它會使用變數 mPageFlipCount 統計我們進行合成的次數,這個變數就是我們統計 FPS 的核心原始資料。mPageFlipCount 記錄了 SurfaceFlinger 一共進行了多少次合成,也可以簡單理解為,SurfaceFlinger 向螢幕提交了多少幀的資料。

void SurfaceFlinger::doComposition() {
  ATRACE_CALL();
  const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
  for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
    const sp<DisplayDevice>& hw(mDisplays[dpy]);
    if (hw->isDisplayOn()) {
      ...
      hw->flip(hw->swapRegion);//重點關注物件
      ...
    }
    // inform the h/w that we're done compositing
    hw->compositionComplete();
  }
  postFramebuffer();
}
void DisplayDevice::flip(const Region& dirty) const {
  ...
  mPageFlipCount++;
}

不僅如此, Android 還為我們獲取這個基礎資料提供了比較方便的方法。通過執行 adb 命令:service call SurfaceFlinger 1013,我們就可以得出當前的 mPageFlipCount。

C:\Users\xiaosongluo>adb shell
shell@cancro:/ $ su
su
root@cancro:/ # service call SurfaceFlinger 1013
service call SurfaceFlinger 1013
Result: Parcel(00aea4f4    '....')

FPS 的計算方法

根據 FPS 的定義,我們不難逆推得出 FPS 的計算方法:

在 t1 時刻獲取 mPageFlipCount 的數值 v1,在在 t2時刻獲取 mPageFlipCount 的數值 v2,FPS 的計算公式:

FPS = (v2 - v1) / (t2 - t1);

需要注意的是:mPageFlipCount 的原始資料是 16 進位制的,一般而言計算之前需要先進行進位制轉換。

基礎資料:應用層級(Surface)的繪製過程中每一幀的關鍵時間點(FrameInfo)

請大家先注意 FrameInfo 是由 Android 6.0(具體來講是 Android M Preview) 引入到 HWUI 模組中的統計功能。 因此,目前來講絕大多數系統上的大多數應用都暫時無法獲取這一基礎資料。不過 This IsTheFuture。

我們再來仔細瞧瞧 Google 給出的顯示效能測試的十全大補丸 《Testing Display Performance : Aggregate frame stats》 。其中,特別值得關注的是 adb shell dumpsys gfxinfo framestats 這一條命令。通過這條命令,我們獲取每一幀繪製過程中每個關鍵節點的耗時情況,從而仔細的分析潛在的效能問題。

不得不說,按照 Google 給出的這種測試方法進行測試得到的顯示效能資料是非常全面的。

這些基礎資料都是記錄在 FrameInfo 之中,由 CanvasContext 在doFrame()時進行記錄。相關的主要原始碼如下:

//原始碼:FrameInfo.cpp
#include "FrameInfo.h"
#include <cstring>

namespace android {
  namespace uirenderer {
    const std::string FrameInfoNames[] = {
      "Flags",
      "IntendedVsync",
      "Vsync",
      "OldestInputEvent",
      "NewestInputEvent",
      "HandleInputStart",
      "AnimationStart",
      "PerformTraversalsStart",
      "DrawStart",
      "SyncQueued",
      "SyncStart",
      "IssueDrawCommandsStart",
      "SwapBuffers",
      "FrameCompleted",
    };

    void FrameInfo::importUiThreadInfo(int64_t* info) {
      memcpy(mFrameInfo, info, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
    }
  } /* namespace uirenderer */
} /* namespace android */

Aggregate frame stats 指標的計算方法

首先需要說明的是 Aggregate frame stats 不是一個指標,而是一系列指標集合。我們來看一個具體的 Aggregate frame stats 的例子:

Stats since: 752958278148ns
Total frames rendered: 82189
Janky frames: 35335 (42.99%)
90th percentile: 34ms
95th percentile: 42ms
99th percentile: 69ms
Number Missed Vsync: 4706
Number High input latency: 142
Number Slow UI thread: 17270
Number Slow bitmap uploads: 1542
Number Slow draw: 23342

在 Android M 以上的系統上,上述資訊的獲取十分方便(事實上也只有這些系統能夠獲取這些資訊)。僅需要執行以下命令即可:

adb shell dumpsys gfxinfo <PACKAGE_NAME>

Jankiness count、Max accumulated frames、Frame rate 指標的計算方法

首先需要說明的是:Jankiness count、Max accumulated frames、Frame rate 與 Aggregate frame stats的基礎資料並不一致,它們的基礎屬於來源於 gfxinfo(Profile data in ms)。

只是在 Android M 中 gfxinfo(Profile data in ms) 的基礎數值來源於 FrameInfo,詳見原始碼:FrameInfoVisualizer。但在更早的系統之上, gfxinfo(Profile data in ms) 的數值也可以獲取。

這裡需要特別指出的是, gfxinfo(Profile data in ms)只儲存了 Surface 最近渲染的128幀的資訊,因此,Jankiness count、Max accumulated frames、Frame rate 也僅僅是針對這 128 幀資料所計算出來的結果,它們的具體含義分別是:

  • Jankiness count:根據相鄰兩幀繪製時間的差值,“估計”是否存在跳幀並進行跳幀次數的統計;
  • Max accumulated frames: 根據相鄰兩幀繪製時間的差值,“估計”這 128 幀繪製過程中可能形成的最大連續跳幀數;
  • Frame rate:計算所得平均(繪製)幀率。

如果你對具體的計算過程感興趣,可以參考詳見原始碼:JankTestBase

基礎資料:應用層級(Surface)的繪製過程中每一幀的關鍵時間點(Choreographer)

先說一句有點繞口的話: Choreographer 是依據 Choreographer 繪製的 Surface 在 UI 繪製過程中最為核心的機制。

Choreographer 的工作機制簡單來說就是,使用者首先通過 postCallback 在 Choreographer 中設定的自己回撥函式:

  • CALLBACK_INPUT:優先順序最高,和輸入事件處理有關。
  • CALLBACK_ANIMATION:優先順序其次,和Animation的處理有關。
  • CALLBACK_TRAVERSAL:優先順序最低,和UI等控制元件繪製有關。

那麼,當 Choreographer 接收到 VSYNC 訊號時,Choreographer 會呼叫 doFrame 函式依次對上述藉口進行回撥,從而進行渲染。

那麼顯然,doFrame 的執行效率(次數、頻率)也就是我們需要的顯示效能資料。而這樣的基礎資料,Choreographer 自身也進行了記錄。如下面程式碼中, jitterNanos 記錄了繪製前後兩幀所間隔的時間差, 而 skippedFrames 則記錄了 jitterNanos 這段時間 doFrame 錯過了多少個 VSYNC 訊號,即跳過了多少幀。


// Set a limit to warn about skipped frames.
// Skipped frames imply jank.
private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.choreographer.skipwarning", 30);

void doFrame(long frameTimeNanos, int frame) {
  ...
  final long jitterNanos = startNanos - frameTimeNanos;
  if (jitterNanos >= mFrameIntervalNanos) {
    final long skippedFrames = jitterNanos / mFrameIntervalNanos;
    if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
      Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");
    }
    ...
    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
    ...
    frameTimeNanos = startNanos - lastFrameOffset;
  }
  ...
}

上述資料的獲取並不是那麼的直接,所以需要一定的手段。方法一共有三種,都不難:

  • Logcat 方案

缺點:該方案需要系統授權 “Adb Root” 許可權,用於修改系統屬性;對於丟幀資訊只能統計分析,無法進行實時處理。
優點:設定完成後,可以獲取系統中所有應用各自的繪製丟幀情況(丟幀發生的時間以及連續丟幀的數量)。

其實,仔細觀察程式碼,我們就可以注意到 Choreographer 原始碼中本身就有輸出的方案:

if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
  Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread.");
}

唯一阻礙我們獲取數值的是:skippedFrames 的數值只有大於 SKIPPED_FRAME_WARNING_LIMIT 才會輸出相關的警告。而 SKIPPED_FRAME_WARNING_LIMIT 的數值可以由系統引數 debug.choreographer.skipwarning 來設定。

注意:初始條件下,系統中不存在 debug.choreographer.skipwarning 引數,因此 SKIPPED_FRAME_WARNING_LIMIT 將取預設值 30。因此,正常情況下,我們能夠看見上訴 Log 出現的機會極少。

因此,如果我們修改(設定)系統屬性 debug.choreographer.skipwarning 為 1,Logcat 中將打印出每一次丟幀的Log。需要說明的是,由於為 SKIPPED_FRAME_WARNING_LIMIT 賦值的程式碼段由 Zygote 在系統啟動階段載入,而其他應用都是在拷貝複用 Zygote 中的設定,因此設定系統屬性後需要重啟 Zygote 才能使得上述設定生效。

具體的設定方法如下:

setprop debug.choreographer.skipwarning 1
setprop ctl.restart surfaceflinger; setprop ctl.restart zygote

設定完成以後,我們可以直接通過 Logcat 中的資訊得到系統中所有應用的繪製丟幀資訊,包括丟幀發生的時間以及連續丟幀的數量。不過由於 Logcat 資訊的滯後性,以上資訊我們幾乎只能進行在測試完成後進行統計分析,而無法進行實時處理。

  • Choreographer.FrameCallback 方案

缺點:該方案需要將測試程式碼與待測應用打包在一起,因此理論上僅能測試自己開發的應用。
優點:可以對丟幀資訊進行實時處理

Implement this interface to receive a callback when a new display frame is being rendered. The callback is invoked on theLooper thread to which the Choreographeris attached.

通過這個介面,我們可以在每一幀被渲染的時候記錄下它開始渲染的時間,這樣在下一幀被處理是,我們不僅可以判斷上一幀在渲染過程中是否出現掉幀,而整個過程都是實時處理的,這為我們可以及時獲取相關的呼叫棧資訊來輔助定位潛在的效能缺陷有極大的幫助。

  • 程式碼注入方案

缺點:該方案需要通過注入程式為指定應用注入測試程式碼,因此需要系統為注入程式授權 “應用Root” 許可權。
優點:與 Choreographer.FrameCallback 方案一致。

該方案可以簡單理解為通過注入的方式來實現與 Choreographer.FrameCallback 方案一樣的目的。因此,這裡我們主要討論兩者在實現方式上的區別。

顯而易見,我們需要注入的物件是 Choreographer ,因此理論上任何第三方應用都是可以被注入的。但是隨著 Android 系統對”應用Root” 許可權管理越來越嚴格,所以該方案可用的範圍越來越小。

SM 指標的計算方法

根據定義,SM 其實類似於 FPS,它被設計為可以衡量應用平均每秒執行 doFrame() 的次數。我們可以認為它是在衡量 Surface 渲染輪詢的次數。

針對 Logcat 方案,我們只需統計測試過程中目標程序一共掉了多少幀,由於對於絕大多數應用在沒有丟幀的情況下會針對每一次 VSYNC 訊號執行一次 doFrame(),而 VSYNC 絕大多數情況下每秒會觸發 60 次,因此我們可以反向計算得出 SM 的數值:

SM = (60* totalSeconds - totalSkippedFrames) / totalSeconds;

針對 Choreographer.FrameCallback 方案 以及 程式碼注入方案,我們需要在程式碼中自己進行統計輸出(可以是設計成實時的,也可以設計成測試結束後進行統計計算的)。

Skipped frames 指標的計算方法

這個指標的就是指當前應用在丟幀發生時的丟幀幀數。

針對 Logcat 方案, 該數值直接在 Logcat 中輸出,並且帶有時間資訊。

04-18 16:31:24.957 I/Choreographer(24164): Skipped 4 frames!  The application may be doing too much work on its main thread.
04-18 16:31:25.009 I/Choreographer(24164): Skipped 2 frames!  The application may be doing too much work on its main thread.

針對 Choreographer.FrameCallback 方案 以及 程式碼注入方案,我們可能很方便的通過計算前後兩幀開始渲染的時間差獲得這一數值,同樣方便。同樣與 Logcat 方案 不同的是,它也是可以設計成實時計算的。

小結

通過對各個顯示效能指標的分析,我們可以知道,雖然目前指標眾多,但其實有本質區別的指標確很少:

  • 系統層面:
    • 合成(上屏)幀率:FPS
  • 應用層面:
    • 跳幀次數:Aggregate frame stats、Jankiness count、Skipped frames
    • 跳幀幅度:Aggregate frame stats、Max accumulated frames、Skipped frames
    • 繪製幀率:Frame rate
    • 繪製輪詢頻率:SM

更為重要的是,我們從上述的分析中知道了各個指標都有著自己的優勢和不足,這也從根本上決定了它們各自有各自的用法。

Step 3:你要到哪兒去——這些指標如何落地來指導優化

其實指標的用法也是多種多樣的,為了方便討論,我們僅從日常監控、缺陷定位以及資料上報三個方面來討論各個顯示效能指標是如何落地的。

日常監控

  • FPS:資料形式最為直觀(FPS 是最早的顯示效能指標,而且在多個平臺中都有著類似的定義),且對系統平臺的要求最低(API level 1),遊戲、視訊等連續繪製的應用可以考慮選用,但不適用於絕大多數非連續繪製的應用;
  • SM:資料形式與 FPS 類似,可以很好的彌補 FPS 無法準確刻畫非連續繪製的應用顯示效能的缺陷;
  • Aggregate frame stats:除了對系統平臺有較高的要求以外,其採集方式最為簡單(系統自帶功能);
  • Skipped frames:與 Aggregate frame stats 類似, 資訊量相對較少,但可適用範圍更廣

特別說明:Jankiness count、Max accumulated frames、Frame rate 只統計了128幀的資訊(約2~3秒),而且 Jankiness count、Max accumulated frames對於掉幀情況的計算並非是一個準確值,因此這些指標都不太適用於日常監控

舉個栗子,筆者服務的某個產品使用如下的一些指標來監控產品與競品的效能變化情況:

測試指標 場景1 場景2 場景3 場景4
FPS 58 58 58 58
SM 59 59 59 59
Num of 6+ Skipped Frames 0 0 0 0
Num of 3+ Skipped Frames 0 0 2 0

備註:
1. Num of x+ Skipped Frames 代表測試過程中發生連續丟 x 幀(及以上)的次數;
2. 至於為什麼我們選擇關注連續丟 3 幀以及連續丟 6 幀的的次數,在【缺陷定位】部分有相關的分析討論;

缺陷定位

  • Skipped frames:基於 Choreographer.FrameCallback 方案實現的 Skipped frames 指標,可以在卡頓出現的時刻獲取應用堆疊資訊,可以在一定程度上進行缺陷定位

特別說明:
1. FrameInfo 相關指標無法直接進行缺陷定位,但 FrameInfo 當中包含了大量詳盡的繪製基礎資料,對於缺陷定位也有較大幫助;
2. 關於缺陷定位過程中連續掉幀閾值的選取,可參考維基百科中提到幾個重要的幀率數值:
- 12 fps:由於人類眼睛的特殊生理結構,如果所看畫面之幀率高於每秒約10-12幀的時候,就會認為是連貫的
- 24 fps:有聲電影的拍攝及播放幀率均為每秒24幀,對一般人而言已算可接受
- 30 fps:早期的高動態電子遊戲,幀率少於每秒30幀的話就會顯得不連貫,這是因為沒有動態模糊使流暢度降低
- 60 fps:在實際體驗中,60幀相對於30幀有著更好的體驗

以上各資料分別對應: 0 幀、1幀、2.5幀、5~6幀。(這就是為啥選擇3/6的原因)

舉個栗子, 筆者的同事萬大師(yuwan)基於上述原理自研了一款效能分析工具。該工具在集成於待測應用之後,可以自動儲存如下的效能缺陷資訊:

Frame lost:... (連續丟幀數量,一般我們會設定一個閾值,例如 6 幀以上我們才會進行記錄)
User action:...(當前使用者操作)
Stack trace: ...(當前堆疊資訊)

有了這個工具之後,我們可以收集應用的各個潛在“卡頓”點,用於進一步的分析和優化。

資料上報

  • Aggregate frame stats:除了對系統平臺有較高的要求以外,其採集方式最為簡單(系統自帶功能)、資料也比較清晰,相信基於這類指標實現效能資料上報是特別方便的
  • Skipped frames :基於 Choreographer.FrameCallback 方案實現的 Skipped frames 指標,採集方式簡單,實現基礎效能資料上報、卡頓資料上報也是很方便的

這方面應用,筆者所服務的產品暫時沒有涉及,就不舉例子了。如果各位觀眾感興趣,建議參考 Android ANR 的設計理念。

小結

發現了沒有 Skipped frames 的用處很大有沒有? 而且通讀全篇,你會發現 Aggregate frame stats、Jankiness count、Max accumulated frames 這些指標都有提供類似的功能。

至於為什麼,這就不是本文需要討論的內容了,如果大家比較感興趣,筆者這裡給出兩份相關的連結以供各位參考:

友情附贈: 現有顯示效能指標對比

本來寫到這裡本文的主要內容就應該結束了。但是如果不對比一下顯示效能指標神馬的,總會讓人覺得缺少了一些什麼。

友情提示:下述內容相對主觀,建議各位讀者依據專案情況自行進行選擇。

bugly

如果您意猶未盡,可以關注我的公眾賬號:
Bugly

騰訊 Bugly 是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智慧合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的程式碼行,實時上報可以在釋出後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧…

相關推薦

Bugly乾貨分享那些我們顯示效能指標

注:Google 在自己文章中用了 Display Performance 來描述我們常說的流暢度,為了顯得有文化,本文主要用“顯示效能”一詞來代指“流暢度”(雖然兩者在概念上有細微差別)。 從 Android 誕生的那一刻起,流暢度就為眾人所關注。一

Bugly乾貨分享老司機教你 “飆” EventBus 3

EventBus對於Android開發老司機來說肯定不會陌生,它是一個基於觀察者模式的事件釋出/訂閱框架,開發者可以通過極少的程式碼去實現多個模組之間的通訊,而不需要以層層傳遞介面的形式去單獨構建通訊橋樑。從而降低因多重回調導致的模組間強耦合,同時避免產生大量內部類。它擁有使

Bugly乾貨分享手把手教你逆向分析 Android 程式

很多人寫文章,喜歡把什麼行業現狀啊,研究現狀啊什麼的寫了一大通,感覺好像在寫畢業論文似的,我這不廢話,先直接上幾個圖,感受一下。 第一張圖是在把程式碼注入到地圖裡面,啟動首頁的時候彈出個浮窗,下載網路的圖片,蒼老師你們不會不認識吧? 第二張圖是微信運動步

Bugly乾貨分享“HTTPS”安全在哪裡?

背景 最近基於興趣學學習了下 HTTPS 相關的知識,在此記錄下學習心得。 在上網獲取資訊的過程中,我們接觸最多的資訊加密傳輸方式也莫過於 HTTPS 了。每當訪問一個站點,瀏覽器的位址列中出現綠色圖示時,意味著該站點支援 HTTPS 資訊傳輸方式。我們知道

那些我們的檢視日誌的命令

我們經常需要在日誌中搜索日誌,找程式碼中的問題,有時候我們需要精確的找到某一行或某個時間的日誌,下面我們來看看都怎麼用命令來搜尋相關日誌。 cat -n test.log日誌是有行號的, cat test.log 日誌沒有行號 tail -n 10 test.log 查詢日誌尾部最後10行的

騰訊Bugly乾貨分享WKWebView 那些

導語 WKWebView 是蘋果在 WWDC 2014 上推出的新一代 webView 元件,用以替代 UIKit 中笨重難用、記憶體洩漏的 UIWebView, 擁有60fps滾動重新整理率、和 safari 相同的 JavaScript 引擎。簡單

騰訊Bugly乾貨分享Android程序保活招式大全

【騰訊Bugly乾貨分享】Android程序保活招式大全 本文來自於騰訊bugly開發者社群,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/57ac4a0ea374c75371c08ce8 作者:騰訊——張興華 目前市面上的應用,貌似除了微信和手Q都會

騰訊Bugly乾貨分享移動網際網路測試到質量的轉變

Dev Club 是一個交流移動開發技術,結交朋友,擴充套件人脈的社群,成員都是經過稽核的移動開發工程師。每週都會舉行嘉賓分享,話題討論等活動。 本期,我們邀請了 TesterHome 測試技術社群聯合創始人“陳曄”,為大家分享《移動網際網路測試到質量的轉

騰訊Bugly乾貨分享聊聊蘋果的Bug

作者:張三華 導語 精神哥最近發現, 很多開發者在 iOS10 上遇到了一類堆疊為nano_free字樣的Crash,也有很多人向我們Bugly客服反饋遇到了這類問題,但並沒有好的解決方案。正當大家都束手無策的時候,微信強大的技術團隊針對這類

騰訊Bugly乾貨分享總結一個技術總監的教訓和經驗

導語 2017年來了,新年開篇,就不跟大家聊技術啦,給大家分享一篇鵝廠技術總監在多年工作中總結出的教訓和經驗。 這篇文章自從在騰訊內部論壇發表後,精神哥每年都會拿出來重新研讀一番,每次都有新的感悟和收穫,所以強烈推薦給大家。 正文 資深程式設

騰訊Bugly乾貨分享打造“微信小程式”元件化開發框架

作者:Gcaufy 導語 Bugly 之前發了一篇關於微信小程式的開發經驗分享(點選閱讀),小夥伴們在公眾賬號後臺問了很多關於小程式開發方面的問題,精神哥在查閱相關內容的時候,發現了龔澄同學自己寫了一個小程式開發框架,真的怒贊,趕緊安利給大家

騰訊Bugly乾貨分享美團大眾點評 Hybrid 化建設

本期 T 沙龍探討了移動端熱更新相關的話題。由於沙龍時間的限制,本期我們選取了美團的 Hybrid 化建設、去哪兒的跨平臺 ListView 效能優化、微博 Android 端熱更新踩過的坑話題。還期待熱更新、熱修復哪些話題?歡迎留言給我們。也歡迎報名參加

騰訊Bugly乾貨分享Android減包 - 減少APK大小

本文是對Google官方文件 Reduce APK Size 的翻譯,點選“閱讀原文”可以檢視英文原文。 譯者簡介:damonxia(夏正冬),天天P圖Android工程師 使用者經常會避免下載看起來體積較大的應用,特別是在不穩定的2G、3G

騰訊Bugly乾貨分享跨平臺 ListView 效能優化

導語 精神哥前陣子去參加了好友小青在北京辦的T沙龍,探討移動端熱更新相關的話題。Bugly 曾為大家介紹過不少騰訊內部的熱更新的框架,正好這次看到了美團,去哪兒以及微博同學在應用熱更新方面的實踐。 上週為大家整理了《美團大眾點評 Hybrid 化建設

騰訊Bugly乾貨分享Android ListView與RecyclerView對比淺析--快取機制

作者:黃寧源 一,背景 RecyclerView是谷歌官方出的一個用於大量資料展示的新控制元件,可以用來代替傳統的ListView,更加強大和靈活。 最近,自己負責的業務,也遇到這樣的一個問題,關於是否要將ListView替換為Recycl

騰訊Bugly乾貨分享Android 新一代多渠道打包神器

關於作者: 李濤,騰訊Android工程師,14年加入騰訊SNG增值產品部,期間主要負責手Q動漫、企鵝電競等專案的功能開發和技術優化。業務時間喜歡折騰新技術,寫一些技術文章,個人技術部落格:www.ltlovezh.com 。 ApkChanne

騰訊Bugly乾貨分享你為什麼需要 Kotlin

一、往事 曾經你有段時間研究 Intellij 的外掛開發,企圖編譯 Intellij Idea Community Edition (ICE)的原始碼,結果發現有個奇怪的東西讓你的程式碼無法編譯。。什麼鬼,kt 是什麼玩意兒? 怎麼又有

騰訊bugly乾貨分享精神哥手把手教你如何智鬥ANR

上帝說要有ANR,於是Bugly就有了ANR上報,那麼ANR到底是什麼? 最近很多童鞋問起精神哥ANR的問題,那麼這次就來聊一下,雞爪怎麼泡才好吃,噢不,是如何快速定位ANR。 ANR是什麼 簡單說,通常就是App執行的時候,duang~卡住了,怎麼搞都動不

騰訊bugly乾貨分享解耦---Hybrid H5跨平臺性思考

跨平臺,是H5最重要的能力之一。而 Hybrid H5 因強依賴於具體 app,往往不具有跨平臺性。這時,將強依賴關係解耦,即可恢復 H5 的跨平臺能力。近期本人負責 手Q 紅包打賞專案的前端開發,因專案涉及到多 app 跨平臺相容,對 hybrid H5

騰訊Bugly乾貨分享WebSocket 淺析

前言 在WebSocket API尚未被眾多瀏覽器實現和釋出的時期,開發者在開發需要接收來自伺服器的實時通知應用程式時,不得不求助於一些“hacks”來模擬實時連線以實現實時通訊,最流行的一種方式是長輪詢 。 長輪詢主要是發出一個HTTP請求到伺服器,