1. 程式人生 > >每日一問:不一樣的角度吐槽下 DataBinding

每日一問:不一樣的角度吐槽下 DataBinding

我們專案採用的是 kotlin && DataBinding 處理的,可能你會疑問,既然用的是 kotlin,為啥沒有用 kotlinx?新的頁面當然是用的 kotlinx 啦,但我們有相當龐大的歷史程式碼,並且我們的通用 adapter 其實也是基於 DataBinding 來封裝的。所以,我們還是不得不來討(吐)論(槽)一下這個 DataBinding 的坑。事實上,這個問題在我當年面試位元組跳動的時候就被問及過。

這是一個非常開放性的問題,所以看到這篇文章的小夥伴一定得帶一個有色眼鏡進行審視,下面會盡可能地列舉中筆者遇到過的坑,當然能力有限,有可能不少東西並不是 DataBinding 的問題,需要大家一起來甄別並進行補充。

DataBinding 是早些時候 Google 推出來的一個支援庫,只要勇於解決我們程式碼中頻繁出現的 findViewById() 問題,在此之前,我相信大部分人都聽過或者使用過 Butterknife,截止目前,該庫都已經更新到 10.1.0 了,而且作者是 JakeWharton 大神,實為相當好用。

但我們今天不對 Butterknife 進行過多的評論,而轉移到我們的主角 DataBinding。

對於 DataBinding 的使用和介紹這裡就不做講述了,建議大家還是直接到 官方文件
進行查閱學習,今天在這兒,主要和大家分享討論一下在筆者 1 年多的 DataBinding 中踩過的一些坑。我想這些 tips 肯定不是全部,並且有些其實是個人誤操所致,不管怎麼說,我們今天就捨棄對它的讚美,吐槽吐槽這個 DataBinding。

極難進行錯誤定位

oh,這應該是使用 DataBinding 的小夥伴們最最最大的坑點了,你甚至一定在各種技術輪胎和開發交流群中見過一些小夥伴對它的深惡痛絕。不管你程式碼中有了什麼錯誤,你一定收到的錯誤日誌是一大堆 DataBinding 生成的類找不到。

可能非常多的小夥伴會對此保持疑問,這不,真正的錯誤都在 build 日誌的最後能看到麼?實在沒有看到,你也可以在控制檯輸入 ./gradlew build --stacktrace 檢視到詳細日誌。

但事實真的如此麼?有沒有小夥伴經歷過不管你怎麼檢視日誌都找不到真正的錯誤程式碼的。

我想一定有!雖然我們一般都要求對 commit 保持「少量多次」的原則,但這並不能保證我們時刻都得嚴格遵守,在部分業務非常紊亂的時候,我們並不希望經常做 commit,因為每一次 commit,我們也得做一次編譯確認當前的程式碼沒有任何語法問題。而對於程式碼量龐大、尤其是還受各種歷史原因元件化不夠徹底的專案,一次編譯基本就可以讓你去吃一個飯了。我司的專案就是如此,受限於歷史原因,元件化做的並不徹底,導致本地編譯在沒有命中快取的情況下至少得 10 分鐘,這還是在 16G 的 mac pro 上。而且大多數人的電腦可能配置還更低,在編譯的時候很難做其他和工作相關的事情。

為了處理這個編譯問題,我司不得不編寫一個編譯指令碼,把編譯操作都在伺服器上進行處理,在做了一系列優化操作後,在沒命中快取的情況下編譯 debug 包還是需要 4.5 分鐘,當然現在在編譯中我們可以做其他有意思的事情了。

好像前面這兩段說了很多無關緊要的東西,但是!我真正想要表達的是,這時候出現了錯誤,並且日誌無法對錯誤進行定位,你會發現非常痛苦,你可能已經改動了數十個檔案,新建了不少 XML,因為無法定位到日誌,你不得不一行一行的進行語法檢查。

我在這個問題上體驗過編碼兩小時,查編譯失敗問題花費時間更多的尷尬情況,通常來說,這樣的錯誤都不容易發現,我深深的記住我有一次因為元件化歷史問題,不得不把一個元件程式碼 move 到另外一個元件,然後就發生了不可預料的錯誤的悲痛場景。當你 stash 改動後就可以編譯,但 pop 出來就錯誤的時候,你才會知道什麼是手段極其殘忍。

OK,目前對於這樣的情況,還是有所總結,這種情況 95% 是 XML 程式碼的問題,你可以直接檢查 XML 了。

程式碼極難維護

我們使用 DataBinding 的時候,必然會喜歡它的雙向繫結操作。在 XML 裡面直接做一些簡單的邏輯處理,這樣的操作讓我們的程式碼變得非常簡潔,並且可以省去 findViewById() 帶來的不必要的效能損耗。但這樣的操作讓後續維護功能程式碼的人非常痛苦。

