Android MVVM 應用框架構建過程詳解

Android開發中文站發表於2017-02-24

概述

說到Android MVVM,相信大家都會想到Google 2015年推出的DataBinding框架。然而兩者的概念是不一樣的,不能混為一談。MVVM是一種架構模式,而DataBinding是一個實現資料和UI繫結的框架,是構建MVVM模式的一個工具。

之前看過很多關於Android MVVM的部落格,但大多數提到的都是DataBinding的基本用法,很少有文章仔細講解在Android中是如何通過DataBinding去構建MVVM的應用框架的。View、ViewModel、Model每一層的職責如何?它們之間聯絡怎樣、分工如何、程式碼應該如何設計?這是我寫這篇文章的初衷。

接下來,我們先來看看什麼是MVVM,然後再一步一步來設計整個MVVM框架。

MVC、MVP、MVVM

首先,我們先大致瞭解下Android開發中常見的模式。

MVC

View:XML佈局檔案。

Model:實體模型(資料的獲取、儲存、資料狀態變化)。

Controllor:對應於Activity,處理資料、業務和UI。

從上面這個結構來看,Android本身的設計還是符合MVC架構的,但是Android中純粹作為View的XML檢視功能太弱,我們大量處理View的邏輯只能寫在Activity中,這樣Activity就充當了View和Controller兩個角色,直接導致Activity中的程式碼大爆炸。相信大多數Android開發者都遇到過一個Acitivty數以千行的程式碼情況吧!所以,更貼切的說法是,這個MVC結構最終其實只是一個Model-View(Activity:View&Controller)的結構。

MVP

View: 對應於Activity和XML,負責View的繪製以及與使用者的互動。

Model: 依然是實體模型。

Presenter: 負責完成View與Model間的互動和業務邏輯。

前面我們說,Activity充當了View和Controller兩個角色,MVP就能很好地解決這個問題,其核心理念是通過一個抽象的View介面(不是真正的View層)將Presenter與真正的View層進行解耦。Persenter持有該View介面,對該介面進行操作,而不是直接操作View層。這樣就可以把檢視操作和業務邏輯解耦,從而讓Activity成為真正的View層。

但MVP也存在一些弊端:

  • Presenter(以下簡稱P)層與View(以下簡稱V)層是通過介面進行互動的,介面粒度不好控制。粒度太小,就會存在大量介面的情況,使程式碼太過碎版化;粒度太大,解耦效果不好。同時對於UI的輸入和資料的變化,需要手動呼叫V層或者P層相關的介面,相對來說缺乏自動性、監聽性。如果資料的變化能自動響應到UI、UI的輸入能自動更新到資料,那該多好!
  • MVP是以UI為驅動的模型,更新UI都需要保證能獲取到控制元件的引用,同時更新UI的時候要考慮當前是否是UI執行緒,也要考慮Activity的生命週期(是否已經銷燬等)。
  • MVP是以UI和事件為驅動的傳統模型,資料都是被動地通過UI控制元件做展示,但是由於資料的時變性,我們更希望資料能轉被動為主動,希望資料能更有活性,由資料來驅動UI。
  • V層與P層還是有一定的耦合度。一旦V層某個UI元素更改,那麼對應的介面就必須得改,資料如何對映到UI上、事件監聽介面這些都需要轉變,牽一髮而動全身。如果這一層也能解耦就更好了。
  • 複雜的業務同時也可能會導致P層太大,程式碼臃腫的問題依然不能解決。

MVVM

View: 對應於Activity和XML,負責View的繪製以及與使用者互動。

Model: 實體模型。

ViewModel: 負責完成View與Model間的互動,負責業務邏輯。

MVVM的目標和思想與MVP類似,利用資料繫結(Data Binding)、依賴屬性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一個更加靈活高效的架構。

資料驅動

