MVVM陷阱之DataBinding(數(shù)據(jù)綁定庫)

本文已經(jīng)對(duì)《第一行代碼》作者郭霖的公眾號(hào)授權(quán)獨(dú)家發(fā)布

一、什么是DataBinding?

官方文檔的描述如下:

數(shù)據(jù)綁定庫是一種支持庫,借助該庫,您可以使用聲明性格式(而非程序化地)將布局中的界面組件綁定到應(yīng)用中的數(shù)據(jù)源。

布局通常是使用調(diào)用界面框架方法的代碼在 Activity 中定義的。例如,以下代碼調(diào)用 findViewById() 來查找 TextView 微件并將其綁定到 viewModel 變量的 userName 屬性:

 TextView textView = findViewById(R.id.sample_text);
 textView.setText(viewModel.getUserName());

以下示例展示了如何在布局文件中使用數(shù)據(jù)綁定庫將文本直接分配到微件。這樣就無需調(diào)用上述任何 Java 代碼。請(qǐng)注意賦值表達(dá)式中 @{} 語法的使用:

<TextView
        android:text="@{viewmodel.userName}" />

借助布局文件中的綁定組件,您可以移除 Activity 中的許多界面框架調(diào)用,使其維護(hù)起來更簡單、方便。還可以提高應(yīng)用性能,并且有助于防止內(nèi)存泄漏以及避免發(fā)生 Null 指針異常。


  • 目前在網(wǎng)絡(luò)上聽到過的質(zhì)疑聲主要有兩點(diǎn):
    1. 使用數(shù)據(jù)綁定會(huì)影響應(yīng)用性能。
    2. 數(shù)據(jù)綁定寫在布局里面維護(hù)起來會(huì)很困難。

針對(duì)以上兩點(diǎn),從上面的引用中可以看出,官方明確說了是可以提高應(yīng)用的性能,并且會(huì)使項(xiàng)目維護(hù)起來更方便。

  • 為什么會(huì)有人提出以上質(zhì)疑呢?
    他們的觀點(diǎn)大抵可以分為以下兩種:

    1. 數(shù)據(jù)綁定會(huì)生成額外的類進(jìn)行布局和數(shù)據(jù)的綁定
    2. 數(shù)據(jù)綁定會(huì)維護(hù)一個(gè)綁定數(shù)據(jù)表 DataBinderMapperImpl

    其實(shí)從源碼和實(shí)現(xiàn)原理上分析,上面的質(zhì)疑好像并沒有什么問題,但是仔細(xì)一想,針對(duì)第一點(diǎn),其實(shí)沒有數(shù)據(jù)綁定生成額外的類來進(jìn)行數(shù)據(jù)綁定,我們自己不是也得寫代碼進(jìn)行類似 findViewById()然后賦值等操作么?可以理解為這里只是運(yùn)用了面向?qū)ο蟮膯我宦氊?zé)原則進(jìn)行了很好的封裝。

    第二點(diǎn)就更神奇了,這個(gè)數(shù)據(jù)綁定表本身就是為了提高效率而設(shè)計(jì)的,用這一丁點(diǎn)的內(nèi)存(這是一個(gè)SparseIntArray而已,size是布局?jǐn)?shù)量),難道會(huì)比平時(shí)我們多用一張沒有壓縮過的圖片消耗大嗎?這點(diǎn)內(nèi)存而帶來的效率提高難道不值得嗎?

    補(bǔ)充一點(diǎn),如果因?yàn)樗^的應(yīng)用的效率而拒絕使用可以提高開發(fā)效率的技術(shù),那為什么我們不去用純C寫呢?native的效率不是更高么?其實(shí)我們都知道,對(duì)于大多是應(yīng)用來說,和游戲或者其他高內(nèi)存消耗的應(yīng)用相比,電商類或者工具類的應(yīng)用對(duì)內(nèi)存的消耗是很小的,在技術(shù)的取舍上要分得清主次。

