Android 性能監控系列一(原理篇)


題圖來自 https://unsplash.com

歡迎關注微信公衆號:BaronTalk,獲取更多精彩好文!java

一. 前言

性能問題是致使 App 用戶流失的罪魁禍首之一,若是用戶在使用咱們 App 的時候遇到諸如頁面卡頓、響應速度慢、發熱嚴重、流量電量消耗大等問題的時候,極可能就會卸載掉咱們的 App。而每每獲取用戶的成本是高昂的,所以由於性能問題致使用戶流失的狀況是咱們要極力避免的,作很差這一點是咱們開發人員的失職。android

去年咱們團隊完成了整個項目架構方面的重構(有興趣的同窗能夠參考我以前的文章安居客 Android 項目架構演進 Android 模塊化探索與實踐 ),目前已經可以很好的支撐咱們的業務,並對團隊的開發效率也有了必定的提高、項目質量也有了大幅的進步。git

可是項目上線後,到底有沒有性能問題?用戶體驗到底怎麼樣?在用戶的使用場景中到底會遇到哪些性能問題?咱們項目的性能短板又在哪裏?這些問題的答案咱們都不得而知,所以開發一套完善的性能監控體系勢在必行。咱們團隊在今年開始着手開發本身的性能監控組件 APM,但願經過它來採集線上性能數據,找到性能短板,針對性的優化用戶體驗。github

APM 全稱 Application Performance Management & Monitoring (應用性能管理/監控)api

後面我會經過一系列的文章來介紹 APM 的原理、框架設計與實現等等。本篇就是這個系列的第一篇,主要從實現原理方面來介紹 APM。按照目前的計劃,這個系列大體會從以下幾個方面來展開:服務器

  • 原理篇:主要介紹 APM 的實現原理;
  • 設計篇:介紹整個 APM 框架設計;
  • 實現篇-Gradle Plugin:介紹 Gradle 插件在 APM 項目中的應用,以及如何開發一個 Gradle Plugin;
  • 實現篇-Javassist/ASM:Javassist、ASM 等字節碼操做庫的介紹,以及如何使用它們在編譯時插入代碼來採集各項性能數據;
  • 實現篇-數據存儲及上報:介紹 APM 框架的存儲上報機制及實現過程;
  • 發佈集成:最後會介紹如何將庫發佈到 jCenter() 以及如何在生產項目中集成。

這裏要向你們交代一點是,以前的文章爲了極力作到將複雜的問題用通俗易懂的方式解釋清楚,又要面面俱到,每每篇幅過長;諸如以前寫過的RxJava系列6(從微觀角度解讀RxJava源碼) 神兵利器Dagger2 安居客 Android 項目架構演進 Android 模塊化探索與實踐 寫給 Android 應用工程師的 Binder 原理剖析等文章,篇幅一般都在 8000~10000字以上,通篇閱讀下來可能須要近半個小時的時間,不太符合當下碎片化閱讀的需求;所以在後面的寫做上會控制篇幅,儘可能控制在 10 分鐘之內的長度。微信

這也是我爲何會將 APM 做爲一個系列來介紹的緣由,同時這也能保證後面在介紹 APM 的時候可以深刻到實現細節,避免泛泛而談。架構

二. Android APM 的基本原理

市場上有不少商業化的 APM 平臺,好比著名的 NewRelic,還有國內的 聽雲、OneAPM 等等。這些平臺的工做流程基本都是一致的:框架

  1. 首先在客戶端(Android、iOS、Web等)採集數據;
  2. 接着將採集到的數據整理上報到服務器;
  3. 服務器接收到數據後建模、存儲、挖掘分析,讓後將數據可視化,供用戶使用。

以下圖: APM 工做流程模塊化

咱們介紹的 Android APM 框架其實就是在 Android 平臺上應用的一個數據採集上報 SDK。主要包含三大模塊:

  1. 數據採集
  2. 數據存儲
  3. 數據上報

其中數據採集是整個 APM 框架的核心。

數據採集咱們能夠經過手動埋點的方式,但這種方式工做量巨大、不靈活,並且沒法覆蓋到全部場景;所以只能經過自動化的方式來採集數據。在應用構建期間,經過修改字節碼的方式來進行字節碼插樁就是實現自動化的方案之一。

三. Android 打包流程及字節碼插樁原理

在談字節碼插樁的原理以前,首先咱們看看 Android 的打包流程,以下圖: Android 打包流程

從上面這張打包流程圖咱們能夠看到,一個 App 的全部 class 文件,包括第三方的 class 文件都會通過 dex 的過程打包成一個或者多個 dex 文件。

這其中涉及到兩個很關鍵的環節:

  1. javac:將 .java 格式的源代碼文件編譯成 class 文件;
  2. dex: 將 class 格式的文件打包彙總,組成一個或者多個 dex 文件。

咱們想要對字節碼進行修改,只須要在 javac 以後 dex 以前遍歷全部的字節碼文件,並按照必定的規則過濾修改就行了,這裏即是字節碼插樁的入口。

那麼咱們到底如何介入打包過程,在 class 轉換爲 dex 文件的時候實現對字節碼的修改呢?

答案是 transform api

Android Gradle Plugin 1.5.0 及以上版本,Google 官方提供了 transform api 做爲字節碼插樁的入口。咱們只須要實現一個自定義的 Gradle Plugin,而後在編譯階段去修改字節碼文件。對於 Gradle Plugin 的具體實現後面的文章再作詳細講解。

四. 修改字節碼

找到了插樁入口,接下來就要對字節碼進行修改。對於字節碼的修改,比較經常使用的框架有 Javassist 和 ASM。

  1. Javassist 是一個開源的分析、編輯和建立 Java 字節碼的類庫,它提供了源碼級別的 API 以及字節碼級別的 API,源碼級別的 API,直接使用 Java 編碼的形式,而不須要深刻了解虛擬機指令,就能動態改變類的結構或者動態生成類。

  2. ASM 是一個 Java 字節碼操控框架。它能被用來動態生成類或者加強既有類的功能。ASM 能夠直接產生二進制 class 文件,也能夠在類被加載入 Java 虛擬機以前動態改變類行爲。

ASM 和 Javassit 相比,API 貼近底層,比較難使用,須要對 Java 字節碼和虛擬機方面有必定程度的瞭解。ASM 的優勢就在於性能上的優點,且更加靈活;Javassist 的實現中大量使用的反射,因此性能偏低。

簡單的說就是 ASM 雖然難以使用,可是功能強大效率高。是不少無痕埋點、APM框架的首選方案。

ASM 的具體時候咱們放到這個系列後面的文章介紹。

五. 總結

Android APM 的原理其實很是簡單,用一句話總結就是:

依據打包原理,在 class 轉換爲 dex 的過程當中,調用 gradle transform api 遍歷 class 文件,藉助 Javassist、ASM 等框架修改字節碼,插入咱們本身的代碼實現性能數據的統計。

以上全部過程都是在編譯期完成的。

其實 Android 上的無痕埋點也是一樣的原理,區別只不過是咱們 hook 的點不一樣,採集的數據不一樣,所以掌握了 APM 的實現原理一樣能夠實現無痕埋點系統。

原理很簡單,難的是實現細節。好比如何插樁採集到頁面幀率、流量、耗電量等等。這些具體細節咱們放到後面一一介紹。至於爲何放到後面……由於不少東西本身沒作過我也不知道啊……🤣

若是你喜歡個人文章,就關注下個人公衆號 BaronTalk 、 知乎專欄 或者在 GitHub 上添個 Star 吧!

相關文章
相關標籤/搜索