在常規的開發模式中,資料變化需要更新UI的時候,需要先獲取UI控制元件的引用,然後再更新UI。獲取使用者的輸入和操作也需要通過UI控制元件的引用。在MVVM中,這些都是通過資料驅動來自動完成的,資料變化後會自動更新UI,UI的改變也能自動反饋到資料層,資料成為主導因素。這樣MVVM層在業務邏輯處理中只要關心資料,不需要直接和UI打交道,在業務處理過程中簡單方便很多。

低耦合度

MVVM模式中,資料是獨立於UI的。

資料和業務邏輯處於一個獨立的ViewModel中,ViewModel只需要關注資料和業務邏輯,不需要和UI或者控制元件打交道。UI想怎麼處理資料都由UI自己決定,ViewModel不涉及任何和UI相關的事,也不持有UI控制元件的引用。即便是控制元件改變了(比如:TextView換成EditText),ViewModel也幾乎不需要更改任何程式碼。它非常完美的解耦了View層和ViewModel,解決了上面我們所說的MVP的痛點。

更新UI

在MVVM中,資料發生變化後,我們在工作執行緒直接修改(在資料是執行緒安全的情況下)ViewModel的資料即可,不用再考慮要切到主執行緒更新UI了,這些事情相關框架都幫我們做了。

團隊協作

MVVM的分工是非常明顯的,由於View和ViewModel之間是鬆散耦合的:一個是處理業務和資料、一個是專門的UI處理。所以,完全由兩個人分工來做,一個做UI(XML和Activity)一個寫ViewModel,效率更高。

可複用性

一個ViewModel可以複用到多個View中。同樣的一份資料,可以提供給不同的UI去做展示。對於版本迭代中頻繁的UI改動,更新或新增一套View即可。如果想在UI上做A/B Testing,那MVVM是你不二選擇。

單元測試

有些同學一看到單元測試,可能腦袋都大。是啊,寫成一團漿糊的程式碼怎麼可能做單元測試?如果你們以程式碼太爛無法寫單元測試而逃避,那可真是不好的訊息了。這時候,你需要MVVM來拯救。

我們前面說過了,ViewModel層做的事是資料處理和業務邏輯,View層中關注的是UI,兩者完全沒有依賴。不管是UI的單元測試還是業務邏輯的單元測試,都是低耦合的。在MVVM中資料是直接繫結到UI控制元件上的(部分資料是可以直接反映出UI上的內容),那麼我們就可以直接通過修改繫結的資料來源來間接做一些Android UI上的測試。

通過上面的簡述以及模式的對比,我們可以發現MVVM的優勢還是非常明顯的。雖然目前Android開發中可能真正在使用MVVM的很少,但是值得我們去做一些探討和調研。

如何構建MVVM應用框架

如何分工

構建MVVM框架首先要具體瞭解各個模組的分工。接下來我們來講解View、ViewModel、Model它們各自的職責所在。

View

View層做的就是和UI相關的工作,我們只在XML、Activity和Fragment寫View層的程式碼,View層不做和業務相關的事,也就是我們在Activity不寫業務邏輯和業務資料相關的程式碼,更新UI通過資料繫結實現,儘量在ViewModel裡面做(更新繫結的資料來源即可),Activity要做的事就是初始化一些控制元件(如控制元件的顏色,新增RecyclerView的分割線),View層可以提供更新UI的介面(但是我們更傾向所有的UI元素都是通過資料來驅動更改UI),View層可以處理事件(但是我們更希望UI事件通過Command來繫結)。簡單地說:View層不做任何業務邏輯、不涉及運算元據、不處理資料,UI和資料嚴格的分開。

ViewModel

