Android 快速定位耗時(shí)方法

一、啟動(dòng)耗時(shí)檢測(cè)

1、查看Logcat

在Android Studio Logcat中過(guò)濾關(guān)鍵字“Displayed”,可以看到對(duì)應(yīng)的Activity啟動(dòng)耗時(shí)日志。

2、adb shell

使用adb shell獲取應(yīng)用的啟動(dòng)時(shí)間

adb shell am start -W [packageName]/[AppstartActivity全路徑]

執(zhí)行后會(huì)得到三個(gè)時(shí)間:ThisTime、TotalTime和WaitTime,詳情如下:

ThisTime 最后一個(gè)Activity啟動(dòng)耗時(shí)。

TotalTime 所有Activity啟動(dòng)耗時(shí)。

WaitTime AMS啟動(dòng)Activity的總耗時(shí)。

一般查看得到的TotalTime,即應(yīng)用的啟動(dòng)時(shí)間,包括創(chuàng)建進(jìn)程 + Application初始化 + Activity初始化到界面顯示的過(guò)程。

特點(diǎn):

  • 線下使用方便,不能帶到線上。
  • 非嚴(yán)謹(jǐn)、精確時(shí)間。

3、AOP(Aspect Oriented Programming)打點(diǎn)

面向切面編程,通過(guò)預(yù)編譯和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能統(tǒng)一維護(hù)的一種技術(shù)。

作用

利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合性降低,提高程序的可重用性,同時(shí)大大提高了開發(fā)效率。

AOP核心概念

1)、橫切關(guān)注點(diǎn)

對(duì)哪些方法進(jìn)行攔截,攔截后怎么處理。

2)、切面(Aspect

類是對(duì)物體特征的抽象,切面就是對(duì)橫切關(guān)注點(diǎn)的抽象。

3)、連接點(diǎn)(JoinPoint)

被攔截到的點(diǎn)(方法、字段、構(gòu)造器)。

4)、切入點(diǎn)(PointCut)

對(duì)JoinPoint進(jìn)行攔截的定義。

5)、通知(Advice)

攔截到JoinPoint后要執(zhí)行的代碼,分為前置、后置、環(huán)繞三種類型。

準(zhǔn)備 首先,為了在Android使用AOP埋點(diǎn)需要引入AspectJ,在項(xiàng)目根目錄的build.gradle下加入:

classpath 'com.hujiang.aspectjx:gradle-android-plugin- aspectjx:2.0.0'

然后,在app目錄下的build.gradle下加入:

apply plugin: 'android-aspectjx'
implement 'org.aspectj:aspectjrt:1.8.+'

AOP埋點(diǎn)實(shí)戰(zhàn) JoinPoint一般定位在如下位置:

  • 函數(shù)調(diào)用
  • 獲取、設(shè)置變量
  • 類初始化 使用PointCut對(duì)我們指定的連接點(diǎn)進(jìn)行攔截,通過(guò)Advice,就可以攔截到JoinPoint后要執(zhí)行的代碼。 Advice通常有以下幾種類型:
  • Before:PointCut之前執(zhí)行
  • After:PointCut之后執(zhí)行
  • Around:PointCut之前、之后分別執(zhí)行

首先,我們舉一個(gè)小栗子:

@Before("execution(* android.app.Activity.on**(..))")
public void onActivityLifeCalled(JoinPoint joinPoint) throws Throwable {
...
}

在execution中的是一個(gè)匹配規(guī)則,第一個(gè)*代表匹配任意的方法返回值,后面的語(yǔ)法代碼匹配所有Activity中on開頭的方法。

處理Join Point的類型:

  • call:插入在函數(shù)體里面
  • execution:插入在函數(shù)體外面

如何統(tǒng)計(jì)Application中的所有方法耗時(shí)?

@Aspect
public class ApplicationAop {
    @Around("call (* com.application.BaseApplication.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) {
    Signature signature = joinPoint.getSignature();
    String name = signature.toShortString();
    long time = System.currentTimeMillis();
    try {
        joinPoint.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    Log.i(TAG, name + " cost" +     (System.currentTimeMillis() - time));
    }
}

注意

當(dāng)Action為Before、After時(shí),方法入?yún)镴oinPoint。 當(dāng)Action為Around時(shí),方法入?yún)镻roceedingPoint。

Around和Before、After的最大區(qū)別:

ProceedingPoint不同于JoinPoint,其提供了proceed方法執(zhí)行目標(biāo)方法。

總結(jié)AOP特性:

  • 無(wú)侵入性
  • 修改方便

強(qiáng)烈推薦結(jié)合第三節(jié)講解的Systrace工具使用,可以非??焖俚囟ㄎ坏胶臅r(shí)方法,上線的時(shí)候,可以考慮屏蔽掉AOP功能。

//apply plugin: 'android-aspectjx'

