Android Studio如何調試Framework層的代碼?
Android框架層的代碼龐大且複雜,在分析一些過程的時候經常會跟丟代碼,所以希望能夠動態調試Framework,已經有編譯好了的Android源代碼。還有兩個疑惑,動態調試時,能否只導入Framework的代碼,而不是導入整個源代碼。如果我想斷在ActivityThread的main中函數,應該如何設置?謝謝。
關注這個問題一段時間了,強答一下吧;軟體開發過程中,只有小部分的時候是編碼,大部分的工作都是在調試,Debug是一項非常非常重要的技能,毋庸多言。我這裡只給出F ramework中Java代碼的調試方法,關於native代碼的調試還請高人指點。
OK,回到正題;其實整個調試過程非常簡單:- 在你要調試進程的合適位置打上斷點
- 跟蹤代碼(Step in/out/over等等)
接下來就從這兩個方面展開。
如何在正確的地方下斷點
「正確的地方」包含兩個含義:首先,調試是以進程為單位進行的,如果你需要調試運行在進程A 中的代碼,卻把debugger attach到了B進程,那麼這個斷點壓根兒就是牛頭不對馬嘴;另外呢,比如你想調試Android的多媒體框架,你得知道media相關的類在哪吧,也就是說需要在正確的函數裡面下斷點。
首先,如何在合適的進程下斷點?
如果是調試我們自己寫的App,在Android Studio裡面非常簡單,在Run菜單de最後面有一個attach debugger to Android process 的選項,打開之後選擇自己需要調試的進程即可;但是,你要是需要調試Android Framework層的代碼,這樣做是達不到目的的——Framework層的代碼通常運行在別的進程(比如ActivityManagerService運行在system_server進程),而這些進程通常情況下是不可調試的,也就是說在attach debugger to android process 的那個菜單裡面不會有系統的進程,如下圖:
解決這個辦法很簡單:使用模擬器(真機也行,限Nexus系列刷原生Android系統,注意事項見附錄),我的Nexus 5 可以調試的進程如下:明白你要執行的代碼運行在哪一個進程相當重要,在Android中,由於Binder通信機制的存在,「進程遷移」使用的非常非常頻繁,因此需要對binder機制有一定的了解;詳細的話可以參考我的博客:Binder學習指南
如何在對應的代碼處下斷點?
假設我們現在把debugger attach到了正確的進程,那麼斷點應該下在哪裡呢?直觀來講,就是說我需要導入所有的Android源碼嗎?如果不是應該導入哪些代碼,怎麼導入?
首先,如果你需要調試的類在sdk裡面導出了,你壓根兒就不需要再導入源碼,Android Studio自動幫你關聯了這部分代碼(前提是你用SDK Manager下載了sdk的源碼,如下圖:
比如你要調試ActivityManagerServce類的attachApplication方法,那麼很簡單;創建一個空的Android項目,SDK版本選擇與你要調試的模擬器/真機 的android相同(這很重要,下文會講述);然後attrach 到system_server進程,直接在attach_application上面打上斷點;隨便啟動一個app,可以看到我們熟悉的調試界面:
根據上面的分析,我們首先得知道「系統設置?」運行在哪一個進程,通常情況下進程名字就是包名;我們查出設置的包名即可,而包名是在源碼的AndroidManifeist中聲明的,因此,我們查到?「系統設置」這個程序的源碼即可;源碼在https://android.googlesource.com/ ,系統App的源碼在/packages這個子目錄下面,我們一個個找,可以確定「系統設置」的源碼在 https://android.googlesource.com/platform/packages/apps/Settings/ ;然後我們把這部分代碼git clone下來,導入Android Studio:
我們去AndroidManifest中查到,「系統設置」的包名為:com.android.settings,這樣我們attach到這個進程 :如何跟蹤代碼?
或許你會說,跟蹤代碼不就是step in/out/over么,這有什麼難的?但其實事情並沒有你想像的那麼簡單,要優雅滴調試,還是需要一些姿勢的。
- 行號對應
跟蹤代碼一個首要的問題是行號對應。如果你在正確位置下了斷點,但是跟蹤的時候,單步調試,發現運行的代碼和Android Studio裡面的代碼對不上號,那麼就很蛋疼;要使得調試器的行號能夠對應,必須保證設備上的代碼和調試器的代碼是同一份;簡單來說,需要使用Android的原生系統(模擬器,Nexus系列真機),然後調試器裡面使用的SDK版本,必須和設備的系統版本一致。
一定要注意行好對應這一點,這會使調試過程簡單很多;那麼,如果沒有辦法,行號對不上,那該如何調試呢?行號對不上也是可以調試的,這裡先賣個關子,有贊更新 ^_^
2. 熟練使用斷點
斷點是有很多種類型的,方法斷點,watch point,條件斷點都能夠很好滴輔助我們調試;如果你連這幾個名詞都沒有聽說過,一定要惡補一下;可以參閱我的博客:Android Studio你不知道的調試技巧
到這裡,Android Framework的Java層如何調試應該比較清楚了;回到題主的問題:如何調試ActivityThread的main函數?
這個有一點點複雜,因為main函數執行得非常早,在進程啟動之後還沒來得及attach debugger,這代碼估計已經執行了;如果你你自己的app進程,你可以使用Debug.waitForDebugger()這個函數等到調試器;但是Framework的代碼就不行了;如樓上所說,這裡可以取個巧:main函數會通過Binder IPC到AMS進程的attachApplication函數,你直接在AMS所在的進程system_server對應位置打上斷點(上文講述過的attachApplciation),這個函數執行完畢,就自動回到app進程了,debugger捕捉到斷點之後,你可以在app進程裡面下斷點。
拋磚引玉,若有不妥多多指教~沒有用Android Studio動態調試過android framework的代碼,但用Eclipse動態調試android framework的代碼以前倒是經常干。
這裡主要針對Eclipse環境來回答一下題主的兩個問題。
對於第一個,能否只導入framework的代碼來動態調試的問題。
答案是可以的。android framework的代碼量本身非常龐大,甚至可以只導入framework的一部分代碼來動態調試對應的那一部分代碼。比如system server進程,也就是我們在DDMS的設備進程列表中看到的那個system_process進程,會運行主要的系統Service的代碼,比如AMS,WMS,PMS等,這些系統Service的代碼位於frameworks/base/services/這個位置,我們需要動態調試這些Service的代碼的時候就可以只導入這一部分的代碼。再比如,android app進程中運行的framework代碼,主要位於frameworks/base/core/這個位置,要動態調試這部分代碼時也可以只導入這部分代碼。儘管這個時候為android framework或framework的一部分創建的Java Project無法在eclipse中編譯通過,但依然有辦法動態調試相關的代碼。
對於第二個調試設置的問題。
想要動態調試android framework的代碼,首先有個前提,即需要將你編譯的ROM燒進設備,且要求編譯模式是user_debug或eng,也就是你的Eclipse中導入的代碼和你的設備上運行的是同一份。然後是調試設置,針對題主要動態調試ActivityThread的代碼這種情形,之前用過的一種思路供參考。
ActivityThread中初始化相關的代碼是Android APP進程運行非常早期會執行的代碼,而我們知道Android APP的進程是由zygote進程創建的,因而想要直接在這些代碼中加斷點動態調試比較困難。
但Android APP進程創建起來之後,比較早期的時候就會訪問AMS,印象里AMS的attachApplication()方法(frameworks/base/services/java/com/android/server/am/ActivityManagerService.java)在Android APP剛啟動時會被訪問到,而且是同步訪問,也就是說ActivityThread的執行流程通過IPC訪問到這個方法,在這個方法結束之前,ActivityThread的執行流程不會繼續往前走。因而可以在設備啟動完成之後,創建針對於system server,也就是system_process進程的遠程調試會話,在這個方法中加斷點,從而截斷Android APP進程的啟動過程,然後再創建針對於Android APP的遠程調試會話,在ActivityThread的代碼中加斷點,進而釋放掉system_process的斷點來達到題主單步調試Android App的ActivityThread的代碼的目的。
創建遠程調試會話的過程
比如要調試system_process進程中運行的系統Service的代碼。第一步,創建Eclipse的Java Project,導入android framework中系統Service的相關代碼。針對Android App進程的遠程調試會話的創建
如我們前面所述,斷下system_process的AMS中的attachApplication()方法會截斷Android App中ActivityThread的執行流程。這個時候,進程的名字都還沒有設置,因而在DDMS中可能會看到名字還是「?」的Android App進程。然後參照上面針對system_process進程創建遠程調試會話的過程,為Android App進程創建遠程調試會話。隨後在ActivityThread的代碼里加斷點,並讓system_process中的斷點恢復執行,就可以單步調試ActivityThread的代碼了。Android App進程初始化過程中出現的問題,常可以採用這種方式來調試。DroidVM之調試Android Framework
前面 幾個答主說的都很好,我今天剛好也寫了一篇小白文,提主可以看看,寫的比較亂,有看不懂的可以繼續聊啊
Android 調試系統代碼(非SDK source裡面的內容) - 知乎專欄
推薦閱讀:
※為什麼 Linus Torvalds 用 Fedora 而不用 Ubuntu 或 Windows?
※Linux 下進行 PHP 開發,相比 Windows 環境有哪些好處?
※代碼使用std::thread,使用-static -lpthread靜態編譯後,運行段錯誤的問題?
※C語言中在一個函數中定義另一個函數是否可編譯並運行?
※如何選購用於ArchLinux的筆記本?
TAG:Linux | Android開發 | Android | AndroidStudio |