資料的價值
APM
目标
對應用的性能、業務可靠性進行線上的監控和預警
采集内容
系統名額,應用性能名額,Crash,自定義日志等
使用者行為
目标
精細化營運
采集内容
從使用者屬性——性别、地域、收入、家庭狀況
從使用者生命周期——注冊、活躍、流失
從使用者行為——功能、内容、産品的喜好等
技術手段
Java層實作功能
1.自定義業務資料鍊路化
2.記憶體名額
3.CPU名額
4.FPS 名額
5.ANR日志
6.卡頓檢測
7.GC日志
8.Crash日志
9.Http名額資料(暫時隻支援OkHttp)
10.電量名額
11.MAOP,使用注解和配置檔案AOP指定方法執行前,執行後,異常插入指定代碼的功能(項目也實作動态日志功能,但是好像沒有很好的使用場景)
12.Remote下發指令,執行shell和動态執行代碼功能
13.互動分析:分析Activity生命周期耗時
概述
因為涉及的知識點太多,受篇幅限制,此篇就隻列出關鍵的知識點和步驟,具體知識點會列出參考的連結或者書籍,相信這些作者都講的很詳細和通透。
Android AOP
在Android編譯過程中,使用自定義的gradle插件,注冊新的tranform任務,在java compile之後,修改編譯後class檔案的内容,在切入點增加相應的代碼來實作AOP,實作無埋點功能。
Android的編譯流程
Gradle
Transform API
Android Gradle 工具在 1.5.0 版本後提供了 Transfrom API, 允許第三方 Plugin 在打包 dex 檔案之前的編譯過程中操作 .class 檔案。目前 jarMerge、proguard、multi-dex、Instant-Run 都已經換成 Transform 實作,我們注冊自己的transform實作對class檔案的修改
Transform具體的詳解
需要注意的是App Module依賴的library Module都是以jar的形式存在的,處理jar包時需要通過jarInput.getName()來判斷是否是":xx"形式開頭的jar,這部分jar包需要當成目錄代碼來通路。
自定義Gradle插件
自定義Gradle Plugin使用的是Groovy語言,需要對groovy有一定的了解,着重了解它的閉包概念,和各種省略寫法規則。
深入了解Android之Gradle
具體操作詳解
調試gradle插件
1.建立新的Configuration,Run->Edit Configurations->Remote
2.終端調用指令
./gradlew clean
./gradlew assembleDebug -Dorg.gradle.daemon=false -Dorg.gradle.debug=true
3.App欄選擇gradleDebug,點選debug按鈕,AS會斷點的地方停下來等待調試
Class檔案格式
Class檔案的總的格式
Class的method屬性
Method的code屬性
Class檔案格式分析
深入了解Java虛拟機(第2版) 提取碼: tmnh
位元組碼指令
Java AOP
常見的位元組碼生成工具有: ASM, AspectJ,Javassit ,Javapoet ,Spring,CGLib等,Spring,CGLib這些應為他們的作用機制原因,在 Android中無法使用,ASM可對指令流程有很好的分析能力,非常适用我們的場景,最終選擇了ASM,其他的工具都都有各自的使用場景。
ASM
ASM 是一個 Java 位元組碼操控架構。它能被用來動态生成類或者增強既有類的功能。ASM 可以直接産生二進制 class 檔案,也可以在類被加載入 Java 虛拟機之前動态改變類行為。Java class 被存儲在嚴格格式定義的 .class 檔案裡,這些類檔案擁有足夠的中繼資料來解析類中的所有元素:類名稱、方法、屬性以及 Java 位元組碼(指令)。ASM 從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類,ASM通過 “Visitor” 模式将 “.class” 類檔案的内容從頭到尾掃描一遍.
ASM詳解
ASM4使用指南
ASM開發的高效工具
Decompiler
位元組碼反編譯的工具,as自帶,用來檢查生成的代碼是否滿足class 檔案格式
Classpy
圖形化的class 檔案分析工具,功能和 javap類似,界面參考了Java Class Viewer
GitHub位址
ASM Bytecode Outline
它有一個Bytecode視圖,該視圖可以檢視目前激活的源碼視圖所對應的類 用asm應該如何生成。也就是說,如果你知道改造後的類的位元組碼,就可以通過該視圖得到改造的過程,asm的調用語句應該怎麼寫。研究asm時特别有用,值得推薦。
IDEA Plugin
dx指令
強烈推薦,當你ASM寫完之後,就是dx不過時,可以使用這個指令,它可以提示你具體哪條指令出現了什麼問題,非常提高效率。
dx --debug --dex —output=~/Desktop/out.dex ~/Desktop/dex
日志
資料鍊路化
1.事件中定義id和parentId屬性來建立事件關系
2.同一個線程裡執行的事件本身就存在鍊路
3.不同線程的事件,需要傳遞ParentId
MAOP
提供簡單使用配置檔案或者使用注解指定哪個類的哪個方法需要在方法執行前,執行後,執行異常時進行攔截,實作日志代碼和業務代碼的分離,如果業務覺得比對規則不夠靈活和豐富,也可以選擇比對文法和場景更多的AspectJ,同時需要一定學習成本。
注解方式
配置檔案方式
插樁代碼
互動分析
一般的互動分析需求,一般業務方法中插入代碼或者監聽系統提供的回調接口就能實作,Mas實作了:App啟動,App結束,頁面浏覽,OnClick幾種基本事件監控。
點選事件
通過Aop在View.OnClickListener.onClick插入代碼實作,具體實作檢視項目 plugin下的 MasAnalyticsClassVisitor實作
App啟動,App結束,頁面浏覽
Activity頁面浏覽都可以通過 Application.ActivityLifecycleCallbacks來實作,具體檢視項目中的MasDataActivityLifecycleCallbacks
Crash
通過Thread.setDefaultUncaughtExceptionHandler(this)擷取
記憶體監控
實作了監控記憶體使用随事件改變的曲線
OOM事件時檢測fd,thread數目
Activity洩漏的檢測
低記憶體狀态檢測
詳細解析
Cpu
通過讀取/proc/stat和/proc/pid/stat檔案擷取cpu資料進行計算,具體計算方法可以參考項目裡CpuSnapshot類和CpuMonitor類實作
GC日志
GC日志暫且隻找到通過Runtime.exc執行"logct -v time",抓取logicat日志比對的方法,運作時可以捕捉自己程序列印的logcat日志,gc日志捕捉應該還有更好的方式實作,檢視dalvik和art的源碼可以知道,Gc列印都會調用系統的logcat接口,如果能在native層hook住方法調用,是否是更好的實作,後面繼續研究下native inline和PLT hook的技術實作,現有實作具體檢視MemMonitor類
電量
通過注冊系統recevier,具體可檢視BatteryMonitor類,但是隻能知道系統電量的變化,對于應用電量消耗并不能很好的描述,如何描述應用電量的消耗還需後續實作,Google之後發現,Android4.4之後,系統将電量權限收掉之後參考其他項目的實作,好像都是模仿整個Android系統計算電量的公式重新做了一遍,這個還需要學習系統如何計算電量的。
ANR日志
通過FileObserver監聽/data/anr/traces.txt讀寫事件,根據anr日志的固定格式解析出屬于目前包名的Anr日志, anr資料一般保持最後一次資料, 但是 Huawei 有些機型最新的anr是寫在最後的,采集的時候需要采集最新的ANR,具體實作檢視ANR類日志内容。實踐中發現FileObserver隻能在第一次Anr發生的時候被通知,再次發生不會有通知,增加日志裡擷取 Wrote stack traces to '/data/anr/traces.txt' 内容來擷取通知。
卡頓檢測
使用Looper的print機制,計算目前主線操作執行的時間來确定是否卡頓,實作時可通過watchdog實作,但是正确的堆棧資料比較難擷取,通過多次擷取MainThread的堆棧來增加準确性,具體檢視UIMonitor類
FPS
通過Choreographer.getInstance().postFrameCallback(frameCallback),設定回調來統計幀數,不過并不是十分準确,具體檢視Fps類,在隻有程序當下的權限的下還沒有找到更好的方式,有更高權限的情況下可通過gxinfo實作。
Http名額資料
暫時隻支援OkHttp3,不過其他httpclient實作原理也大緻相同。項目目前實作的http 請求耗時,異常,資料大小,狀态碼 的擷取,直接使用前面實作的MAOP,攔截OkHttpClient.Builder的build方法加入統計Interceptor ,DNSLookUp 耗時,連接配接耗時,ssl耗時,通過OkhttpClient 3.10之後設定EventListener.Factory,可以直接收集,首包時間需要攔截OkHttp讀請求資料的方法來實作,OKHttpClient 最終調用CallServerInterceptor,關鍵代碼就是讀取readResponseHeaders的時機。
實作
使用前面提供的MAOP功能,在AOP配置檔案中加入,攔截OkHttpClient的builder方法和Http1Codec的readHeaderLine方法和okhttp3.internal.http2.Http2Stream的takeResponseHeaders方法的配置
在攔截OkHttpClient的Builder的build()方法中加入統計Interceptor和EventListenerFactory
首包的時間通過:認為第一次讀響應頭傳回時為首包時間,攔截okhttp3.internal.http1.Http1Code.readHeaderLine的方法和okhttp3.internal.http2.Http2Stream.takeResponseHeaders計算首包時間
Remote執行
遠端在日常開發維護實踐過程中被證明是非常有用的功能,提供了開發直接觸達使用者的途徑,改變出了問題,開發和使用者低效溝通的情況。
不過,現項目暫時還沒有實作功能,~.~~.~~.~。現有的思路是通過push通道下發指令,用戶端解析執行,用戶端使用lua具備動态執行能力,通過runtime執行shell指令。項目中已經接入可用的lua庫,但是shell執行,在開發過程中發現Android Runtime.exc執行帶空格的shell指令,并不能像直接執行shell指令那樣正确,這點還需要想辦法解決。
後續
目前項目初步具備名額的采集的能力,但是名額資料和問題的解決之間天然橫亘着巨大的鴻溝,這些還需要在實踐過程中找到解決一個問題需要的全部資料,需要更加了解Android提供運作環境的實作原理,實作一個完善成熟的品質保障體系的道路還很漫長。
項目位址
可關注微信二維碼共同交流