二、耗時(shí)分析工具 — TraceView

使用方式

方式1

檢測(cè)開始代碼處添加:

Debug.startMethodTracing();

檢測(cè)結(jié)束代碼處添加:

Debug.stopMethodTracing();

使用adb pull將生成的**.trace文件導(dǎo)出到電腦,然后使用Android Studio的Profiler加載

方式2

打開Profiler -> CPU -> 點(diǎn)擊 Record -> 點(diǎn)擊 Stop -> 查看Profiler下方Top Down/Bottom Up 區(qū)域找出耗時(shí)的熱點(diǎn)方法。

Profile CPU

1、Trace types

Trace Java Methods

會(huì)記錄每個(gè)方法的時(shí)間、CPU信息。對(duì)運(yùn)行時(shí)性能影響較大。

Sample Java Methods

相比于Trace Java Methods會(huì)記錄每個(gè)方法的時(shí)間、CPU信息,它會(huì)在應(yīng)用的Java代碼執(zhí)行期間頻繁捕獲應(yīng)用的調(diào)用堆棧,對(duì)運(yùn)行時(shí)性能的影響比較小,能夠記錄更大的數(shù)據(jù)區(qū)域。

Sample C/C++ Functions

需部署到Android 8.0及以上設(shè)備,內(nèi)部使用simpleperf跟蹤應(yīng)用的native代碼,也可以命令行使用simpleperf。

Trace System Calls

檢查應(yīng)用與系統(tǒng)資源的交互情況。 查看所有核心的CPU瓶。 內(nèi)部采用systrace,也可以使用systrace命令。

2、Event timeline 顯示應(yīng)用程序中在其生命周期中轉(zhuǎn)換不同狀態(tài)的活動(dòng),如用戶交互、屏幕旋轉(zhuǎn)事件等。

3、CPU timeline 顯示應(yīng)用程序?qū)崟r(shí)CPU使用率、其它進(jìn)程實(shí)時(shí)CPU使用率、應(yīng)用程序使用的線程總數(shù)。

4、Thread activity timeline 列出應(yīng)用程序進(jìn)程中的每個(gè)線程,并使用了不同的顏色在其時(shí)間軸上指示其活動(dòng)。

綠色:線程處于活動(dòng)狀態(tài)或準(zhǔn)備好使用CPU。 黃色:線程正等待IO操作。(重要) 灰色:線程正在睡眠,不消耗CPU時(shí)間。

Profile提供的檢查跟蹤數(shù)據(jù)窗口有四種

1、Call Chart

提供函數(shù)跟蹤數(shù)據(jù)的圖形表示形式。

水平軸:表示調(diào)用的時(shí)間段和時(shí)間。 垂直軸:顯示被調(diào)用方。 橙色:系統(tǒng)API。 綠色:應(yīng)用自有方法 藍(lán)色:第三方API(包括Java API) 提示:右鍵點(diǎn)擊Jump to source跳轉(zhuǎn)至指定函數(shù)。

Call Chart 是 Traceview 和 systrace 默認(rèn)使用的展示方式。它按照應(yīng)用程序的函數(shù)執(zhí)行順序來(lái)展示,適合用于分析整個(gè)流程的調(diào)用。舉一個(gè)最簡(jiǎn)單的例子,A 函數(shù)調(diào)用 B 函數(shù),B 函數(shù)調(diào)用 C 函數(shù),循環(huán)三次,就得到了下面的 Call Chart。

Call Chart 就像給應(yīng)用程序做一個(gè)心電圖,我們可以看到在這一段時(shí)間內(nèi),各個(gè)線程的具體工作,比如是否存在線程間的鎖、主線程是否存在長(zhǎng)時(shí)間的 I/O 操作、是否存在空閑等。

2、Flame Chart

將具有相同調(diào)用方順序的完全相同的方法收集起來(lái)。

水平軸:執(zhí)行每個(gè)方法的相對(duì)時(shí)間量。 垂直軸:顯示被調(diào)用方。 注意:看頂層的哪個(gè)函數(shù)占據(jù)的寬度最大(平頂),可能存在性能問(wèn)題。

Flame Chart 也就是大名鼎鼎的火焰圖。它跟 Call Chart 不同的是,F(xiàn)lame Chart 以一個(gè)全局的視野來(lái)看待一段時(shí)間的調(diào)用分布,它就像給應(yīng)用程序拍 X 光片,可以很自然地把時(shí)間和空間兩個(gè)維度上的信息融合在一張圖上。上面函數(shù)調(diào)用的例子,換成火焰圖的展示結(jié)果如下。