我們的程式碼裡面就有相當部分的這樣的程式碼,部分頁面邏輯非常複雜,比如一個商品詳情頁,會牽扯到非常大量的資訊展示和各種促銷秒殺狀態,還有不少的動效,這時候不少邏輯放在 XML 裡面後,後續維護的同事在處理產品改動的時候,一定在心中暗自謾罵。

通常來說,我習慣於把這些複雜的邏輯放置在我們的 Java 程式碼中。就目前來看,在程式碼中檢視這些複雜的互動邏輯得心應手不少。

@{} 不會去做檢查

早些時候,我連續犯過兩次低階錯誤,所以對這個問題記憶深刻。我們可以發現,XML 裡面支援我們用類似 @{} 這樣的方式去為 TextView 設定顯示內容,但在 XML 裡面我們並沒有檢測機制,所以極易出現原本你這個是一個 Number 型別的值,編譯器卻當做一個 resourceId 進行處理而報錯。實際上,在程式碼裡面設定編譯器是會直接報錯的。

根據控制元件 ID 在程式碼裡面找一個東西很難

在我們專案的早期程式碼中,XML 裡面的命名規範基本是 xxx_xxx_xxx 這樣的格式,但在 DataBinding 裡面為我們生成的變數卻採用的是駝峰命名法,這導致我們根據一個控制元件 id 去對應 class 裡面尋找的時候,還得自己更改為駝峰命名法命名的名字,這一度讓我們感到非常不適,所以我們後面的程式碼 XML 命名規範就跟著變成駝峰命名法了。這可能和命名規範有些許出入,不過我們堅信適合自己的,才是最好的理念。

部分 XML 中的表示式在不同 gradle 版本上表現有所不同

前面說到,我們平時會採用伺服器編譯,所以此前有出現過 XML 檔案裡面的某個屬性設定在本地編譯不過,但在伺服器上甚至其他同事的電腦上可以編譯的問題。老實說,目前我並沒有找到真正的原因,我姑且把這個問題甩鍋給了此前做這個的同事。我後續更改了他的實現方式才讓這個問題得到妥善處理,但至今沒有明白問題出在哪裡,因為那樣的方式我認為本身程式碼是沒有什麼問題的。可惜我現在沒有時間去尋找這個程式碼,大概是設定一個公用的 onClickListener 的問題。

BindingAdapter 不好維護

我們通常會用到 @BindingAdapter 方式來做一些公用邏輯,而不是直接去把邏輯放在頁面通過設定屬性來使用它,這樣就會出現這些公用邏輯比較難維護,當然,這極有可能是我們專案的歷史問題,但我覺得這算是一個坑點了。不知道有沒有人出現這樣的屬性在 XML 裡面沒有提示的情況。就像你自定義 View Styleable 名字不唯一一樣。

多模組依賴問題

這個問題我之前還沒有發現,因為我們每個模組都用到了 DataBinding,所以認為每個模組的 gradle 都設定上 DataBinding 的配置,並不算什麼令人可以吐槽的事,但看起來這個問題挺嚴重的,所以也在這分享給大家。轉自 wanAndroid 上面 xujiafeng 的回答}

DataBinding在多模組開發的時候,有這樣一個機制:
如果子模組使用了 DataBinding,那麼主模組也必須在 gradle 加上配置,不然就會報錯;
如果主模組和子模組都新增上了 DataBinding 的配置,那麼在編譯時,子模組的 XML 檔案產生的 Binding 類除了在自己的 build 裡會有一份外,在主模組下也會有一份。
那麼,如果主模組與子模組都有一個 layout 根目錄的 activity_main.xml,主模組生成的 ActivityMainBinding 會是根據子模組的檔案生成的!這種情況我們還可以通過讓主模組和子模組使用不同的命名,那麼下面這個問題就更要命了:

如果子模組的某個 XML 檔案使用了一些第三方的控制元件,那麼主模組由於也會生成這個檔案的 Binding 類,並且其會有第三方控制元件的引用,這時候由於主模組沒有引入這些控制元件,就會報錯,解決辦法是在子模組應用第三方控制元件的時候,使用 API 的方式應用,這樣主模組就堅決引用到了這些第三方控制元件,這是這樣違背瞭解耦的原則。

哎,個人能力有限,就想到哪兒說到哪兒了。可能有不少其實並不是 databinding 的坑,是個人使用問題,還往明白的人能直接指出。PS:再給我一個機會,我不想在用 Databinding。

希望有其他槽點或者認為上面的東西是有更好的處理方式的小夥伴一定要在下面留言,盼復盼