ViewModel層做的事情剛好和View層相反,ViewModel只做和業務邏輯和業務資料相關的事,不做任何和UI相關的事情,ViewModel 層不會持有任何控制元件的引用,更不會在ViewModel中通過UI控制元件的引用去做更新UI的事情。ViewModel就是專注於業務的邏輯處理,做的事情也都只是對資料的操作(這些資料繫結在相應的控制元件上會自動去更改UI)。同時DataBinding框架已經支援雙向繫結,讓我們可以通過雙向繫結獲取View層反饋給ViewModel層的資料,並對這些資料上進行操作。關於對UI控制元件事件的處理,我們也希望能把這些事件處理繫結到控制元件上,並把這些事件的處理統一化,為此我們通過BindingAdapter對一些常用的事件做了封裝,把一個個事件封裝成一個個Command,對於每個事件我們用一個ReplyCommand去處理就行了,ReplyCommand會把你可能需要的資料帶給你,這使得我們在ViewModel層處理事件的時候只需要關心處理資料就行了,具體見MVVM Light Toolkit 使用指南的 Command 部分。再強調一遍:ViewModel 不做和UI相關的事。

Model

Model層最大的特點是被賦予了資料獲取的職責,與我們平常Model層只定義實體物件的行為截然不同。例項中,資料的獲取、儲存、資料狀態變化都是Model層的任務。Model包括實體模型(Bean)、Retrofit的Service ,獲取網路資料介面,本地儲存(增刪改查)介面,資料變化監聽等。Model提供資料獲取介面供ViewModel呼叫,經資料轉換和操作並最終對映繫結到View層某個UI元素的屬性上。

如何協作

關於協作,我們先來看下面的一張圖:

上圖反映了MVVM框架中各個模組的聯絡和資料流的走向,我們從每個模組一一拆分來看。那麼我們重點就是下面的三個協作。

  • ViewModel與View的協作。
  • ViewModel與Model的協作。
  • ViewModel與ViewModel的協作。

ViewModel與View的協作

圖2中ViewModel和View是通過繫結的方式連線在一起的,繫結分成兩種:一種是資料繫結,一種是命令繫結。資料的繫結DataBinding已經提供好了,簡單地定義一些ObservableField就能把資料和控制元件繫結在一起了(如TextView的text屬性),但是DataBinding框架提供的不夠全面,比如說如何讓一個URL繫結到一個ImageView,讓這個ImageView能自動去載入url指定的圖片,如何把資料來源和佈局模板繫結到一個ListView,讓ListView可以不需要去寫Adapter和ViewHolder相關的東西?這些就需要我們做一些工作和簡單的封裝。MVVM Light Toolkit 已經幫我們做了一部分的工作,詳情可以檢視MVVM Light Toolkit 使用指南。關於事件繫結也是一樣,MVVM Light Toolkit 做了簡單的封裝,對於每個事件我們用一個ReplyCommand去處理就行了,ReplyCommand會把可能需要的資料帶給你,這樣我們處理事件的時候也只關心處理資料就行了。

由圖1中ViewModel的模組中我們可以看出ViewModel類下面一般包含下面5個部分:

  • Context (上下文)
  • Model (資料來源 Java Bean)
  • Data Field (資料繫結)
  • Command (命令繫結)
  • Child ViewModel (子ViewModel)

我們先來看下示例程式碼,然後再一一講解5個部分是幹嘛用的:

//contextprivate Activity context;//model(資料來源 Java Bean)private NewsService.News news;private TopNewsService.News topNews;//資料繫結,繫結到UI的欄位(data field)public final ObservableField<String> imageUrl = new ObservableField<>();public final ObservableField<String> html = new ObservableField<>();public final ObservableField<String> title = new ObservableField<>();// 一個變數包含了所有關於View Style 相關的欄位public final ViewStyle viewStyle = new ViewStyle();//命令繫結(command)public final ReplyCommand onRefreshCommand = new ReplyCommand<>(() -> {     

})public final ReplyCommand<Integer> onLoadMoreCommand = new ReplyCommand<>((itemCount) -> {  

});//Child ViewModelpublic final ObservableList<NewItemViewModel> itemViewModel = new ObservableArrayList<>();/** * ViewStyle 關於控制元件的一些屬性和業務資料無關的Style 可以做一個包裹,這樣程式碼比較美觀, 
ViewModel 頁面也不會有太多太雜的欄位。 **/public static class ViewStyle {     
   public final ObservableBoolean isRefreshing = new ObservableBoolean(true);     
   public final ObservableBoolean progressRefreshing = new ObservableBoolean(true); 
}