當(dāng)我們不想知道應(yīng)用程序的整個(gè)調(diào)用流程,只想直觀看出哪些代碼路徑花費(fèi)的 CPU 時(shí)間較多時(shí),火焰圖就是一個(gè)非常好的選擇。例如,之前我的一個(gè)反序列化實(shí)現(xiàn)非常耗時(shí),通過(guò)火焰圖發(fā)現(xiàn)耗時(shí)最多的是大量 Java 字符串的創(chuàng)建和拷貝,通過(guò)將核心實(shí)現(xiàn)轉(zhuǎn)為 Native,最終使性能提升了很多倍。

3、Top Down

遞歸調(diào)用列表,提供self、children、total時(shí)間和比率來(lái)表示被調(diào)用的函數(shù)信息。 Flame Chart是Top Down列表數(shù)據(jù)的圖形化。

4、Bottom Up

展開函數(shù)會(huì)顯示其調(diào)用方。 按照消耗CPU時(shí)間由多到少的順序?qū)瘮?shù)排序。 注意點(diǎn):

Wall Clock Time:程序執(zhí)行時(shí)間。 Thread Time:CPU執(zhí)行的時(shí)間。

TraceView小結(jié)

特點(diǎn)

  • 圖形的形式展示執(zhí)行時(shí)間、調(diào)用棧等。
  • 信息全面,包含所有線程。
  • 運(yùn)行時(shí)開銷嚴(yán)重,整體都會(huì)變慢,得出的結(jié)果并不真實(shí)。
  • 找到最耗費(fèi)時(shí)間的路徑:Flame Chart、Top Down。
  • 找到最耗費(fèi)時(shí)間的節(jié)點(diǎn):Bottom Up。

作用 主要做熱點(diǎn)分析,得到兩種數(shù)據(jù):

  • 單次執(zhí)行最耗時(shí)的方法。
  • 執(zhí)行次數(shù)最多的方法。

三、耗時(shí)分析工具 — Systrace

使用方式:代碼插樁

定義Trace靜態(tài)工廠類,將Trace.begainSection(),Trace.endSection()封裝成i、o方法,然后再在想要分析的方法前后進(jìn)行插樁即可。

在命令行下執(zhí)行systrace.py腳本:

python /Users/yourname-xxx/Library/Android/sdk/platform-tools/systrace/systrace.py -t 20 sched gfx view wm am app webview -a "com.mypackageName" -o ~/Documents/systrace_data/start_1.html

具體參數(shù)含義如下:

  • -t:指定統(tǒng)計(jì)時(shí)間為20s。
  • shced:cpu調(diào)度信息。
  • gfx:圖形信息。
  • view:視圖。
  • wm:窗口管理。
  • am:活動(dòng)管理。
  • app:應(yīng)用信息。
  • webview:webview信息。
  • -a:指定目標(biāo)應(yīng)用程序的包名。
  • -o:生成的systrace.html文件。

如何查看數(shù)據(jù)?

在UIThread一欄可以看到核心的系統(tǒng)方法時(shí)間區(qū)域和我們自己使用代碼插樁捕獲的方法時(shí)間區(qū)域。

Systrace原理

在系統(tǒng)的一些關(guān)鍵鏈路(如SystemServcie、虛擬機(jī)、Binder驅(qū)動(dòng))插入一些信息(Label); 通過(guò)Label的開始和結(jié)束來(lái)確定某個(gè)核心過(guò)程的執(zhí)行時(shí)間; 把這些Label信息收集起來(lái)得到系統(tǒng)關(guān)鍵路徑的運(yùn)行時(shí)間信息,最后得到整個(gè)系統(tǒng)的運(yùn)行性能信息; Android Framework里面一些重要的模塊都插入了label信息,用戶App中可以添加自定義的Lable。

Systrace小結(jié)

特性

結(jié)合Android內(nèi)核的數(shù)據(jù),生成Html報(bào)告。 系統(tǒng)版本越高,Android Framework中添加的系統(tǒng)可用Label就越多,能夠支持和分析的系統(tǒng)模塊也就越多。 必須手動(dòng)縮小范圍,會(huì)幫助你加速收斂問(wèn)題的分析過(guò)程,進(jìn)而快速地定位和解決問(wèn)題。

作用

主要用于分析繪制性能方面的問(wèn)題。 分析系統(tǒng)關(guān)鍵方法和應(yīng)用方法耗時(shí)。

結(jié)合AOP,可以在方法的前后,非常方便地批量插入以下代碼。最后從運(yùn)行生成Html報(bào)告后,可以快速查找出耗時(shí)的方法。

Trace.begainSection();
//your code
Trace.endSection();

四 插件MethodTraceMan

該插件,是可配置式地插樁,可視化界面查看耗時(shí),支持搜索、排序。一旦集成,小白也能通過(guò)界面找出耗時(shí)方法。


五、總結(jié)

Traceview 和 systrace 都是我們比較常用的排查卡頓的工具,從實(shí)現(xiàn)上這些工具分為兩個(gè)流派。

