- 原文地址:Data Binding — Lessons Learnt
- 原文做者:Chris Banes
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Mirosalva
- 校對者:DevMcryYu
Data Binding 庫(下文中以『DB 庫』詞語來指代)提供了一個靈活強大的方式來綁定數據到 UI 界面。可是要用一句陳詞濫調:『能力越大,責任越大』,僅僅是使用數據綁定,並不意味着你能夠避免成爲一個優秀 UI 開發者。html
過去的幾年我一直在 Android 開發中使用 data binding 庫,本文會寫出我這一路上了解到的與它有關的一些內容細節。前端
自定義 binding adapter 是一種給 View 控件輕鬆提供自定義功能的好方法。和許多開發者同樣,我對 binding adapter 研究得稍微深刻,最終總結出一套包含 15 種不一樣用途的適配器的類集。java
最糟糕的實踐是這類適配器,它們生成格式化的字符串並設置到 TextViews
控件,這些適配器一般僅在同一個佈局文件中使用:android
雖然這可能看起來很聰明,可是有三大缺點:ios
優化它們的過程太痛苦。除非你把代碼組織得很是好,不然你可能會有一個包含全部適配器方法的大文件,這與代碼內聚和解耦原則相違背。git
你須要使用 instrumentation 工具來作測試。根據定義,你的 binding adapter 不會有返回值,它們接收一個輸入參數後設置 view 的屬性。這就意味着你必須使用 instrumentation 來測試你的自定義邏輯,這樣會使得測試變得既緩慢又難以維護。github
自定義 binding adapter 代碼(一般)不是最佳選項。若是你查看內建文本綁定[參考這裏],你將會看到已經作了許多檢查來避免調用 TextView.setText()
,這樣就節省了被浪費的佈局檢測。我以爲本身陷入了這樣的思惟困境:DB 庫將會自動優化個人 view 更新。它確實能夠作到,但僅限於你使用被謹慎優化的內建 binding adapter的狀況。後端
相反的,把你的方法的邏輯抽象爲內聚類(我稱之爲文本建立者類),而後將它們傳遞給 binding。這樣你就能夠調用你的文本建立者類並使用內建 view binding:app
這樣咱們能夠從內建的綁定操做過程當中提升效率,而且咱們能夠很是輕鬆地對建立格式化字符串的代碼進行單元測試。less
若是你確實須要使用自定義適配器,由於你所需的功能不存在,請儘可能使其變得高效。個人意思是使用全部標準的 Android UI 優化:儘量避免觸發測量/佈局操做。
這能夠像檢查當前使用的視圖以及你設置的內容同樣簡單。這裏有一個咱們爲 android:drawable
從新實現了標準 ImageView adapter 的樣例:
遺憾的是,視圖並不老是可以顯示咱們須要檢查的狀態。這裏有一個在 TextView 上設置切換最大行的示例。它經過改變 TextView 的 maxLines
屬性以及一個延時佈局轉換(android.view.ViewGroup)來實現切換。
以前 binding adapter 比較簡單而且老是設置了 maxLines
屬性和一個點擊監聽對象。TextView 在 setMaxLines()
被調用後總會觸發一次佈局,這就意味着每次 binding adapter 啓動,一次佈局就會被觸發。
讓咱們改變這個狀況。因爲此功能與 TextView 是徹底分開的(咱們只是在單擊時使用不一樣的值調用 setMaxLines()
),咱們須要將引用存儲爲當前狀態。幸運的是,『DB 庫』爲咱們提供了一個手工方式去在 binding adapter 中接收狀態。經過提供參數兩次:第一個參數接收當前值,第二個參數接收新值。
因此這裏咱們只需比較當前的和新的 collapsedMaxLines
值。若是值實際發生了改變,咱們纔去調用 setMaxLines()
等方法。
編輯按: 感謝 Alexandre Gianquinto 在評論中提到『double parameters』功能。
我一直在慢慢的從新設計 Tivi,使用相似 MVI 的東西,使用優秀的 MvRx 庫來使它變得規範化。這在實踐中意味着個人 fragment/view 訂閱到 ViewModel對象,而且接收 ViewStates 的實例。這些實例包含全部用於顯示 UI 的必要狀態。
這是一個展現 Tivi(連接)中類的樣例:
你能夠看到它僅僅是一個簡單的數據類,包含了 UI 須要在一個 TV 秀界面上顯示的全部細節 UI 元素。
聽起來像是傳遞咱們的 data binding 實例對象的完美選項,讓咱們的 binding 表達式來去更新 UI,對吧?好吧這確實有效,可是有一些須要注意的地方,這是因爲『DB 庫』的工做機制。
在 data binding 中你經過 <variable>
標籤聲明瞭輸入,而後在書寫 binding 表達式時在 view 屬性處引用了這些輸入變量。當任何被依賴的變量發生變化,『DB 庫』都會運行你的 binding 表達式(接着會更新 view)。這個變化檢測就是你能夠免費獲取的很棒的優化。
因此回到個人場景,個人佈局最終看起來是這樣的:
因此我最終獲取一個包含全部 UI 狀態的全局 ViewState 實例,而且你能夠想象出這些狀態常常會發生變化。UI 狀態的任何輕微變化都會產生一個全新的 ViewState,並被傳遞到咱們的 data binding 實例。
因此問題是什麼?因爲咱們只有一個輸入變量,全部的 binding 表達式將會引用變量,這就意味着『DB 庫』將沒法自由選擇運行哪一個表達式。在實際過程當中,這意味着每次變量變化(無論多小的變化)發生時全部的 binding 表達式都會運行。
這個問題與 MVI 這點無關,特別是它只是組合狀態的 artifact,與data binding 結合在一塊兒使用。
有種替代方法是在佈局中顯式聲明 ViewState 中的每一個變量,而後顯式傳遞組合狀態實例中的值,以下所示:
這顯然會使開發人員維護和同步更多的代碼,但它確實意味着『DB 庫』能夠優化去運行哪些表達式。若是你的 UI 狀態不常常變化(可能在建立時有一些次)而且變量數量較少時,我會推薦使用此模式。
我我的一直在佈局中使用單個變量,傳入個人 ViewState 實例,並依賴於咱們的視圖綁定合理地運行。這就是爲何讓視圖綁定變得高效很是重要。
另外一個須要注意的是 Tivi 是 RecyclerView 的重度使用者,還有 Epoxy 和 Data Binding,意思就是在 DiffUtil 中會額外有一些變化相關的計算髮生。因此若是你的 UI 也有大量的 RecyclerView 組成,你能夠相似上文描述不費事地獲取計算這方面的優化。
但願這篇文章強調了一些能夠優化數據綁定實現方案中的一些小事。瞭解『DB 庫』的內部機制能夠幫助你提升數據綁定效率,並提升你的 UI 性能。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。