Context

Context是幹嘛用的呢,為什麼每個ViewModel都最好需要持了一個Context的引用呢?ViewModel不處理和UI相關的事也不操作控制元件,更不更新UI,那為什麼要有Context呢?原因主要有以下兩點:

  1. 通過圖1中,然後得到一個Observable,其實這就是網路請求部分。其實這就是網路請求部分,做網路請求我們必須把Retrofit Service返回的Observable繫結到Context的生命週期上,防止在請求回來時Activity已經銷燬等異常,其實這個Context的目的就是把網路請求繫結到當前頁面的生命週期中。
  2. 在圖1中,我們可以看到兩個ViewModel之間的聯絡是通過Messenger來做,這個Messenger是需要用到Context,這個我們後續會講解。

當然,除此以外,呼叫工具類、幫助類有時候需要Context做為引數等也是原因之一。

Model (資料來源)

Model是什麼呢?其實就是資料來源,可以簡單理解是我們用JSON轉過來的Bean。ViewModel要把資料對映到UI中可能需要大量對Model的資料拷貝和操作,拿Model的欄位去生成對應的ObservableField然後繫結到UI(我們不會直接拿Model的資料去做繫結展示),這裡是有必要在一個ViewModel保留原始的Model引用,這對於我們是非常有用的,因為可能使用者的某些操作和輸入需要我們去改變資料來源,可能我們需要把一個Bean在列表頁點選後傳給詳情頁,可能我們需要把這個Model當做表單提交到伺服器。這些都需要我們的ViewModel持有相應的Model(資料來源)。

Data Field(資料繫結)

Data Field就是需要繫結到控制元件上的ObservableField欄位,這是ViewModel的必需品,這個沒有什麼好說。但是這邊有一個建議:

這些欄位是可以稍微做一下分類和包裹的。比如說可能一些欄位是繫結到控制元件的一些Style屬性上(如長度、顏色、大小),對於這類針對View Style的的欄位可以宣告一個ViewStyle類包裹起來,這樣整個程式碼邏輯會更清晰一些,不然ViewModel裡面可能欄位氾濫,不易管理和閱讀性較差。而對於其他一些欄位,比如說title、imageUrl、name這些屬於資料來源型別的欄位,這些欄位也叫資料欄位,是和業務資料和邏輯息息相關的,這些欄位可以放在一塊。

Command(命令繫結)

Command(命令繫結)簡言之就是對事件的處理(下拉重新整理、載入更多、點選、滑動等事件處理)。我們之前處理事件是拿到UI控制元件的引用,然後設定Listener,這些Listener其實就是Command。但是考慮到在一個ViewModel寫各種Listener並不美觀,可能實現一個Listener就需要實現多個方法,但是我們可能只想要其中一個有用的方法實現就好了。更重要一點是實現一個Listener可能需要寫一些UI邏輯才能最終獲取我們想要的。簡單舉個例子,比如你想要監聽ListView滑到最底部然後觸發載入更多的事件,這時候就要在ViewModel裡面寫一個OnScrollListener,然後在裡面的onScroll方法中做計算,計算什麼時候ListView滑動底部了。其實ViewModel的工作並不想去處理這些事件,它專注做的應該是業務邏輯和資料處理,如果有一個東西不需要你自己去計算是否滑到底部,而是在滑動底部自動觸發一個Command,同時把當前列表的總共的item數量返回給你,方便你通過 page=itemCount/LIMIT+1去計算出應該請求伺服器哪一頁的資料那該多好啊。MVVM Light Toolkit 幫你實現了這一點:

public final ReplyCommand<Integer> onLoadMoreCommand =  new ReplyCommand<>((itemCount) -> {  
  int page=itemCount/LIMIT+1;  
  loadData(page.LIMIT) 
);

接著在XML佈局檔案中通過bind:onLoadMoreCommand繫結上去就行了。

<android.support.v7.widget.RecyclerView 
android:layout_width="match_parent" android:layout_height="match_parent" bind:onLoadMoreCommand="@{viewModel.loadMoreCommand}"/>

具體想了解更多請檢視 MVVM Light Toolkit 使用指南,裡面有比較詳細地講解Command的使用。當然Command並不是必須的,你完全可以依照自己的習慣和喜好在ViewModel寫Listener,不過使用Command可以使ViewModel更簡潔易讀。你也可以自己定義更多的、其他功能的Command,那麼ViewModel的事件處理都是託管ReplyCommand來處理,這樣的程式碼看起來會比較美觀和清晰。Command只是對UI事件的一層隔離UI層的封裝,在事件觸發時把ViewModel層可能需要的資料傳給ViewModel層,對事件的處理做了統一化,是否使用的話,還是看你個人喜好了。

Child ViewModel(子ViewModel)

子ViewModel的概念就是在ViewModel裡面巢狀其他的ViewModel,這種場景還是很常見的。比如說你一個Activity裡面有兩個Fragment,ViewModel是以業務劃分的,兩個Fragment做的業務不一樣,自然是由兩個ViewModel來處理,這時候Activity對應的ViewModel裡面可能包含了兩個Fragment各自的ViewModel,這就是巢狀的子ViewModel。還有另外一種就是對於AdapterView,如ListView RecyclerView、ViewPager等。

//Child ViewModelpublic final  
   ObservableList<ItemViewModel> itemViewModel = new ObservableArrayList<>();

它們的每個Item其實就對應於一個ViewModel,然後在當前的ViewModel通過ObservableList持有引用(如上述程式碼),這也是很常見的巢狀的子ViewModel。我們其實還建議,如果一個頁面業務非常複雜,不要把所有邏輯都寫在一個ViewModel,可以把頁面做業務劃分,把不同的業務放到不同的ViewModel,然後整合到一個總的ViewModel,這樣做起來可以使我們的程式碼業務清晰、簡短意賅,也方便後人的維護。

總的來說,ViewModel和View之前僅僅只有繫結的關係,View層需要的屬性和事件處理都是在XML裡面繫結好了,ViewModel層不會去操作UI,只是根據業務要求處理資料,這些資料自動對映到View層控制元件的屬性上。

關於ViewModel類中包含哪些模組和欄位,這個需要開發者自己去衡量,我們建議ViewModel不要引入太多的成員變數,成員變數最好只有上面的提到的5種(context、model……),能不引入其他型別的變數就儘量不要引進來,太多的成員變數對於整個程式碼結構破壞很大,後面維護的人要時刻關心成員變數什麼時候被初始化、什麼時候被清掉、什麼時候被賦值或者改變,一個細節不小心可能就出現潛在的Bug。太多不清晰定義的成員變數又沒有註釋的程式碼是很難維護的。

另外,我們會把UI控制元件的屬性和事件都通過XML(如bind:text=@{…})繫結。如果一個業務邏輯要彈一個Dialog,但是你又不想在ViewModel裡面做彈窗的事(ViewModel不希望做UI相關的事)或者說改變ActionBar上面的圖示的顏色,改變ActionBar按鈕是否可點選,這些都不是寫在XML裡面(都是用Java程式碼初始化的),如何對這些控制元件的屬性做繫結呢?我們先來看下程式碼:

public class MainViewModel implements ViewModel { 
....//true的時候彈出Dialog,false的時候關掉dialogpublic final ObservableBoolean isShowDialog = new ObservableBoolean(); 
.... 
..... 
}// 在View層做一個對isShowDialog改變的監聽public class MainActivity extends RxBasePmsActivity {private MainViewModel mainViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) { 
.....  
mainViewModel.isShowDialog.addOnPropertyChangedCallback(new android.databinding.Observable.OnPropertyChangedCallback() {      @Override 
      public void onPropertyChanged(android.databinding.Observable sender, int propertyId) {          if (mainViewModel.isShowDialog.get()) { 
               dialog.show(); 
          } else { 
               dialog.dismiss(); 
          } 
       } 
    }); 
 } 
 ... 
}

