visible_windows生成邏輯和解析
visible_windows生成邏輯
在開發者選項中生成錯誤報告或命令列使用dumpstate,會生成一個bugreprot的壓縮包,如
其中包含了一些dump資訊,其中海有個壓縮包
裡面包含了一些可見視窗的控制元件資訊,但用notepad開啟卻多是亂碼
之前工作遇到過一個顯示問題只有截圖、簡要log和上面bugreport,最後根據上面的控制元件資訊結合截圖然後排查程式碼找到疑點最後解決問題,這裡介紹下上面視窗控制元件dump資訊的生成和解析
首先找到其生成visible_windows.zip的程式碼:(frameworks/native/cmds/dumpstate/dumpstate.cpp)
可見這裡主要是呼叫了cmd window dump-visible-window-views命令列,即使直接命令列呼叫也可以執行,其是呼叫的windowmanager的命令列即WindowManagerShellCommand.java中邏輯
檢視其onCommand方法:
顯然在呼叫dump-visible-window-views命令時,其會執行runDumpVisibleWindowViews方法
runDumpVisibleWindowViews主要邏輯時遍歷視窗,如果視窗可見則呼叫視窗(WindowState)的mClient.executeCommand方法,這裡mClient對端是ViewRootImpl的一個內部類W,所以,這裡最後會遍歷可見視窗執行其ViewRootImpl中的executeCommand方法
executeCommand方法這裡顯然根本邏輯只有一行,即呼叫ViewDebug的dispatchCommand方法,其引數view是對應視窗的控制元件樹的根節點,其引數command是ViewDebug.REMOTE_COMMAND_DUMP_ENCODED即DUMP_ENCODED,其引數parameters是null,其引數clientStream是之前建立的輸出流
繼續檢視下ViewDebug的dispatchCommand方法
在該場景下其呼叫的是dumpEncoded方法
這裡主要建立了一個ViewHierarchyEncoder,後面控制元件也主要是透過這裡寫入的
encoder.addProperty
先看下這裡最開始寫如的widnow:left和window:top資料,其都是呼叫ViewHierarchyEncoder的addProperty方法
這裡createPropertyIndex方法如下,其維護了一個屬性名的map,其key是屬性名,value是記錄的該屬性名出現的index,如果已經新增過對應屬性名,這裡則返回其對應index,如果沒有新增過,則將心出現的屬性名新增到map中(mPropertyNames),並將其新的index(根據屬性名出現順序從1開始遞增)新增到map中
再檢視下writeShort和writeInt方法,其邏輯相似,都是先寫入一個標記字母表示資料型別,然後寫入對應資料
view.encode
然後看下view.encode方法
beginObject和endObject(ViewHierarchyEncoder)
其中addProperty之前介紹過了,這裡不重複,這裡beginObject看著主要是對對應控制元件的資訊兩邊加個標記,類似於加個括號以區分
encodeProperties(View和ViewGroup)
這裡只分析下View和ViewGroup的方法,其他可能也有實現encodeProperties方法,但其基本邏輯作用都是相似的
先看下ViewGroup的encodeProperties方法:
這裡首先呼叫了super方法,也即會先呼叫View. encodeProperties方法
addProperty方法之前分析過,新增寫入的屬性和其值
然後是對控制元件的子控制元件進行遍歷,這裡會先呼叫encoder的addPropertyKey方法,其與addProperty相似,只是不寫入具體的資料值而已,這裡也只是起到一個隔離的作用,然後即是最關鍵的呼叫子控制元件的encode方法,這樣就可以將控制元件樹和其各控制元件的屬性列印出來
然後再看下View的encodeProperties,這裡列印了很多控制元件的屬性,包括對應LayoutParams、Theme等各種資訊
encoder.endStream
在前面的addProperty方法分析中有介紹其新增屬性名和屬性值時,屬性名並不是直接寫入的,而是先生成一個屬性名的map,然後寫入的是map中屬性名對應的value,也相當於其index,而這裡就是將map的資訊寫入(mPropertyNames),相當於一個索引
總結
根據前面的分析,總結下其寫入資料的邏輯,這裡一般有兩種資料,一種是作為類似括號功能的標記用以區分不同控制元件/不同作用的資料,一種是具體的控制元件的屬性等,其新增資料寫入,會生成一個類似索引表的map,將屬性名都放到索引表中,寫入資料時不直接寫屬性名,而是寫入其對應索引,然後寫入其值,在最後,則寫入索引表資訊
visible_windows解析
弄明白了生成的過程,其實解析也就不難了
而在Android原始碼中其實也有個簡易的例子:
frameworks/base/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/
這裡參考原始碼中的程式碼結合上面分析寫個完整的解析的程式碼(原始碼中Decoder.java可直接使用,這個也是包含最基本的解析資料的邏輯)
先第一步使用Decoder將資料進行解析列印出來(以NavigationBar的為例,這裡只是當作例子實現下功能,程式碼上可能有些地方並不算完善)
如上即是解析列印出來的檔案,雖然並不完善,但可以看到已經沒有亂碼了,而結合之前的生成邏輯,根據這裡的列印也可以識別出資訊內容了
第一行是1,查詢列印出來的索引表,其代表window:left,第二行是0,表示window:left是0
第三行是2,查詢列印出來的索引表,其代表window:top,第四行是2792,表示window:top是2792
這幾行也符合前面生成邏輯裡的最開始的列印資訊
要將解析更完善一步的話也很簡單,最後面的map是索引表,可將索引表的具體資訊直接替換成前面的索引index,這樣看著更方便,這裡不進一步舉例