二、哪些情況下我可以或者應(yīng)該用DataBinding?

在使用數(shù)據(jù)綁定之前你要清楚你要拿它來解決什么問題?

  1. 如果只是為了替代findViewById()那你可以去使用最新抽取出來的視圖綁定(ViewBinding)因?yàn)檫@個(gè)相對(duì)于數(shù)據(jù)綁定來說更輕巧,在DataBinding剛出來的時(shí)候并沒有單獨(dú)區(qū)分?jǐn)?shù)據(jù) or 視圖綁定,這是在新的版本中,為了更好的設(shè)計(jì),將ViewBinding進(jìn)行了單獨(dú)的抽取。
    從最新版本的代碼可以看出:

      public abstract class ViewDataBinding extends BaseObservable 
      implements ViewBinding {}
    

    抽象類ViewDataBinding實(shí)現(xiàn)了ViewBinding接口,這個(gè)名字也是取得恰到好處,View-Data-Binding,其實(shí)大家常說的DataBinding是包含ViewBinding的。
    說句題外話,如果只是為了替代findViewById(),并且是使用kotlin開發(fā)的話,也可以考慮Kotlin-android-extension,KAE具有和ViewBinding差不多的功能,具體使用方式在此也不介紹了,有興趣的可以去找相關(guān)資料。`

  2. 為了實(shí)現(xiàn)數(shù)據(jù)綁定,類似數(shù)據(jù)驅(qū)動(dòng)架構(gòu),MVVM架構(gòu)等。恭喜你,這時(shí)候Databinding是不錯(cuò)的選擇。
    說到這里又涉及到MVCMVP,MVVM設(shè)計(jì)架構(gòu)的區(qū)別,相信大家對(duì)這幾個(gè)概念都有所了解。
    首先,明確一點(diǎn),架構(gòu)的目的就是為了提高開發(fā)效率,降低維護(hù)成本。
    利用面向?qū)ο蟮脑O(shè)計(jì)原則,對(duì)每個(gè)模塊的職責(zé)進(jìn)行合理的劃分,為了讓其他人更好的理解架構(gòu)設(shè)計(jì)思想,然后給予每個(gè)模塊一個(gè)通用的名詞解釋,為了更好的說清楚數(shù)據(jù)綁定,這里就簡單解釋一下目前常見的幾種架構(gòu)(以Android為例)。

    • MVC
      • 模型層(Model),負(fù)責(zé)處理數(shù)據(jù)邏輯,一般包含數(shù)據(jù)庫、本地?cái)?shù)據(jù)、網(wǎng)絡(luò)獲取的Bean等組成。
      • 視圖層(View),負(fù)責(zé)處理視圖顯示,一般由XML布局承擔(dān)此責(zé)任,基本組件和自定義View等充當(dāng)視圖層的補(bǔ)充元素。
      • 控制層(Control),負(fù)責(zé)處理業(yè)務(wù)邏輯,一般由Activity、Fragment承擔(dān)此責(zé)任。
    • MVP
      • 模型層(Model),負(fù)責(zé)處理數(shù)據(jù)邏輯,一般包含數(shù)據(jù)庫、本地?cái)?shù)據(jù)、網(wǎng)絡(luò)獲取的Bean等組成。
      • 視圖層(View),負(fù)責(zé)處理視圖顯示,一般由XML布局承擔(dān)此責(zé)任,基本組件和自定義View等充當(dāng)視圖層的補(bǔ)充元素,Activity、Fragment充當(dāng)視圖層和控制層的粘合劑。
      • ??(Presenter),負(fù)責(zé)處理業(yè)務(wù)邏輯,由從原來MVC控制層中抽取出來的Presenter充當(dāng)控制層(Presenter)。
    • MVVM
      • 模型層(Model),負(fù)責(zé)處理數(shù)據(jù)邏輯,一般包含數(shù)據(jù)庫、本地?cái)?shù)據(jù)、網(wǎng)絡(luò)獲取的Bean、(這里我單獨(dú)抽取的視圖數(shù)據(jù)ViewData概念也屬于Model層)等組成。
      • 視圖層(View),負(fù)責(zé)處理視圖顯示,一般由XML布局承擔(dān)此責(zé)任,基本組件和自定義View等充當(dāng)視圖層的補(bǔ)充元素,Activity、Fragment主要負(fù)責(zé)視圖層綁定事件觸發(fā),熟練的話也可以直接在XML中綁定觸發(fā)事件。
      • ??(ViewModel),通過數(shù)據(jù)綁定連接ViewModel(這里由ViewData充當(dāng)視圖模型被綁定到視圖上)實(shí)現(xiàn)視圖層和模型層的解藕,事件觸發(fā)后通過ViewModel處理業(yè)務(wù)邏輯,并且通過數(shù)據(jù)驅(qū)動(dòng)的方式修改視圖數(shù)據(jù),而達(dá)到間接修改視圖的功能。注意:ViewModel一定不能持有視圖層的引用,同樣不能持有Context的引用!不然還是MVP!

    對(duì)于新手來說,看完上面說明,更讓人覺得摸不著頭腦,只是換一個(gè)名字而已,最終不還是分三層嗎?視圖層(View)、數(shù)據(jù)層(Model)、邏輯處理層(???),這么簡單的東西,為什么搞得很高深莫測的樣子?

    參考一下圖示也許你就豁然開朗了:

    image

    其實(shí)不同的設(shè)計(jì)架構(gòu)最終目的還是為了解耦,實(shí)現(xiàn)高內(nèi)聚低耦合一直是架構(gòu)師的理想,這種情況下,每個(gè)程序員只需要關(guān)心自己的模塊就可以了。

    • 就拿MVVM來說,當(dāng)一個(gè)項(xiàng)目足夠大的時(shí)候,可能有的人負(fù)責(zé)界面繪制(XML),有的人負(fù)責(zé)業(yè)務(wù)邏輯處理(ViewModel),有的人負(fù)責(zé)數(shù)據(jù)邏輯處理(Model)。這時(shí)候,每個(gè)模塊的人只需要關(guān)心自己的邏輯就可以了,而且每個(gè)模塊都可以單獨(dú)跑Use Case,每個(gè)模塊并沒有很強(qiáng)的依賴關(guān)系,而且當(dāng)某個(gè)模塊的邏輯變更了并不一定會(huì)影響到其他模塊的變更。
有幾點(diǎn)要注意:

1、官方的ViewModel庫并不是實(shí)現(xiàn)MVVM架構(gòu)的必備,MVVM的重點(diǎn)是解藕,通過一定方式解除ViewModel的耦合,比如使用數(shù)據(jù)綁定庫DataBinding

2、也有不使用DataBinding實(shí)現(xiàn)的MVVM嗎?其實(shí)也有,比如說郭神第三版的《第一行代碼》中的方式,利用LiveData實(shí)現(xiàn)ViewModel的解藕,且ViewModel不依賴ViewContext,這里郭神把ActivityFragment當(dāng)作View的主體,而我更傾向于把XML當(dāng)作View的主體,所見即所得,看得到的當(dāng)成View,會(huì)更直觀一點(diǎn)。ActivityFragment只是當(dāng)作一個(gè)粘合劑,比如進(jìn)行事件綁定和一些復(fù)雜動(dòng)畫的處理等。所以DataBinding更多的是服務(wù)于XML這種View的。

3、ViewModel庫是在DadaBinding庫之后才有的,ViewModel類旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存,這樣可以更好的提升用戶體驗(yàn)和提高應(yīng)用性能。

image

上圖說明了Activity經(jīng)歷屏幕旋轉(zhuǎn)而后結(jié)束的過程中所處的各種生命周期狀態(tài)。該圖還在關(guān)聯(lián)的 Activity生命周期的旁邊顯示了ViewModel的生命周期。此圖表說明了 Activity 的各種狀態(tài)。這些基本狀態(tài)同樣適用于 Fragment 的生命周期。
4、其實(shí)可以從官方的介紹中看出來,官方的ViewModel庫和我們所說的MVVM架構(gòu)中的ViewModel層并不是等價(jià)的東西,ViewModel層不止包含ViewModel數(shù)據(jù)(我更愿意稱為ViewData),還應(yīng)該包含視圖模型的邏輯處理。

總的來說,為了提高開發(fā)效率,為了更好的在大型團(tuán)隊(duì)中協(xié)調(diào)開發(fā),MVVM是一個(gè)不錯(cuò)的選擇!目前為止,個(gè)人認(rèn)為DataBinding、ViewModel,再加上LiveData,是搭建MVVM架構(gòu)最完美的組合。

三、DataBinding的基本使用方式

  • 第一步:要將應(yīng)用配置為使用數(shù)據(jù)綁定,請(qǐng)?jiān)趹?yīng)用模塊的 build.gradle 文件中添加 dataBinding 元素,如以下示例所示:
    android {
        ...
        dataBinding {
            enabled = true
        }
    }
    

注意:即使應(yīng)用模塊不直接使用數(shù)據(jù)綁定,也必須為依賴于使用數(shù)據(jù)綁定的庫的應(yīng)用模塊配置數(shù)據(jù)綁定。

  • 第二步:修改原來的XML布局文件,在原布局外層包裹一層layout標(biāo)簽,并且使用datavariable標(biāo)簽,添加需要綁定的數(shù)據(jù),如下所示:
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data calss="MainBinding">
           <variable name="user" type="com.example.User"/>
       </data>
    <!-- 原布局開始-->
         <LinearLayout 
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName, default=default_value}"/>
       </LinearLayout>
    <!-- 原布局結(jié)束-->
    </layout>
    
  • 第三步:設(shè)置布局頁面以及綁定數(shù)據(jù)到頁面
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       MainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
       User user = new User("Test", "User");
       binding.setUser(user);
    }
    
目前為止,布局已經(jīng)和數(shù)據(jù)進(jìn)行了綁定,其實(shí)這時(shí)候和平時(shí)我們使用findViewById()然后再setText()差別并不大,精彩的是后面。

四、DataBinding的高級(jí)用法

上面雖然實(shí)現(xiàn)了基本的數(shù)據(jù)綁定,但是改變?cè)瓉淼臄?shù)據(jù)對(duì)象,界面并不會(huì)發(fā)生改變,這個(gè)時(shí)候就需要引入一個(gè)新的概念,可觀察字段

可觀察性是指一個(gè)對(duì)象將其數(shù)據(jù)變化通知給其他對(duì)象的能力。通過數(shù)據(jù)綁定庫,您可以讓對(duì)象、字段或集合變?yōu)榭捎^察。
通過數(shù)據(jù)綁定,數(shù)據(jù)對(duì)象可在其數(shù)據(jù)發(fā)生更改時(shí)通知其他對(duì)象,即監(jiān)聽器。可觀察類有三種不同類型:對(duì)象、字段和集合
當(dāng)其中一個(gè)可觀察數(shù)據(jù)對(duì)象綁定到界面并且該數(shù)據(jù)對(duì)象的屬性發(fā)生更改時(shí),界面會(huì)自動(dòng)更新。

  • 最早的時(shí)候使用ObservableField或者對(duì)基本數(shù)據(jù)類型封裝好的ObservableField,比如ObservableBoolean、ObservableByte
    、ObservableChar、ObservableInt等,更輕量級(jí)的還有自己通過調(diào)用notifyPropertyChanged控制數(shù)據(jù)刷新。

  • 后來LiveData的出現(xiàn),可以使用LiveData代替ObservableField并獲得更好的生命周期管理。因此這里主要講講使用LiveData實(shí)現(xiàn)ModelView的解藕。

  • 目前這些方式都是支持的,從代碼中可以看出,最終都會(huì)通過注冊(cè),并統(tǒng)一由mLocalFieldObservers進(jìn)行管理。
    ObservableField

       protected boolean updateRegistration(int localFieldId, Observable observable) {
          return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
      }
    

    LiveData調(diào)用差不多:

     protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
          mInLiveDataRegisterObserver = true;
          try {
              return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
          } finally {
              mInLiveDataRegisterObserver = false;
          }
      }
    

    最終都調(diào)用到同樣的一個(gè)函數(shù):

     protected void registerTo(int localFieldId, Object observable,
              CreateWeakListener listenerCreator) {
          if (observable == null) {
              return;
          }
          WeakListener listener = mLocalFieldObservers[localFieldId];
          if (listener == null) {
              listener = listenerCreator.create(this, localFieldId);
              mLocalFieldObservers[localFieldId] = listener;
              if (mLifecycleOwner != null) {
                  listener.setLifecycleOwner(mLifecycleOwner);
              }
          }
          listener.setTarget(observable);
      }
    

其實(shí)說白了就是觀察這模式的靈活運(yùn)用,最終實(shí)現(xiàn)了數(shù)據(jù)的綁定,對(duì)于雙向綁定也沒有什么特別的,自動(dòng)生成的代碼會(huì)根據(jù)我們?cè)诓季种惺欠裨O(shè)定了雙向綁定,而主動(dòng)幫我們?cè)O(shè)置一個(gè)InverseBindingListener監(jiān)聽,XML中格式如下:android:text="@={...}"。

小知識(shí)點(diǎn)

  • 可以在XML中使用default(默認(rèn)值)也可以不用。

  • 可以在data標(biāo)簽中,通過class屬性指定要生成的DataBinding文件名字。

  • 可以在data標(biāo)簽中,通過import標(biāo)簽導(dǎo)入需要使用的類類型。

  • Null 合并運(yùn)算符

    android:text="@{user.displayName ?? user.lastName}"
    

    這在功能上等效于:

    android:text="@{user.displayName != null ? user.displayName : user.lastName}"
    
  • 要使XML不含語法錯(cuò)誤,您必須轉(zhuǎn)義 < 字符。例如:不要寫成 List<String> 形式,而是必須寫成 List&lt;String>。

  • BindingAdapter、InverseBindingAdapter等注解的使用
    例如設(shè)置一個(gè)斜體綁定適配器:

    
      @BindingAdapter({"strike"})
      public static void setStrike(TextView view, boolean strike) {
          if (strike) {
              view.setPaintFlags(view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
          } else {
              view.setPaintFlags(view.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
          }
      }
    

    在布局中使用

    <TextView
      ...
      bind:strike="@{true}"
      ...
      />
    
  • 設(shè)置事件監(jiān)聽

    <!-- 在data標(biāo)簽添加監(jiān)聽器綁定-->
    <variable
            name="onClickListener"
            type="android.view.View.OnClickListener" />
    <!-- 在需要綁定點(diǎn)擊事件的控件上添加綁定-->
    ...
    <View android:onClick="@{onClickListener::onClick}"/>
    ...
    

五、優(yōu)雅的使用DataBinding實(shí)現(xiàn)MVVM

  • 通過對(duì)源碼的分析可以得知,無論是使用ObservableField還是使用LivaData 最終都會(huì)在本地屬性觀察者mLocalFieldObservers中注冊(cè)監(jiān)聽,其實(shí)數(shù)據(jù)綁定庫的使用非常的靈活,除了在上面提到的使用方式,還有更多的使用方式,甚至可以在布局XML中進(jìn)行邏輯判斷,事件綁定,布局管理器設(shè)定,列表子布局設(shè)定等,可以說是無所不能。

  • 其實(shí)正是這種靈活,也遭受了很多使用者的詬病,說很多邏輯寫在XML中,調(diào)試?yán)щy,維護(hù)麻煩,更有甚者,跳出來直接得出DataBinding不能用的結(jié)論,這就好比說菜刀能傷人就說菜刀不好一樣。

有問題的不是工具,而是使用工具的方式!

  • 為了給DataBinding正名,因此總結(jié)一些使用原則,分享如下。

    • 原則一:能不用可觀察變量盡量不要用。
    • 原則二:多個(gè)變量會(huì)同時(shí)改變的情況盡量使用一個(gè)可觀察變量進(jìn)行包裝。
    • 原則三data標(biāo)簽?zāi)苌賹?dǎo)入一個(gè)變量盡量少導(dǎo)入。
    • 原則四:XML布局盡量少或者不使用過多的邏輯判斷。
    • 原則五:避免對(duì)一個(gè)數(shù)據(jù)進(jìn)行多次綁定(有人通過這種方式刷新界面,這個(gè)其實(shí)和DataBinding的初衷違背了)。
    • 原則六:嚴(yán)格遵守上述五條。
  • 基于以上六條使用原則,目前經(jīng)過多次迭代,總結(jié)出了滿足絕大多數(shù)場景的MVVM。

    • 第一步
      整個(gè)XML使用統(tǒng)一的格式,無論是普通的布局,還是列表的Item布局,抑或是include的布局,都是使用同樣的方式,這樣就可以使用AndroidStudioFile Templates模版功能創(chuàng)建布局文件了。

      <?xml version="1.0" encoding="utf-8"?>
      <layout xmlns:android="http://schemas.android.com/apk/res/android">
         <data>
      <!-- 用于控制顯示隱藏導(dǎo)入此類-->
             <import type="android.view.View" />
      <!-- 用于點(diǎn)擊事件綁定-->
             <variable name="onClickListener" type="android.view.View.OnClickListener" />
      <!-- 用于視圖數(shù)據(jù)綁定-->
             <variable name="viewData" type="com.example.UserViewData"/>
         </data>
      <!-- 原布局開始-->
           <LinearLayout 
             android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="match_parent">
             <TextView android:id="@+id/helloSomeOne"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:onClick="@{onClickListener::onClick}"
                 android:text="@{viewData.firstName}"/>
             <TextView android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:visibility="@{viewData.noLastName?View.GONE:View.VISIBLE}"
                 android:text="@{viewData.lastName, default=default_value}"/>
      <!-- 包含另一個(gè)布局 并傳遞事件綁定和視圖數(shù)據(jù)綁定-->
              <include
                  android:id="@+id/includeViewId"
                  layout="@layout/include_view_layout"
                  bind:onClickListener="@{onClickListener}"
                  bind:viewData="@{viewData}" />
         </LinearLayout>
      <!-- 原布局結(jié)束-->
      </layout>
      
    • 第二步
      創(chuàng)建BaseBindActivityBaseBindFragment,實(shí)現(xiàn)底層的數(shù)據(jù)綁定,以及生命周期設(shè)定,以及事件綁定。

      public abstract class BaseBindActivity<B extends ViewDataBinding> extends Activity implements  View.OnClickListener {
      private B mBinding;
      
      /**
       * 數(shù)據(jù)綁定
       */
      protected abstract <ViewData> ViewData getViewData();
      
      /**
       * 子類提供有binding的資源ID
       */
      @LayoutRes
      protected abstract int getLayoutID();
      
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          mBinding = DataBindingUtil.setContentView(this, getLayoutID());
          if (mBinding != null) {
              mBinding.setLifecycleOwner(this);
              mBinding.setVariable(BR.onClickListener, this);
              mBinding.setVariable(BR.viewData, getViewData());
          } else {
              setContentView(getLayoutID());
          }
      }
      
      @Override
      public B getBinding() {
          return mBinding;
      }
      }
      

      這樣子頁面只需要實(shí)現(xiàn)簡單的邏輯處理就可以了。BaseBindFragment邏輯類似就不貼代碼了。

    • 第三步
      在具體業(yè)務(wù)Activity中通過 ViewModelProviders獲取ViewModel,并從ViewModel中獲取ViewData,將ViewData綁定到視圖中,子類通過實(shí)現(xiàn) getViewData(),進(jìn)行綁定操作。

      ViewModelProviders.of(this, factory).get(viewModelClass)
      
    • 第四步
      當(dāng)用戶操作(比如點(diǎn)擊)導(dǎo)致一個(gè)事件產(chǎn)生,在具體業(yè)務(wù)Activity中,通過ViewModel的方法調(diào)用業(yè)務(wù)數(shù)據(jù)提供方,并實(shí)現(xiàn)業(yè)務(wù)邏輯,業(yè)務(wù)處理完成后,操作ViewData中的屬性,實(shí)現(xiàn)動(dòng)態(tài)更新界面的功能。

很多邏輯具有通用性,我們可以抽取很多模版代碼作為基類使用,比如說列表的ListAdapter、數(shù)據(jù)庫Room、數(shù)據(jù)差分類DiffUtil.ItemCallback、RecyclerView的ViewHolder都可以進(jìn)行很好的封裝,使用時(shí)就會(huì)變得很簡單,以后再也不用處理那么多的AdapterViewHolder了。

因?yàn)槠?,這里就不詳細(xì)介紹了,有興趣的可以參考github源碼,源碼里面的README也有一部分介紹。
源碼傳送門

GitHub地址:https://github.com/codyer/component

說明
因?yàn)?code>component是實(shí)際開發(fā)時(shí)可能會(huì)使用到的一些組件,為了方便,我之前放在一個(gè)項(xiàng)目下統(tǒng)一管理的,因此這個(gè)項(xiàng)目包含很多模塊,感興趣的歡迎StarFork
如果只想看MVVM的代碼可以只參考以上項(xiàng)目的一個(gè)modlue
MVVM-模塊

GitHub地址:https://github.com/codyer/component/blob/master/app-core/README.md

至此,為DataBinding的正名就算完成了。

尾記

寫這篇文章花了整整一天的時(shí)間,希望走過路過的伙伴們不要吝嗇一個(gè)點(diǎn)贊和Star

另外,之前因?yàn)闄C(jī)緣巧合實(shí)現(xiàn)了一套基于LiveData的事件總線LiveEventBus,自己使用下來發(fā)現(xiàn)其實(shí)還是很不錯(cuò)的,之前一直做項(xiàng)目也不空閑,再加上覺得好像大家都可以實(shí)現(xiàn),也沒有什么太難的地方,也就沒有想著寫出來分享一下。

最近看見有人實(shí)現(xiàn)了一個(gè)類似的庫,而且還在各種地方發(fā)表,首先很佩服他的分享精神,也希望大家多向這樣的同學(xué)學(xué)習(xí),但是呢,看了他的源碼和說明,他的實(shí)現(xiàn)方式,感覺還是有優(yōu)化的地方,而且還有些許問題,因此和這次為DataBinding正名一樣,激起了我的保護(hù)欲,為了保護(hù)技術(shù),保護(hù)大家優(yōu)雅的使用姿勢,決定花花時(shí)間整理一下,教教大家如何優(yōu)雅的使用LiveData實(shí)現(xiàn)一套EventBus。

如果有下一篇,它將是《如何優(yōu)雅的使用LiveData實(shí)現(xiàn)一套EventBus

~敬請(qǐng)期待~
----------------------------------------------------------- by Cody.yi

謝謝閱讀

寫得越多發(fā)現(xiàn)自己不知道的越多,如有錯(cuò)誤缺漏之處歡迎指正!

下一篇以及完成,歡迎閱讀
http://www.itdecent.cn/p/79d909b6f8bd

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容