簡單地說你可以對任意的ObservableField做監聽,然後根據資料的變化做相應UI的改變,業務層ViewModel只要根據業務處理資料就行,以資料來驅動UI。

ViewModel與Model的協作

從圖1中,ViewModel通過傳引數到Model層獲取網路資料(資料庫同理),然後把Model的部分資料對映到ViewModel的一些欄位(ObservableField),並在ViewModel保留這個Model的引用,我們來看下這一塊的大致程式碼(程式碼涉及簡單的RxJava,如看不懂可以查閱入門一下):

//Model 
 private NewsDetail newsDetail; private void loadData(long id) {   
   //  Observable<Bean> 用來獲取網路資料 
   Observable<Notification<NewsDetailService.NewsDetail>>   newsDetailOb =    
   RetrofitProvider.getInstance() 
                  .create(NewsDetailService.class)    
                  .getNewsDetail(id)                    
                  .subscribeOn(Schedulers.io())       
                  .observeOn(AndroidSchedulers.mainThread())                 // 將網路請求繫結到Activity 的生命週期 
                  .compose(((ActivityLifecycleProvider) context).bindToLifecycle())  
                 //變成 Notification<Bean> 使我們更方便處理資料和錯誤 
                  .materialize().share();   // 處理返回的資料 
   newsDetailOb.filter(Notification::isOnNext)          
               .map(n -> n.getValue())     
               // 給成員變數newsDetail 賦值,之前提到的5種變數型別中的一種(model型別)         
               .doOnNext(m -> newsDetail = m)    
               .subscribe(m -> initViewModelField(m)); // 網路請求錯誤處理 
    NewsListHelper.dealWithResponseError( 
      newsDetailOb.filter(Notification::isOnError)         
                  .map(n -> n.getThrowable())); 
}//Model -->ViewModelprivate void initViewModelField(NewsDetail newsDetail) {   
     viewStyle.isRefreshing.set(false);    
     imageUrl.set(newsDetail.getImage());     
     Observable.just(newsDetail.getBody()) 
            .map(s -> s + "<style type=\"text/css\">" + newsDetail.getCssStr())            
            .map(s -> s + "</style>")             
            .subscribe(s -> html.set(s));    
     title.set(newsDetail.getTitle()); 
 }

注1:我們推薦MVVM和RxJava一塊兒使用,雖然兩者皆有觀察者模式的概念,但是RxJava不使用在針對View的監聽,更多是業務資料流的轉換和處理。DataBinding框架其實是專用於View-ViewModel的動態繫結的,它使得我們的ViewModel只需要關注資料,而RxJava提供的強大資料流轉換函式剛好可以用來處理ViewModel中的種種資料,得到很好的用武之地,同時加上Lambda表示式結合的鏈式程式設計,使ViewModel的程式碼非常簡潔同時易讀易懂。

注2:因為本文樣例Model層只涉及到網路資料的獲取,並沒有資料庫、儲存、資料狀態變化等其他業務,所以本文涉及的原始碼並沒有單獨把Model層抽出來,我們是建議把Model層單獨抽出來放一個類中,然後以面向介面程式設計方式提供外界獲取和儲存資料的介面。

ViewModel與ViewModel的協作

在圖1中我們看到兩個ViewModel之間用一條虛線連線著,中間寫著Messenger。Messenger可以理解是一個全域性訊息通道,引入Messenger最主要的目的是實現ViewModel和ViewModel的通訊,雖然也可以用於View和ViewModel的通訊,但並不推薦。ViewModel主要是用來處理業務和資料的,每個ViewModel都有相應的業務職責,但是在業務複雜的情況下,可能存在交叉業務,這時候就需要ViewModel和ViewModel交換資料和通訊,這時候一個全域性的訊息通道就很重要的。

