??????? 很久以前就看過使用Android studio的Method Trace工具調(diào)查函數(shù)性能或者效率的問題妹蔽,一直沒有在解決日常問題過程中實戰(zhàn)過殃恒。最近剛好遇到了一個關(guān)閉wifi開關(guān)時wifi設(shè)置頁面卡頓的問題劣欢,所以就想著嘗試一下。雖然解決UI卡頓一般都用systrace工具,但是method trace可以查看各個線程中某個函數(shù)執(zhí)行情況,理論上也是可以用來定位UI卡頓問題的爽冕。
首先啟動method trace功能,然后關(guān)閉wifi披蕉,待卡頓過后颈畸,停止trace。Android studio會自動打開抓取的內(nèi)容没讲。?
雖然Eclipse通過ddms抓取的traceview也是可以查看函數(shù)調(diào)用情況眯娱,但是Adroid studio的method trace功能更強大,尤其是可以分線程查看爬凑,這就更方便我們查找問題了徙缴。由于UI卡頓肯定是主線程被block了,必然我們要看這段時間內(nèi)主線程的執(zhí)行情況嘁信。選中上圖中的主線程于样,可以看到中間有個很長的長條,鼠標放上去之后吱抚,發(fā)現(xiàn)該函數(shù)執(zhí)行了約1.5s(注意起始時間百宇,與后面做對比時可以很方便確認多線程調(diào)用時函數(shù)執(zhí)行先后順序),那么可以確認秘豹,就是這個函數(shù)出的問題了携御。
定位到源碼中的copyAndNotifyListeners函數(shù),去掉其他邏輯后主邏輯如下
private void copyAndNotifyListeners(boolean notifyListeners) {
……1……
???? synchronized (mLock) {
??????? ……2……
??? }
……3……
}
直觀的懷疑是執(zhí)行完1處的邏輯之后既绕,主線程要去拿鎖啄刹,此時由于鎖被其他線程占用,所以主線程要一直等待凄贩。但是這只是我們的懷疑誓军,要找到證據(jù)才行。這又要體現(xiàn)出Method Trace工具的強大之處疲扎。圖中的每個小方格都代表了函數(shù)的執(zhí)行情況昵时,包括函數(shù)名、函數(shù)執(zhí)行時間以及各個函數(shù)之間的調(diào)用情況椒丧。原理上壹甥,如果邏輯2中有函數(shù)調(diào)用的話,必定能在圖中體現(xiàn)出來壶熏。把圖中卡頓地方前后位置放大之后句柠,發(fā)現(xiàn)卡頓之前確實在調(diào)用1處的函數(shù),而在卡頓之后恰好在調(diào)用2處的邏輯。這樣溯职,就為我們之前的假設(shè)找到了證據(jù)精盅。
既然主線程要取的鎖被占用,肯定是被其他線程占用著谜酒,具體是什么線程叹俏,可以通過圖中左上角查看每個線程的調(diào)用情況來確認。果然甚带,在某個線程中我們發(fā)現(xiàn)了與主線程類似的長條她肯。查看函數(shù)調(diào)用堆棧的情況佳头,確實在該函數(shù)中持有對應(yīng)的鎖鹰贵,且該鎖剛好是在主線程之前拿到的。這樣卡頓的原因就找到了康嘉。進一步分析碉输,該部分卡頓是由于子線程在持鎖期間調(diào)用了WifiManager的接口,而這個接口在向WifiManager取數(shù)據(jù)時采用的是同步調(diào)用亭珍,而WifiManager最終是向WifiStateMachine中取數(shù)據(jù)敷钾,WifiStateMachine線程此時正在忙碌調(diào)用driver中的接口,無法及時處理這個請求肄梨,因此這就是應(yīng)用層被卡住的原因阻荒。
雖然經(jīng)過上面的分析,問題是WifiStateMachine引起的众羡,但是一款優(yōu)秀的應(yīng)用侨赡,還是要首先檢查自己能否兼容這種場景,自己的邏輯是否也存在問題粱侣。深入分析之后發(fā)現(xiàn)羊壹,子線程確實存在持鎖范圍過大的問題,可以進行優(yōu)化齐婴,避免不必要的等待油猫。
補充問題
在解決上面問題時,對android源碼的@GuardedBy標記有些疑惑柠偶,這個標記有啥作用呢情妖?我直觀的以為這個可以當(dāng)成一把鎖來用,但這只是我的假設(shè)而已诱担,肯定要驗證一下才能踏實毡证。查閱了一些資料,感覺還是云里霧里该肴,所以還是自己做實驗看看把情竹。
寫了上面的測試代碼,編譯之后得到.class文件,通過javap -c Test.class后查看編譯后的代碼
從這個結(jié)果可以看出秦效,@GuardedBy標簽并沒有對編譯后的結(jié)果產(chǎn)生影響雏蛮,也就沒有同步作用。事實上該標簽和@Override標簽的功能是一致的阱州,更多的作用是一種標記挑秉,標記這個變量或者這個函數(shù)是需要同步的,同時編譯器在編譯時可以根據(jù)這些標簽做lint檢查之類的工作苔货。在日常工作中犀概,可以合理的使用這些標簽以增強代碼邏輯的可讀性,同時也方便其他同學(xué)閱讀此處邏輯時能更快的領(lǐng)會設(shè)計意圖夜惭。