第一個(gè)流派是 instrument。獲取一段時(shí)間內(nèi)所有函數(shù)的調(diào)用過(guò)程,可以通過(guò)分析這段時(shí)間內(nèi)的函數(shù)調(diào)用流程,再進(jìn)一步分析待優(yōu)化的點(diǎn)。

第二個(gè)流派是 sample。有選擇性或者采用抽樣的方式觀察某些函數(shù)調(diào)用過(guò)程,可以通過(guò)這些有限的信息推測(cè)出流程中的可疑點(diǎn),然后再繼續(xù)細(xì)化分析。

Traceview

Traceview是吐槽的比較多的工具。它利用 Android Runtime 函數(shù)調(diào)用的 event 事件,將函數(shù)運(yùn)行的耗時(shí)和調(diào)用關(guān)系寫入 trace 文件中。

由此可見,Traceview 屬于 instrument 類型,它可以用來(lái)查看整個(gè)過(guò)程有哪些函數(shù)調(diào)用,但是工具本身帶來(lái)的性能開銷過(guò)大,有時(shí)無(wú)法反映真實(shí)的情況。比如一個(gè)函數(shù)本身的耗時(shí)是 1 秒,開啟 Traceview 后可能會(huì)變成 5 秒,而且這些函數(shù)的耗時(shí)變化并不是成比例放大。

在 Android 5.0 之后,新增了startMethodTracingSampling方法,可以使用基于樣本的方式進(jìn)行分析,以減少分析對(duì)運(yùn)行時(shí)的性能影響。新增了 sample 類型后,就需要我們?cè)陂_銷和信息豐富度之間做好權(quán)衡。

systrace

systrace是 Android 4.1 新增的性能分析工具。我通常使用 systrace 跟蹤系統(tǒng)的 I/O 操作、CPU 負(fù)載、Surface 渲染、GC 等事件。

systrace 利用了 Linux 的ftrace調(diào)試工具,相當(dāng)于在系統(tǒng)各個(gè)關(guān)鍵位置都添加了一些性能探針,也就是在代碼里加了一些性能監(jiān)控的埋點(diǎn)。Android 在 ftrace 的基礎(chǔ)上封裝了atrace,并增加了更多特有的探針,例如 Graphics、Activity Manager、Dalvik VM、System Server 等。

systrace 工具只能監(jiān)控特定系統(tǒng)調(diào)用的耗時(shí)情況,所以它是屬于 sample 類型,而且性能開銷非常低。但是它不支持應(yīng)用程序代碼的耗時(shí)分析,所以在使用時(shí)有一些局限性。

由于系統(tǒng)預(yù)留了Trace.beginSection接口來(lái)監(jiān)聽?wèi)?yīng)用程序的調(diào)用耗時(shí),那我們有沒(méi)有辦法在 systrace 上面自動(dòng)增加應(yīng)用程序的耗時(shí)分析呢?

劃重點(diǎn)了,我們可以通過(guò)編譯時(shí)給每個(gè)函數(shù)AOP插樁的方式來(lái)實(shí)現(xiàn),也就是在重要函數(shù)的入口和出口分別增加Trace.beginSection和Trace.endSection。當(dāng)然出于性能的考慮,我們會(huì)過(guò)濾大部分指令數(shù)比較少的函數(shù),這樣就實(shí)現(xiàn)了在 systrace 基礎(chǔ)上增加應(yīng)用程序耗時(shí)的監(jiān)控。

通過(guò)這樣方式的好處有:

  • 可以看到整個(gè)流程系統(tǒng)和應(yīng)用程序的調(diào)用流程。包括系統(tǒng)關(guān)鍵線程的函數(shù)調(diào)用,例如渲染耗時(shí)、線程鎖,GC 耗時(shí)等。
  • 性能損耗可以接受。由于過(guò)濾了大部分的短函數(shù),而且沒(méi)有放大 I/O,所以整個(gè)運(yùn)行耗時(shí)不到原來(lái)的兩倍,基本可以反映真實(shí)情況。

Profiler

在 Android Studio 3.2 的 Profiler 中直接集成了幾種性能分析工具,其中:

  • Sample Java Methods 的功能類似于 Traceview 的 sample 類型。
  • Trace Java Methods 的功能類似于 Traceview 的 instrument 類型。
  • Trace System Calls 的功能類似于 systrace。
  • SampleNative (API Level 26+) 的功能類似于 Simpleperf,用來(lái)分析Native方法耗時(shí)。

選擇哪種工具,需要看具體的場(chǎng)景。我來(lái)匯總一下,如果需要分析 Native 代碼的耗時(shí),可以選擇 Simpleperf;如果想分析系統(tǒng)調(diào)用,可以選擇 systrace;如果想分析整個(gè)程序執(zhí)行流程的耗時(shí),可以選擇 Traceview 或者插樁版本的 systrace(優(yōu)先推薦)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容