項目地址:github.com/didi/booste…java
作過開發的同窗都深有體會,用 XML 來擼 UI 的效率簡直是吊打手寫代碼,在 Anko (Kotlin 庫) 尚未流行以前,你們對 XML 仍是親睞有加,雖然性能上偶爾會有卡頓,可是整體來講,仍是勉強能接受,可是 Anko 的流行,也讓咱們開始思考,有沒有辦法既能享受 XML 擼 UI 的快感,又能享受像 Anko 同樣的性能呢?android
XML 佈局是在運行時解析的,由 XmlPullParser 一邊解析二進制的 XML 文件一邊反射構造 View 節點,像 APP 首頁通常都是由不少 XML 組成,這樣會致使屢次「加載-解析」。都說手寫代碼性能更好,到底有多好呢?如下是 Anko 與 XML 的性能對比數據:git
機型 | 規格 | Anko | XML | 差別 |
---|---|---|---|---|
阿爾卡特 One Touch |
Mediatek MT6572 Dual-core 1.3GHz Cortex-A7 512MB RAM |
169 ms | 608 ms | 359% |
華爲 Y300 | Qualcomm MSM 8225 Dual-core 1.0GHz Cortex-A5 512MB RAM |
593 ms | 3435 ms | 578% |
華爲 Y330 | Mediatek MT6572 Dual-core 1.3GHz Cortex-A7 512MB RAM |
162 ms | 984 ms | 606% |
三星 Galaxy S2 | Exynos 4210 Dual-core 1.2GHz Cortex-A9 1GB RAM |
207 ms | 753 ms | 363% |
以上數據來源於 android.jlelse.eu/400-faster-…github
既然 Booster 是作專門用來作性能優化的,看到這裏,你們可能想到了解決方案——將 XML 轉譯成字節碼。沒錯,就是這樣,你們可能會問:轉譯成字節碼會有兼容性問題麼?——這得看實現方式。目前來看,有如下兩種實現:canvas
手寫代碼的思路api
在解析 XML 後,根據 XML 元素的屬性去調用對應的 API性能優化
RelativeLayout root = new RelativeLayout(context); TextView txt = new TextView(context); txt.setText(...); txt.setTextColor(...); txt.setTextSize(...); root.addView(txt); 複製代碼
運行時解析 XML 的思路markdown
在解析 XML 後,根據 XML 元素的屬性去構造 AttributeSet工具
AttributeSet attr = new AttributeSet(...); RelativeLayout root = new RelativeLayout(context, attr); TextView txt = new TextView(context, attr); 複製代碼
從以上兩種方案,咱們能夠發現:oop
所以,Booster 選擇了第 2 種方案,至於其中的細節和原理,請參考此示例工程。
Android SDK 提供了 LayoutInflator 用於將 XML 轉換成 View,讀過 AOSP 源碼的同窗可能會發現 LayoutInflator 是一個系統服務,爲何要將 LayoutInflator 做爲一個系統服務而不是一個工具類呢?我認爲,最主要的緣由在於提高性能。
「What? 調用系統服務會涉及到跨進程調用,怎麼可能會是爲了提高性能呢?」
這得從 XML 解析提及,由於 XML 佈局中會引用三類資源:
因此,對於系統資源來講
所以,基於以上緣由,須要有一個更高層次的資源管理,所以做爲一個系統服務合情合理。
爲了分析 LayoutInflator 構造 View 的過程,咱們想到了 Android Studio 的佈局可視化設計器,就是這個示例工程的由來,這樣就能夠在 IDE 中去單步調試 XML 的渲染過程。
Layout Library 由兩部分組成:
之因此這麼設計,主要仍是爲了考慮向後兼容。
Layout Library 的另外一大亮點在於它參用了相似 Robolectric Shadow 的方式,對 Android SDK 中的 API 進行了替換,這樣,本來調用 native 的 API 都在 Layout Library 中用 Java 實現了:
@LayoutlibDelegate static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, Bitmap bitmap, float left, float top, long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) { Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); if (bitmapDelegate != null) { BufferedImage image = bitmapDelegate.getImage(); float right = left + (float)image.getWidth(); float bottom = top + (float)image.getHeight(); drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(), image.getHeight(), (int)left, (int)top, (int)right, (int)bottom); } } 複製代碼
有興趣的同窗能夠用單步調式示例工程。
瞭解了 LayoutInflator 和 Layout Library 的實現原理後,對於 Booster 如何去轉換 XML 成爲字節碼,相信你們已經有了思路了,實現原理以下:
LayoutInflater.inflate(...)
Activity.setContentView(int)
Dialog.setContentView(int)