關於Messenger的詳細使用方法可以參照 MVVM Light Toolkit 使用指南的 Messenger 部分。這裡給出一個簡單的例子僅供參考:場景是這樣的,你的MainActivity對應一個MainViewModel,MainActivity 裡面除了自己的內容還包含一個Fragment,這個Fragment 的業務處理對應於一個FragmentViewModel,FragmentViewModel請求伺服器並獲取資料。剛好這個資料MainViewModel也需要用到,我們不可能在MainViewModel重新請求資料,這樣不太合理,這時候就需要把資料傳給MainViewModel,那應該怎麼傳呢,如果彼此沒有引用或者回撥?那麼只能通過全域性的訊息通道Messenger。

FragmentViewModel獲取訊息後通知MainViewModel並把資料傳給它:

combineRequestOb.filter(Notification::isOnNext) 

.map(n -> n.getValue()) 

.map(p -> p.first) 

.filter(m -> !m.getTop_stories().isEmpty()) 

.doOnNext(m ->Observable.just(NewsListHelper.isTomorrow(date)).filter(b -> b).subscribe(b -> itemViewModel.clear()))
// 上面的程式碼可以不看,就是獲取網路資料 ,通過send把資料傳過去.subscribe(m -> Messenger.getDefault().send(m, TOKEN_TOP_NEWS_FINISH));

MainViewModel接收訊息並處理:

Messenger.getDefault().register(activity, NewsViewModel.TOKEN_TOP_NEWS_FINISH, TopNewsService.News.class, (news) -> {// to something....}

在MainActivity onDestroy取消註冊就行了(不然導致記憶體洩露):

@Override  

protected void onDestroy() {  

super.onDestroy();  

Messenger.getDefault().unregister(this);  

}

上面的例子只是簡單地說明,Messenger可以用在很多場景,通知、廣播都可以,不一定要傳資料,在一定條件下也可以用在View層和ViewModel上的通訊和廣播,運用範圍特別廣,需要開發者結合實際的業務中去做更深層次的挖掘。

總結和原始碼

本文主要講解了一些個人開發過程中總結的Android MVVM構建思想,更多是理論上各個模組如何分工、程式碼如何設計。雖然現在業界使用Android MVVM模式開發還比較少,但是隨著DataBinding 1.0的釋出,相信在Android MVVM 這一領域會更多的人來嘗試。剛好我最近用MVVM開發了一段時間,有點心得,寫出來僅供參考。

本文和原始碼都沒有涉及到單元測試,如果需要寫單元測試,可以結合Google開源的MVP框架新增Contract類實現面向介面程式設計,可以幫助你更好地編寫單測。同時MVP和MVVM並沒孰好孰壞,適合業務、適合自己的才是最有價值的,建議結合Google開源的MVP框架和本文介紹的MVVM相關的知識去探索適合自己業務發展的框架。

MVVM Light Toolkit只是一個工具庫,主要目的是更快捷方便地構建Android MVVM應用程式,在裡面新增了一些控制元件額外屬性和做了一些事件的封裝,同時引進了全域性訊息通道Messenger,個人覺得用起來會比較方便,你也可以嘗試一下。當然這個庫還有不少地方需要完善和優化,後續也會持續做更新和優化,如果不能達到你的業務需求時,可以clone下來自己做一些相關的擴充套件。如果想更深入瞭解MVVM Light Toolkit,請看我這篇博文 《MVVM Light Toolkit 使用指南》。

專案的原始碼地址 https://github.com/Kelin-Hong/MVVMLight 。其中:

library是MVVM Light Toolkit的原始碼,原始碼很簡單,感興趣的同學可以看看,沒什麼技術難度,可以根據自己的需求,新增更多的控制元件屬性和事件繫結。

sample是一個實現知乎日報首頁樣式的Demo,本文的程式碼示例均出自這個Demo。程式碼包含了一大部分MVVM Light Toolkit的使用場景(Data、Command、Messenger均有涉及),同時sample嚴格按照本博文闡述的MVVM設計思想開發,對理解本文會有比較大的幫助。

本文和原始碼涉及RxJava+Retrofit+Lambda如有不懂或沒接觸過,花點時間入門一下,用到的都是比較簡單的東西。

相關文章