一個能讓你瞭解所有函式呼叫順序以及函式耗時的Android庫 (更新版)

weixin_33935777發表於2018-03-31

背景:當專案程式碼量很大的時候,或者你作為一名新人要快速掌握程式碼的時候,給函式打上log,來了解程式碼執行邏輯,這種方式會顯然成本太大,要改動專案編譯執行,NO!太耗時;或者你想debug的方式來給你想關注的幾個函式,來了解程式碼執行邏輯,NO!因為你肯定會漏掉函式;也許你可以固執的給你寫的專案打滿log說這樣也行,但是你要知道你方法所呼叫的jdk的函式或者第三方aar或者jar再或者android sdk中的函式呼叫順序你怎麼辦,還能打log嗎?顯然不行吧,來~這個專案給讓可以讓你以包名為過濾點過濾你想要知道所有函式呼叫順序。

專案地址:github.com/zjw-swun/Ap… 歡迎star

作者列表(排名按程式碼貢獻時間順序):二精-霽雪清虹,xingstarx,大精-wing,pighead4u ,Tesla ,lijunjie,三精-虹貓,Harlber

1. 效果奉上

動作簡介:首先點選MainActivity的自定義MyTextView然後進入SecondActivity再點選textview之後finish跳轉回MainActivity
下面是庫處理過所得到的函式呼叫順序order.txt檔案(我這裡遮蔽了jdk函式,第三方庫函式,以及android sdk中函式,換句話說我就保留了我自己包名中的函式順序)

