背景:當專案程式碼量很大的時候,或者你作為一名新人要快速掌握程式碼的時候,給函式打上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代表結束掐表
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執行,因此函式耗時只是一個參考值,比較哪個函式更耗時是可以的。