code小生,一個專注 Android 領域的技術平臺
公眾號回復 Android 加入我的安卓技術群
作者:Android_Jieyao
鏈接:https://www.jianshu.com/p/3292267fcd38
聲明:本文已獲Android_Jieyao
授權發(fā)表,轉發(fā)等請聯(lián)系原作者授權
讀完本文你將了解到:
1.TraceView 是什么;
2.如何生存trace文件;
3.根據(jù)TraceView顯示定位問題
4.解決DDMS中find功能無法使用的問題
5.TraceView使用場景
6.StrictMode嚴苛模式使用
TraceView 是 Android SDK 中內置的一個工具,它可以加載 trace 文件,用圖形的形式展示代碼的執(zhí)行時間、調用次數(shù)及調用棧,便于我們分析。
trace 文件是 log 信息文件的一種,可以通過代碼,Android Studio,或者 DDMS 生成。
使用 Android SDK 提供的工具可以生成很多 log 文件,便于我們分析當前應用的內存、布局等狀況,下面是幾種文件的截圖:
手機卡頓很多時候都是由于某個操作過于耗時,在茫茫代碼中查找元兇未免太過痛苦,這時候就該體現(xiàn) TraceView 的價值了。
生成 trace 文件有三種方法:
使用代碼生成
Debug.startMethodTracing('shixintrace'); //開始 trace保存文件到 '/sdcard/shixintrace.trace'
// ...
Debug.stopMethodTracing(); //結束
代碼很簡單,當你調用開始代碼的時候,系統(tǒng)會生產 trace 文件,并且產生追蹤數(shù)據(jù),當你調用結束代碼時,會將追蹤數(shù)據(jù)寫入到 trace 文件中。
下一步使用 adb 命令將 trace 文件導出到電腦:
adb pull /sdcard/shixintrace.trace /tmp
使用代碼生成 trace 方式的好處是容易控制追蹤的開始和結束,缺點就是步驟稍微多了一點。
使用 Android Studio生成
Android Studio 內置的 Android Monitor 可以很方便的生成 trace 文件到電腦。
在 CPU 監(jiān)控的那欄會有一個鬧鐘似的的按鈕,未啟動應用時是灰色:
啟動應用后,這個按鈕會變亮,點擊后開始追蹤,相當于代碼調用 startMethodTracing:
當要結束追蹤時再次點擊這個按鈕,就會生成 trace 文件了。
生成 trace 后 Android Studio 自動加載的 traceview 圖形如下:
從這個圖可以大概了解一些方法的執(zhí)行時間、次數(shù)以及調用關系,也可以搜索過濾特定的內容。
左上角可以切換不同的線程,這其實也是直接用 Android Studio 查看 trace 文件的缺點:無法直觀地對比不同線程的執(zhí)行時間。
鼠標懸浮到黃色的矩形上,會顯示對應方法的開始、結束時間,以及自己占用和調用其他方法占用的時間比例:
使用 DDMS生成
DDMS 即 Dalvik Debug Monitor Server ,是 Android 調試監(jiān)控工具,它為我們提供了截圖,查看 log,查看視圖層級,查看內存使用等功能,可以說是如今 Android Studio 中內置的 Android Monitor 的前身。
雙擊 shift 彈出全局搜索,搜索 “Android Device Monitor”:
或者直接在 設置里設置 Android Device Monitor 的快捷鍵:
打開 Android Device Monitor,在 DDMS 中打開 trace 文件,DDMS 會啟動 TraceView 加載 trace 文件:
上圖介紹了 TraceView 的大致內容:
上半部分顯示了不同線程的執(zhí)行時間
其中不同的顏色表示不同的方法
同一個顏色越長,說明執(zhí)行時間越久,如圖中的主線程 main
空白表示這個時間段內沒有執(zhí)行內容
下半部分展示了不同方法的執(zhí)行時間信息,關鍵指標有三個:
Cpu Time/Call :該方法平均占用 CPU 的時間
Real Time/Call :平均執(zhí)行時間,包括切換、阻塞的時間,>= Cpu Time
Calls + Recur Calls/Total :調用、遞歸次數(shù)
點擊下面的任意一個方法,可以看到它的詳細信息:
Parents:選中方法的調用處
Children:選中方法調用的方法
定位問題時, TraceView 的使用方式:
從上半部分查看哪些線程執(zhí)行時間長?什么時候開始執(zhí)行?與主線程交錯時間?
哪些方法的執(zhí)行需要花費很長時間
點擊 TraceView 中的 Cpu Time/Call,按照占用 CPU 時間從高到低排序
哪些方法調用次數(shù)非常頻繁
點擊 TraceView 中的 Calls + Recur Calls/Total ,按照調用次數(shù)從高到底排序
排序后,然后逐個排查是否有項目代碼或者依賴庫代碼,有的話點擊查看詳情,查看是這個方法還是調用的子方法的問題,進一步定位問題。
Traceview 中信息太多,想要查找可以使用最下方的 find:
但是目前 DDMS 中的 TraceView 有 bug,find 無法使用, 解決辦法就是直接使用 SDK 中的 TraceView。
直接打開 SDK 中的 TraceView :
然后打開之前生成的 trace 文件:
如果直接打開 traceview 有問題,可以通過命令行 traceview 打開:
雖然提示 deprecated,但起碼在搜索上還是比 Android Device Monitor 中好用。
在發(fā)現(xiàn)某個頁面或者操作會卡頓時,可以使用 TraceView 定位問題代碼。
比如啟動,加載圖片列表卡頓等情況。
還有一點,不要過早優(yōu)化!
1.簡介
StrictMode類是Android 2.3 (API 9)引入的一個工具類,可以用來幫助開發(fā)者發(fā)現(xiàn)代碼中的一些不規(guī)范的問題,以達到提升應用響應能力的目的。舉個例子來說,如果開發(fā)者在UI線程中進行了網(wǎng)絡操作或者文件系統(tǒng)的操作,而這些緩慢的操作會嚴重影響應用的響應能力,甚至出現(xiàn)ANR對話框。為了在開發(fā)中發(fā)現(xiàn)這些容易忽略的問題,我們使用StrictMode,系統(tǒng)檢測出主線程違例的情況并做出相應的反應,最終幫助開發(fā)者優(yōu)化和改善代碼邏輯。
2.用途
使用嚴格模式,系統(tǒng)檢測出主線程違例的情況會做出相應的反應,如日志打印,彈出對話框亦或者崩潰等。換言之,嚴格模式會用不同的策略對應用的違例細節(jié)做監(jiān)控,通過一定的方式暴露給開發(fā)者方便優(yōu)化與改善。
3.使用
通常在Activity或者自定義的Application類的onCreate方法中啟動StrictMode.
// DEVELOPER_MODE代表只在調試模式下啟用StrictMode
if (DEVELOPER_MODE) {
//線程方面的策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
// VM方面的策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
StrictMode通過策略方式來讓你自定義需要檢查哪些方面的問題,主要有兩種策略:
ThreadPolicy:線程方面的策略
StrictMode.ThreadPolicy.Builder主要方法解釋:
detectNetwork():用于檢查UI線程中是否有網(wǎng)絡請求
detectDiskReads()和detectDiskWrites():磁盤讀寫檢查
detectCustomSlowCalls():主要用于幫助開發(fā)者發(fā)現(xiàn)UI線程中調用的哪些方法執(zhí)行的比較慢,
要和StrictMode.noteSlowCall配合使用,只有通過StrictMode.noteSlowCall標記“可能會”執(zhí)行
比較慢的方法,只有標記過的方法才能被檢測到,日志中會記錄方法的執(zhí)行時間
(注意:只有在主線程中執(zhí)行的方法才會顯示執(zhí)行時間,在其他線程中執(zhí)行的方法,
就算是使用StrictMode.noteSlowCall標記,在日志中也不會打印執(zhí)行時間)
VmPolicy:VM方面的策略
StrictMode.VmPolicy.Builder()主要方法解釋:
detectActivityLeaks(): 用戶檢查 Activity 的內存泄露情況
detectLeakedClosableObjects() 和 detectLeakedSqlLiteObjects():資源沒有正確關閉時回觸發(fā)
detectLeakedSqlLiteObjects() 和 detectLeakedClosableObjects()的用法類似,
只不過是用來檢查 SQLiteCursor 或者 其他 SQLite 對象是否被正確關閉
detectLeakedRegistrationObjects() 用來檢查 BroadcastReceiver或者ServiceConnection注冊
類對象是否被正確釋放
setClassInstanceLimit(),設置某個類的同時處于內存中的實例上限,可以協(xié)助檢查內存泄露
懲罰:
penaltyDeath():當觸發(fā)違規(guī)條件時,直接Crash掉當前應用程序
penaltyDeathOnNetwork():當觸發(fā)網(wǎng)絡違規(guī)時,Crash掉當前應用程序
penaltyDialog():觸發(fā)違規(guī)時,顯示對違規(guī)信息對話框
penaltyFlashScreen():會造成屏幕閃爍,不過一般的設備可能沒有這個功能
penaltyDropBox():將違規(guī)信息記錄到 dropbox 系統(tǒng)日志目錄中(/data/system/dropbox),
你可以通過如下命令進行插件
adb shell dumpsys dropbox dataappstrictmode --print
permitCustomSlowCalls()、permitDiskReads ()、permitDiskWrites()、
permitNetwork: 如果你想關閉某一項檢測,可以使用對應的permit*方法
最后:我們也可以在Android設備的設置(Settings)中啟用StrictMode(親測沒有什么效果):開發(fā)者選項 > 啟用嚴格模式。
最后分享一個小例子:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
writeToExternalStorage();
}
});
}
/**
* 文件系統(tǒng)的操作
*/
public void writeToExternalStorage() {
try {
File externalStorage = Environment.getExternalStorageDirectory();
File mbFile = new File(externalStorage, 'xxx.txt');
if (mbFile.exists()){
mbFile.createNewFile();
}
OutputStream output = new FileOutputStream(mbFile, true);
output.write('www.wooyun.org'.getBytes());
output.flush();
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用penaltyLog():
D/StrictMode: StrictMode policy violation; ~duration=3 ms:
android.os.StrictMode$StrictModeDiskWriteViolation: policy=65567 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1253)
at java.io.FileOutputStream.(FileOutputStream.java:220)
at com.perf.strictmodedemo.MainActivity.writeToExternalStorage(MainActivity.java:44)
at com.perf.strictmodedemo.MainActivity$1.onClick(MainActivity.java:28)
at android.view.View.performClick(View.java:5619)
at android.view.View$PerformClick.run(View.java:22298)
at android.os.Handler.handleCallback(Handler.java:754)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6365)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:883)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
使用penaltyDialog():
總結:StrictMode適合作為一些基礎規(guī)范問題排查。
分享技術我是認真的