832 ent     67593 .....com.zjw.appmethodorder.MainActivity.onClick (Landroid/view/View;)V	MainActivity.java
832 ent     99956 ..........com.zjw.appmethodorder.MainActivity.onPause ()V	MainActivity.java
832 ent     99970 ...........com.zjw.appmethodorder.BaseActivity.onPause ()V	BaseActivity.java
832 ent    100472 ............com.zjw.appmethodorder.BaseActivity.baseOnPause ()V	BaseActivity.java
832 ent    128540 ........com.zjw.appmethodorder.SecondActivity.<init> ()V	SecondActivity.java
832 ent    128562 .........com.zjw.appmethodorder.BaseActivity.<init> ()V	BaseActivity.java
832 ent    213911 ........com.zjw.appmethodorder.SecondActivity.onCreate (Landroid/os/Bundle;)V	SecondActivity.java
832 ent    213928 .........com.zjw.appmethodorder.BaseActivity.onCreate (Landroid/os/Bundle;)V	BaseActivity.java
832 ent    258414 ..........com.zjw.appmethodorder.BaseActivity.baseOnCreate ()V	BaseActivity.java
832 ent   1440503 .........com.zjw.appmethodorder.SecondActivity.onResume ()V	SecondActivity.java
832 ent   1440563 ..........com.zjw.appmethodorder.BaseActivity.onResume ()V	BaseActivity.java
832 ent   1445675 ...........com.zjw.appmethodorder.BaseActivity.baseOnResume ()V	BaseActivity.java
832 ent   2954291 .................com.zjw.appmethodorder.MyTextView.onWindowVisibilityChanged (I)V	MyTextView.java
832 ent   3065664 ........com.zjw.appmethodorder.MainActivity.onStop ()V	MainActivity.java
832 ent   3065701 .........com.zjw.appmethodorder.BaseActivity.onStop ()V	BaseActivity.java
832 ent   3069155 ..........com.zjw.appmethodorder.BaseActivity.baseOnStop ()V	BaseActivity.java
832 ent   3139519 .......com.zjw.appmethodorder.SecondActivity.click (Landroid/view/View;)V	SecondActivity.java
832 ent   3146300 ........com.zjw.appmethodorder.SecondActivity.finish ()V	SecondActivity.java
832 ent   3183478 ..........com.zjw.appmethodorder.SecondActivity.onPause ()V	SecondActivity.java
832 ent   3183498 ...........com.zjw.appmethodorder.BaseActivity.onPause ()V	BaseActivity.java
832 ent   3183843 ............com.zjw.appmethodorder.BaseActivity.baseOnPause ()V	BaseActivity.java
832 ent   3209420 ........com.zjw.appmethodorder.MainActivity.<init> ()V	MainActivity.java
832 ent   3209438 .........com.zjw.appmethodorder.BaseActivity.<init> ()V	BaseActivity.java
832 ent   3283359 ........com.zjw.appmethodorder.MainActivity.onCreate (Landroid/os/Bundle;)V	MainActivity.java
832 ent   3283378 .........com.zjw.appmethodorder.BaseActivity.onCreate (Landroid/os/Bundle;)V	BaseActivity.java
832 ent   3330938 ..........com.zjw.appmethodorder.BaseActivity.baseOnCreate ()V	BaseActivity.java
832 ent   4363295 .....................com.zjw.appmethodorder.MyTextView.<init> (Landroid/content/Context;Landroid/util/AttributeSet;)V	MyTextView.java
832 ent   4436094 ..................com.zjw.appmethodorder.MyTextView.onFinishInflate ()V	MyTextView.java
832 ent   4449689 .........com.zjw.appmethodorder.MainActivity.initView ()V	MainActivity.java
832 ent   4539427 .........com.zjw.appmethodorder.MainActivity.onResume ()V	MainActivity.java
832 ent   4539467 ..........com.zjw.appmethodorder.BaseActivity.onResume ()V	BaseActivity.java
832 ent   4543597 ...........com.zjw.appmethodorder.BaseActivity.baseOnResume ()V	BaseActivity.java
832 ent   4917854 .................com.zjw.appmethodorder.MyTextView.onAttachedToWindow ()V	MyTextView.java
832 ent   4918658 .................com.zjw.appmethodorder.MyTextView.onWindowVisibilityChanged (I)V	MyTextView.java
832 ent   5090653 ...................................com.zjw.appmethodorder.MyTextView.onMeasure (II)V	MyTextView.java
832 ent   5355203 ..................................com.zjw.appmethodorder.MyTextView.onMeasure (II)V	MyTextView.java
832 ent   5456681 .......................................com.zjw.appmethodorder.MyTextView.onSizeChanged (IIII)V	MyTextView.java
832 ent   5467577 ....................................com.zjw.appmethodorder.MyTextView.onLayout (ZIIII)V	MyTextView.java
832 ent   5876623 ...........................................com.zjw.appmethodorder.MyTextView.onDraw (Landroid/graphics/Canvas;)V	MyTextView.java
832 ent   6121967 ........com.zjw.appmethodorder.SecondActivity.onStop ()V	SecondActivity.java
832 ent   6121986 .........com.zjw.appmethodorder.BaseActivity.onStop ()V	BaseActivity.java
832 ent   6123689 ..........com.zjw.appmethodorder.BaseActivity.baseOnStop ()V	BaseActivity.java
832 ent   6127522 ........com.zjw.appmethodorder.SecondActivity.onDestroy ()V	SecondActivity.java
832 ent   6127679 .........com.zjw.appmethodorder.BaseActivity.onDestroy ()V	BaseActivity.java
832 ent   6133301 ..........com.zjw.appmethodorder.BaseActivity.baseOnDestroy ()V	BaseActivity.java
複製程式碼

2. 原理篇

原理就是基於android sdk中提供的工具----traceview,和dmtracedump。traceview會生成.trace檔案,該檔案記錄了函式呼叫順序,函式耗時,函式呼叫次數等等有用的資訊。而dmtracedump 工具就是基於trace檔案解析生成報告的工具,好在花了一些時間發現dmtracedump -o 選項,經過研究發現,這玩意輸出的內容居然是按邏輯順序從上到下的。

