View載入原理
我們經常在Activity的onCreate()中使用setContentView(R.layout.XX)來載入靜態佈局,今天我們一起來分析一下介面載入原理。所有技術都是這樣,知道原理以後就能做一些意想不到的事情,當然View的載入也是一樣。
廢話少說直接開幹,我們從呼叫處一層一層貼出了程式碼,在呼叫setContentView的時候實際上是呼叫的LayoutInflater的inflate方法
inflate中先對佈局xml進行了解析,解析以後將結果傳到過載的另一個inflate方法。
接下來我們點選進入另一個inflate方法,裡面首先會判斷佈局是否是merge include什麼的,這些我們先不管,只先分析最簡單的情況。如下圖,系統通過呼叫了一個createViewFromTag方法來獲取到一個view,那麼核心就是這個createViewFromTag方法了,所以我們必須繼續往下面看。
進入createViewFromTag方法,發現這裡很多判斷,但是不管怎麼判斷,最後都是呼叫的onCreateView方法,這個onCreateView方法最後點進去是通過反射來獲取,具體我這裡就不進去看了,大概就是先將佈局屬性傳入到反射constructor中,然後最後通過反射呼叫JNI底層程式碼生成的,生成以後再直接從constructor中獲取。
View載入攔截
我們再回過頭來分析一下最後onCreateView方法是怎麼被呼叫的。如上圖,首先判斷mFactory2是否為空,如果為空接下來再判斷mFactory是否為空,如果也為空的話接下來再判斷mPrivateFactory是否為空,如果也為空的話接下來這裡就直接呼叫反射程式碼獲取view了。也就是說如果我們提前設定好自己自定義的Factory的話,就不會呼叫系統自帶的建立view的方式,而是使用我們自定義的了。例如AppCompatActivity為了對控制元件做一些相容性,就通過setFactory的方式來自定義了一個factory,對很多控制元件重新初始化了一次。
借鑑AppCompatActivity的思路,我們也來弄一個自己的Factory,把專案中所有的TextView內容都換成helloworld吧,程式碼很簡單,如下:
最後,再將這個自定義的factory使用起來:
注意這裡的順序,setFactory2方法的呼叫一定要在super.onCreate之前,否則等系統設定了以後再設定是會報錯的。最後看一下效果,介面上所有TextView內容都被替換成helloworld了。執行的效果如下:
是不是很厲害,一鍵把介面上所有的TextView內容全部給換掉了,其實除了更換文字內容還有很多其他的用法,基本是可以設定的屬性都可以更換。主流的用法比如網易雲音樂的換膚方案,核心是結合外掛化技術實現在不閃爍的情況下更換app皮膚,將在下一篇文章中詳細講解。