3. 使用方法(建議使用小工具,廢棄原有的兩個gradle Task

注意:請先確保 anroid sdk 中的dmtracedump 工具加入在你的環境變數中

熟悉appMethodOrder的朋友以前用的是兩個gradle Task,由於android studio 3.0掐表操作生成的.trace檔案改變了生成位置,android studio 3.0以下版本,預設產生在專案目錄\capture\時間.trace,而到了android studio 3.0,windows使用者trace檔案生成目錄以本人為例為:C:\Users\hasee\AppData\Local\Temp\cpu_trace.trace,所以以前用的是兩個gradle Task,已經不再適應,考慮到擴充套件性,建議使用小工具

3.1 生成trace檔案的方式

生成trace檔案的方式有2種一種是掐表操作,一種是在專案中使用程式碼操作 先介紹掐表生成trace檔案 android studio 3.0以下版本操作如下 第一次點選下圖時鐘icon代表開始掐表,然後回到您的app,進行您要跟蹤函式呼叫順序的操作,再次點選下圖時鐘icon代表結束掐表

掐表結束後即會在captures目錄生成com.zjw.appmethodorder_2017.03.25_21.41.trace檔案,android studio會預設開啟一個視覺化視窗

android studio 3.0版本操作如下 點選CPU 進入CPU皮膚,然後選擇Instrumented選項(Sampled和Instrumented區別在於,Sampled函式呼叫取樣率低生成的trace檔案小,取樣率低會造成大部分函式呼叫順序漏記,Instrumented相當於無損取樣生成檔案大),第一次點選下圖紅色圓形icon代表開始掐表,然後回到您的app,進行您要跟蹤函式呼叫順序的操作,再次點選下圖紅色圓形icon代表結束掐表,

結束掐表就會在C:\Users\hasee\AppData\Local\Temp\cpu_trace.trace(本人是windows環境,使用windows系統可以參照,使用Mac的同學 cd /private/var/folders在folders 搜尋cpu_trace.trace),存在cpu_trace.trace時會新建cpu_trace1.tracecpu_trace2.trace 以此類推

第二種生成trace檔案的方式就是在您的專案程式碼中使用 android.os.Debug.startMethodTracing();android.os.Debug.stopMethodTracing();,執行完stopMethodTracing將會在您手機app的sdcard下面生成.trace檔案

3.2 appMethodOrder小工具使用

將trace檔案拖拽到小工具File Path左邊區域

package name處填入您需要過濾的目標包名,或者填入空串(空串將生成所有執行緒所有包名的函式呼叫順序)

如上圖所示,查詢結構存在四個tab,第一個threadID代表執行緒id,第二個threadName代表執行緒名,第三個usecs代表函式耗時,第四個method代表函式名+類名,(查詢結果是以時間排序的)

4. 海量資訊的處理手段

不難發現trace檔案時間越長意味著記錄的資訊也會越來越多,這時候單純以包名過濾其實滿足不了需求,比如我們想通過3個條件(例如 主執行緒+含zjw包名的 or android.view)來過濾出結果,以下將給出解決辦法。

appMethodOrder小工具會在該jar所在目錄生成appMethodOrderTrace.txt,以文字方式記錄查詢結果。 我們可以藉助資料庫這個工具使用查詢語句來幫助我們過濾出有用的資訊,可以使用以下資料庫命令,把appMethodOrderTrace.txt內容匯入到資料庫表中 //從text 匯入資料到 mysql (以下語句在mysql中實驗是OK的) truncate table app; LOAD DATA LOCAL INFILE 'C:/Users/hasee/Desktop/appMethodOrderTrace.txt' INTO TABLE `app`;

app表結建立語句如下

CREATE TABLE `app` (
  `threadId` int(11) DEFAULT NULL,
  `threadName` varchar(255) DEFAULT NULL,
  `usecs` varchar(255) DEFAULT NULL,
  `method` varchar(255) DEFAULT NULL,
  `className` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
複製程式碼

查詢語句示範 //查詢main執行緒中 自定義包下最耗時的操作(以毫秒為單位,降序) SELECT threadName, usecs/1000, method FROM app WHERE app.threadName = "main" AND method like "%自定義包名%" ORDER BY app.usecs DESC

5. 計算得出的函式耗時不是準確的時間

根據官方文件(https://developer.android.com/studio/profile/traceview.html) Interpreted code runs more slowly when profiling is enabled. Don't try to generate absolute timings from the profiler results (such as, "function X takes 2.5 seconds to run"). The times are only useful in relation to other profile output, so you can see if changes have made the code faster or slower relative to a previous profiling run.所知(我也用了程式碼在函式開頭結尾用System.nanoTime()時間相減,發現,生成trace的時候確實會拖慢所以函式的執行時間,比System.nanoTime()時間相減得出的時間差的比較多)。原因是trace過程會整體拖慢JVM執行,因此函式耗時只是一個參考值,比較哪個函式更耗時是可以的。

相關文章