精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發(fā) 架構
說到Android MVVM,相信大家都會想到Google 2015年推出的DataBinding框架。然而兩者的概念是不一樣的,不能混為一談。MVVM是一種架構模式,而DataBinding是一個實現(xiàn)數(shù)據(jù)和UI綁定的框架,是構建MVVM模式的一個工具。

概述

說到Android MVVM,相信大家都會想到Google 2015年推出的DataBinding框架。然而兩者的概念是不一樣的,不能混為一談。MVVM是一種架構模式,而DataBinding是一個實現(xiàn)數(shù)據(jù)和UI綁定的框架,是構建MVVM模式的一個工具。

之前看過很多關于Android MVVM的博客,但大多數(shù)提到的都是DataBinding的基本用法,很少有文章仔細講解在Android中是如何通過DataBinding去構建MVVM的應用框架的。View、ViewModel、Model每一層的職責如何?它們之間聯(lián)系怎樣、分工如何、代碼應該如何設計?這是我寫這篇文章的初衷。

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

MVC、MVP、MVVM

首先,我們先大致了解下Android開發(fā)中常見的模式。

MVC

View:XML布局文件。

Model:實體模型(數(shù)據(jù)的獲取、存儲、數(shù)據(jù)狀態(tài)變化)。

Controllor:對應于Activity,處理數(shù)據(jù)、業(yè)務和UI。

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

MVP

View: 對應于Activity和XML,負責View的繪制以及與用戶的交互。

Model: 依然是實體模型。

Presenter: 負責完成View與Model間的交互和業(yè)務邏輯。

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

但MVP也存在一些弊端:

  • Presenter(以下簡稱P)層與View(以下簡稱V)層是通過接口進行交互的,接口粒度不好控制。粒度太小,就會存在大量接口的情況,使代碼太過碎版化;粒度太大,解耦效果不好。同時對于UI的輸入和數(shù)據(jù)的變化,需要手動調用V層或者P層相關的接口,相對來說缺乏自動性、監(jiān)聽性。如果數(shù)據(jù)的變化能自動響應到UI、UI的輸入能自動更新到數(shù)據(jù),那該多好!
  • MVP是以UI為驅動的模型,更新UI都需要保證能獲取到控件的引用,同時更新UI的時候要考慮當前是否是UI線程,也要考慮Activity的生命周期(是否已經(jīng)銷毀等)。
  • MVP是以UI和事件為驅動的傳統(tǒng)模型,數(shù)據(jù)都是被動地通過UI控件做展示,但是由于數(shù)據(jù)的時變性,我們更希望數(shù)據(jù)能轉被動為主動,希望數(shù)據(jù)能更有活性,由數(shù)據(jù)來驅動UI。
  • V層與P層還是有一定的耦合度。一旦V層某個UI元素更改,那么對應的接口就必須得改,數(shù)據(jù)如何映射到UI上、事件監(jiān)聽接口這些都需要轉變,牽一發(fā)而動全身。如果這一層也能解耦就更好了。
  • 復雜的業(yè)務同時也可能會導致P層太大,代碼臃腫的問題依然不能解決。

MVVM

View: 對應于Activity和XML,負責View的繪制以及與用戶交互。

Model: 實體模型。

ViewModel: 負責完成View與Model間的交互,負責業(yè)務邏輯。

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

數(shù)據(jù)驅動

在常規(guī)的開發(fā)模式中,數(shù)據(jù)變化需要更新UI的時候,需要先獲取UI控件的引用,然后再更新UI。獲取用戶的輸入和操作也需要通過UI控件的引用。在MVVM中,這些都是通過數(shù)據(jù)驅動來自動完成的,數(shù)據(jù)變化后會自動更新UI,UI的改變也能自動反饋到數(shù)據(jù)層,數(shù)據(jù)成為主導因素。這樣MVVM層在業(yè)務邏輯處理中只要關心數(shù)據(jù),不需要直接和UI打交道,在業(yè)務處理過程中簡單方便很多。

低耦合度

MVVM模式中,數(shù)據(jù)是獨立于UI的。

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

更新UI

在MVVM中,數(shù)據(jù)發(fā)生變化后,我們在工作線程直接修改(在數(shù)據(jù)是線程安全的情況下)ViewModel的數(shù)據(jù)即可,不用再考慮要切到主線程更新UI了,這些事情相關框架都幫我們做了。

團隊協(xié)作

MVVM的分工是非常明顯的,由于View和ViewModel之間是松散耦合的:一個是處理業(yè)務和數(shù)據(jù)、一個是專門的UI處理。所以,完全由兩個人分工來做,一個做UI(XML和Activity)一個寫ViewModel,效率更高。

可復用性

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

單元測試

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

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

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

如何構建MVVM應用框架

如何分工

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

View

View層做的就是和UI相關的工作,我們只在XML、Activity和Fragment寫View層的代碼,View層不做和業(yè)務相關的事,也就是我們在Activity不寫業(yè)務邏輯和業(yè)務數(shù)據(jù)相關的代碼,更新UI通過數(shù)據(jù)綁定實現(xiàn),盡量在ViewModel里面做(更新綁定的數(shù)據(jù)源即可),Activity要做的事就是初始化一些控件(如控件的顏色,添加RecyclerView的分割線),View層可以提供更新UI的接口(但是我們更傾向所有的UI元素都是通過數(shù)據(jù)來驅動更改UI),View層可以處理事件(但是我們更希望UI事件通過Command來綁定)。簡單地說:View層不做任何業(yè)務邏輯、不涉及操作數(shù)據(jù)、不處理數(shù)據(jù),UI和數(shù)據(jù)嚴格的分開。

ViewModel

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

Model

Model層最大的特點是被賦予了數(shù)據(jù)獲取的職責,與我們平常Model層只定義實體對象的行為截然不同。實例中,數(shù)據(jù)的獲取、存儲、數(shù)據(jù)狀態(tài)變化都是Model層的任務。Model包括實體模型(Bean)、Retrofit的Service ,獲取網(wǎng)絡數(shù)據(jù)接口,本地存儲(增刪改查)接口,數(shù)據(jù)變化監(jiān)聽等。Model提供數(shù)據(jù)獲取接口供ViewModel調用,經(jīng)數(shù)據(jù)轉換和操作并最終映射綁定到View層某個UI元素的屬性上。

如何協(xié)作

關于協(xié)作,我們先來看下面的一張圖:

上圖反映了MVVM框架中各個模塊的聯(lián)系和數(shù)據(jù)流的走向,我們從每個模塊一一拆分來看。那么我們重點就是下面的三個協(xié)作。

  • ViewModel與View的協(xié)作。
  • ViewModel與Model的協(xié)作。
  • ViewModel與ViewModel的協(xié)作。

ViewModel與View的協(xié)作

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

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

  • Context (上下文)
  • Model (數(shù)據(jù)源 Java Bean)
  • Data Field (數(shù)據(jù)綁定)
  • Command (命令綁定)
  • Child ViewModel (子ViewModel)

我們先來看下示例代碼,然后再一一講解5個部分是干嘛用的:

//contextprivate Activity context;//model(數(shù)據(jù)源 Java Bean)private NewsService.News news;private TopNewsService.News topNews;//數(shù)據(jù)綁定,綁定到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 關于控件的一些屬性和業(yè)務數(shù)據(jù)無關的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,其實這就是網(wǎng)絡請求部分。其實這就是網(wǎng)絡請求部分,做網(wǎng)絡請求我們必須把Retrofit Service返回的Observable綁定到Context的生命周期上,防止在請求回來時Activity已經(jīng)銷毀等異常,其實這個Context的目的就是把網(wǎng)絡請求綁定到當前頁面的生命周期中。
  2. 在圖1中,我們可以看到兩個ViewModel之間的聯(lián)系是通過Messenger來做,這個Messenger是需要用到Context,這個我們后續(xù)會講解。

當然,除此以外,調用工具類、幫助類有時候需要Context做為參數(shù)等也是原因之一。

Model (數(shù)據(jù)源)

Model是什么呢?其實就是數(shù)據(jù)源,可以簡單理解是我們用JSON轉過來的Bean。ViewModel要把數(shù)據(jù)映射到UI中可能需要大量對Model的數(shù)據(jù)拷貝和操作,拿Model的字段去生成對應的ObservableField然后綁定到UI(我們不會直接拿Model的數(shù)據(jù)去做綁定展示),這里是有必要在一個ViewModel保留原始的Model引用,這對于我們是非常有用的,因為可能用戶的某些操作和輸入需要我們去改變數(shù)據(jù)源,可能我們需要把一個Bean在列表頁點擊后傳給詳情頁,可能我們需要把這個Model當做表單提交到服務器。這些都需要我們的ViewModel持有相應的Model(數(shù)據(jù)源)。

Data Field(數(shù)據(jù)綁定)

Data Field就是需要綁定到控件上的ObservableField字段,這是ViewModel的必需品,這個沒有什么好說。但是這邊有一個建議:

這些字段是可以稍微做一下分類和包裹的。比如說可能一些字段是綁定到控件的一些Style屬性上(如長度、顏色、大小),對于這類針對View Style的的字段可以聲明一個ViewStyle類包裹起來,這樣整個代碼邏輯會更清晰一些,不然ViewModel里面可能字段泛濫,不易管理和閱讀性較差。而對于其他一些字段,比如說title、imageUrl、name這些屬于數(shù)據(jù)源類型的字段,這些字段也叫數(shù)據(jù)字段,是和業(yè)務數(shù)據(jù)和邏輯息息相關的,這些字段可以放在一塊。

Command(命令綁定)

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

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層的封裝,在事件觸發(fā)時把ViewModel層可能需要的數(shù)據(jù)傳給ViewModel層,對事件的處理做了統(tǒng)一化,是否使用的話,還是看你個人喜好了。

Child ViewModel(子ViewModel)

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

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

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

總的來說,ViewModel和View之前僅僅只有綁定的關系,View層需要的屬性和事件處理都是在XML里面綁定好了,ViewModel層不會去操作UI,只是根據(jù)業(yè)務要求處理數(shù)據(jù),這些數(shù)據(jù)自動映射到View層控件的屬性上。

關于ViewModel類中包含哪些模塊和字段,這個需要開發(fā)者自己去衡量,我們建議ViewModel不要引入太多的成員變量,成員變量最好只有上面的提到的5種(context、model……),能不引入其他類型的變量就盡量不要引進來,太多的成員變量對于整個代碼結構破壞很大,后面維護的人要時刻關心成員變量什么時候被初始化、什么時候被清掉、什么時候被賦值或者改變,一個細節(jié)不小心可能就出現(xiàn)潛在的Bug。太多不清晰定義的成員變量又沒有注釋的代碼是很難維護的。

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

public class MainViewModel implements ViewModel { 
....//true的時候彈出Dialog,false的時候關掉dialogpublic final ObservableBoolean isShowDialog = new ObservableBoolean(); 
.... 
..... 
}// 在View層做一個對isShowDialog改變的監(jiān)聽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做監(jiān)聽,然后根據(jù)數(shù)據(jù)的變化做相應UI的改變,業(yè)務層ViewModel只要根據(jù)業(yè)務處理數(shù)據(jù)就行,以數(shù)據(jù)來驅動UI。

ViewModel與Model的協(xié)作

從圖1中,ViewModel通過傳參數(shù)到Model層獲取網(wǎng)絡數(shù)據(jù)(數(shù)據(jù)庫同理),然后把Model的部分數(shù)據(jù)映射到ViewModel的一些字段(ObservableField),并在ViewModel保留這個Model的引用,我們來看下這一塊的大致代碼(代碼涉及簡單的RxJava,如看不懂可以查閱入門一下):

//Model 
 private NewsDetail newsDetail; private void loadData(long id) {   
   //  Observable<Bean> 用來獲取網(wǎng)絡數(shù)據(jù) 
   Observable<Notification<NewsDetailService.NewsDetail>>   newsDetailOb =    
   RetrofitProvider.getInstance() 
                  .create(NewsDetailService.class)    
                  .getNewsDetail(id)                    
                  .subscribeOn(Schedulers.io())       
                  .observeOn(AndroidSchedulers.mainThread())                 // 將網(wǎng)絡請求綁定到Activity 的生命周期 
                  .compose(((ActivityLifecycleProvider) context).bindToLifecycle())  
                 //變成 Notification<Bean> 使我們更方便處理數(shù)據(jù)和錯誤 
                  .materialize().share();   // 處理返回的數(shù)據(jù) 
   newsDetailOb.filter(Notification::isOnNext)          
               .map(n -> n.getValue())     
               // 給成員變量newsDetail 賦值,之前提到的5種變量類型中的一種(model類型)         
               .doOnNext(m -> newsDetail = m)    
               .subscribe(m -> initViewModelField(m)); // 網(wǎng)絡請求錯誤處理 
    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的監(jiān)聽,更多是業(yè)務數(shù)據(jù)流的轉換和處理。DataBinding框架其實是專用于View-ViewModel的動態(tài)綁定的,它使得我們的ViewModel只需要關注數(shù)據(jù),而RxJava提供的強大數(shù)據(jù)流轉換函數(shù)剛好可以用來處理ViewModel中的種種數(shù)據(jù),得到很好的用武之地,同時加上Lambda表達式結合的鏈式編程,使ViewModel的代碼非常簡潔同時易讀易懂。

注2:因為本文樣例Model層只涉及到網(wǎng)絡數(shù)據(jù)的獲取,并沒有數(shù)據(jù)庫、存儲、數(shù)據(jù)狀態(tài)變化等其他業(yè)務,所以本文涉及的源碼并沒有單獨把Model層抽出來,我們是建議把Model層單獨抽出來放一個類中,然后以面向接口編程方式提供外界獲取和存儲數(shù)據(jù)的接口。

ViewModel與ViewModel的協(xié)作

在圖1中我們看到兩個ViewModel之間用一條虛線連接著,中間寫著Messenger。Messenger可以理解是一個全局消息通道,引入Messenger最主要的目的是實現(xiàn)ViewModel和ViewModel的通信,雖然也可以用于View和ViewModel的通信,但并不推薦。ViewModel主要是用來處理業(yè)務和數(shù)據(jù)的,每個ViewModel都有相應的業(yè)務職責,但是在業(yè)務復雜的情況下,可能存在交叉業(yè)務,這時候就需要ViewModel和ViewModel交換數(shù)據(jù)和通信,這時候一個全局的消息通道就很重要的。

關于Messenger的詳細使用方法可以參照 MVVM Light Toolkit 使用指南的 Messenger 部分。這里給出一個簡單的例子僅供參考:場景是這樣的,你的MainActivity對應一個MainViewModel,MainActivity 里面除了自己的內容還包含一個Fragment,這個Fragment 的業(yè)務處理對應于一個FragmentViewModel,F(xiàn)ragmentViewModel請求服務器并獲取數(shù)據(jù)。剛好這個數(shù)據(jù)MainViewModel也需要用到,我們不可能在MainViewModel重新請求數(shù)據(jù),這樣不太合理,這時候就需要把數(shù)據(jù)傳給MainViewModel,那應該怎么傳呢,如果彼此沒有引用或者回調?那么只能通過全局的消息通道Messenger。

FragmentViewModel獲取消息后通知MainViewModel并把數(shù)據(jù)傳給它:

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()))
// 上面的代碼可以不看,就是獲取網(wǎng)絡數(shù)據(jù) ,通過send把數(shù)據(jù)傳過去.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可以用在很多場景,通知、廣播都可以,不一定要傳數(shù)據(jù),在一定條件下也可以用在View層和ViewModel上的通信和廣播,運用范圍特別廣,需要開發(fā)者結合實際的業(yè)務中去做更深層次的挖掘。

總結和源碼

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

本文和源碼都沒有涉及到單元測試,如果需要寫單元測試,可以結合Google開源的MVP框架添加Contract類實現(xiàn)面向接口編程,可以幫助你更好地編寫單測。同時MVP和MVVM并沒孰好孰壞,適合業(yè)務、適合自己的才是最有價值的,建議結合Google開源的MVP框架和本文介紹的MVVM相關的知識去探索適合自己業(yè)務發(fā)展的框架。

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

項目的源碼地址 https://github.com/Kelin-Hong/MVVMLight 。其中:

library是MVVM Light Toolkit的源碼,源碼很簡單,感興趣的同學可以看看,沒什么技術難度,可以根據(jù)自己的需求,添加更多的控件屬性和事件綁定。

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

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

責任編輯:張燕妮 來源: Android開發(fā)中文站
相關推薦

2017-02-24 10:02:04

AndroidMVVM應用框架

2024-04-28 10:22:08

.NETMVVM應用工具包

2017-03-02 11:10:39

AndroidMVVM應用程序

2016-11-30 17:28:02

移動開發(fā)iOSAndroid

2013-06-20 10:28:39

MVVM框架avalon架構

2023-09-05 23:29:49

前端Vue

2022-05-16 11:50:45

HDF驅動框架

2011-10-10 09:11:09

Java

2017-02-05 09:13:58

PHP Cake框架構建

2015-05-05 10:32:15

iOS-MVVM框架

2020-08-24 11:48:49

人工智能

2024-09-30 11:45:10

2011-09-07 13:18:40

Android Wid

2023-04-25 15:50:50

Flask框架Web

2009-12-24 14:30:19

WPF MVVM

2009-10-13 11:32:19

Winform假框架

2010-08-11 10:24:46

Flex開發(fā)

2010-10-08 09:03:51

2009-03-26 08:28:17

AndroidGoogle移動OS

2011-07-28 15:47:20

IOS 程序 測試
點贊
收藏

51CTO技術棧公眾號

精品91久久久| 亚洲一区二区在线免费看| 在线一区二区视频| 91情侣在线视频| 国产综合精品在线| 欧美激情网站| 欧美在线三区| 欧美日韩中文字幕一区| 欧美午夜精品久久久久免费视| 妺妺窝人体色www在线下载| 日韩国产一二三区| 欧美国产国产综合| 国产精品久久不能| 亚洲午夜久久久久久久久红桃| 欧美日韩经典丝袜| 艳女tv在线观看国产一区| 欧美视频你懂的| 美女黄色免费看| 亚洲成人黄色片| 欧美精品播放| 欧美变态凌虐bdsm| 精品无码国产一区二区三区av| 午夜精品久久久久久久91蜜桃| 午夜影院欧美| 欧美成人乱码一区二区三区| 欧美成人精品欧美一级乱| 欧美理论在线观看| 肉色丝袜一区二区| 最近中文字幕日韩精品| 亚洲精品视频三区| 最近中文字幕免费mv2018在线| 久99久精品视频免费观看| 精品国产一区久久久| 又黄又爽又色的视频| 成人短视频在线| 国产成人免费av在线| 欧美片一区二区三区| 欧美xxxxx精品| 韩日精品一区二区| 国产精品美女久久久久久久网站| 国产精品嫩草视频| av最新在线观看| 日韩免费一级| 福利视频一区二区| 亚洲精品欧洲精品| 99精品免费观看| 在线看片日韩| 在线观看日韩视频| 人妻激情偷乱视频一区二区三区| gratisvideos另类灌满| 久久久久综合网| 国产中文字幕亚洲| 日本天堂网在线观看| 中文精品一区二区| 3atv一区二区三区| 欧美 日韩 国产在线观看| 国产二区视频在线观看| 国产伦精品一区二区三区免费 | 午夜18视频在线观看| 亚洲一级在线| 精品国偷自产在线视频99| 欧美aaa级片| 国产另类在线| 欧美日韩成人综合在线一区二区| 菠萝蜜视频在线观看入口| 色播色播色播色播色播在线| 激情成人综合网| 97精品在线观看| 国产传媒免费在线观看| 香蕉人人精品| 欧美成人激情免费网| 国产成人av片| 欧美三级电影网址| 精品免费在线视频| 男女视频一区二区三区| 成入视频在线观看| 亚洲精品va在线观看| 日韩视频在线播放| 五月婷婷狠狠干| 久久综合网色—综合色88| 亚洲自拍中文字幕| 欧美成人一区二区视频| 一区二区三区国产盗摄| 美女av一区二区三区 | 中文字幕一区二区三区免费看 | 国产精品美女久久福利网站| 裸体裸乳免费看| 男男激情在线| 国产精品久久久久久一区二区三区| 丰满女人性猛交| 波多野结衣一区二区| 91亚洲精品久久久蜜桃网站 | 国产精品乱码一区二三区小蝌蚪| 异国色恋浪漫潭| 国产激情视频在线看| 欧美日韩亚洲丝袜制服| 国产情侣久久久久aⅴ免费| 国产精品4hu.www| 色偷偷久久人人79超碰人人澡| 国产精品免费看久久久无码| 一区二区精品伦理...| 性感美女久久精品| 和岳每晚弄的高潮嗷嗷叫视频| 顶级网黄在线播放| 欧美日韩一区二区三区| 五月激情婷婷在线| 欧美一级做a| 亚洲国产精品久久久久| 免费看污黄网站| 欧美黑人疯狂性受xxxxx野外| 五月天亚洲婷婷| 国产在线播放观看| 开心久久婷婷综合中文字幕| 亚洲黄页网在线观看| 亚洲AV成人无码精电影在线| 久久久久免费| 国产精品成人观看视频国产奇米| 国产偷拍一区二区| 亚洲国产精品成人久久综合一区 | 视频一区在线视频| 日本中文字幕久久看| 亚洲熟妇无码乱子av电影| 香蕉久久国产| 国产精品ⅴa在线观看h| 一区二区视频免费| 久久午夜国产精品| 久久久久久久久久网| 三级在线看中文字幕完整版| 欧美一级电影网站| 波多野结衣一二三区| 中文无码久久精品| 成人免费视频a| 亚洲成a人片77777精品| 亚洲日本在线看| 国内少妇毛片视频| 欧美视频二区欧美影视| 亚洲а∨天堂久久精品9966| 久久久无码人妻精品一区| 激情综合自拍| 国产精品一区二区三区观看| 欧美18xxxxx| 天天操天天干天天综合网| 动漫美女无遮挡免费| 国产精品a久久久久| 91pron在线| 任你弄在线视频免费观看| 日韩一区二区三区在线视频| 91精品国产闺蜜国产在线闺蜜| 久久国产综合精品| 国产日韩欧美亚洲一区| 国产视频第一页在线观看| 一本一道波多野结衣一区二区| 一区二区三区免费在线观看视频| 日韩免费高清| 国产69精品久久久久9999| 天天综合久久综合| 高清久久久久久| 日韩精品久久一区二区三区| 97成人资源| 亚洲视频专区在线| 免费人成视频在线| 国产·精品毛片| 无罩大乳的熟妇正在播放| 日韩影视高清在线观看| 国产99久久精品一区二区| 福利成人在线观看| 91精品国产欧美一区二区成人| 97人妻天天摸天天爽天天| 性欧美暴力猛交另类hd| 亚洲激情啪啪| 国产美女视频一区二区| 亚洲天堂av图片| 中文永久免费观看| 伊人开心综合网| 黄色片视频免费观看| 自由日本语亚洲人高潮| 96sao精品视频在线观看| 蜜乳av一区| 亚洲欧美中文字幕| 国产一区二区在线不卡| 久久精品人人做人人爽人人| 无码av天堂一区二区三区| 精品国产乱子伦一区二区| 欧美精品一二区| 婷婷国产在线| 亚洲一级二级三级在线免费观看| 日韩www视频| 免费看欧美女人艹b| 欧美一区国产一区| 狠狠躁少妇一区二区三区| 亚洲网站在线播放| 无码人妻黑人中文字幕| 99国产精品一区| 簧片在线免费看| 日韩视频在线一区二区三区 | 性直播体位视频在线观看| 欧美日韩精品欧美日韩精品一综合| 麻豆天美蜜桃91| 久久精品一区四区| 精品1卡二卡三卡四卡老狼| 午夜亚洲福利| 久久久久久久久久久久久久久久av| 国产精品国精产品一二| 精品成a人在线观看| 久久精品偷拍视频| 性欧美疯狂xxxxbbbb| 激情高潮到大叫狂喷水| 久久99精品久久久久久动态图| 日韩精品在线观看av| 日本精品三区| 国产欧美精品在线播放| 日本中文字幕在线2020| 在线不卡免费av| 青青草激情视频| 国产成人av在线影院| 91色国产在线| 99香蕉国产精品偷在线观看| 四虎永久免费网站| 成人情趣视频| 欧美专区一二三| 久久男人av| 国产女人水真多18毛片18精品| 婷婷久久免费视频| 国产精品主播视频| 精品欧美一区二区三区在线观看| 这里只有精品视频| 国产手机av在线| 欧美三级电影精品| 69视频免费看| 色综合天天性综合| 免费在线不卡视频| 亚洲国产精品激情在线观看| 亚洲天堂久久新| 日韩二区三区四区| 日本一本二本在线观看| 99精品网站| 国产精品 日韩| 向日葵视频成人app网址| 2019中文在线观看| 欧美天天影院| 亚洲国产97在线精品一区| 国产高清视频免费| 色综合久久综合网97色综合 | 97电影在线看视频| 日韩一区二区在线观看视频| 国产精品久久久久久久久毛片 | 成人羞羞国产免费网站| 久久av在线| 北条麻妃视频在线| 蜜臀久久久久久久| 向日葵污视频在线观看| 亚洲视频免费| 亚洲国产欧美不卡在线观看| 青青草成人影院| 伊人久久大香线蕉精品| 欧美交a欧美精品喷水| 国产精自产拍久久久久久| 成人做爰免费视频免费看| 欧美激情免费看| 亚洲xxxxxx| 日韩有码在线观看| 黄色片免费在线| 亚洲成人av在线| 凸凹人妻人人澡人人添| 国产视频精品免费播放| 99久久久国产精品无码网爆 | 国产精品69精品一区二区三区| 欧美色网一区| 成人h猎奇视频网站| 欧美2区3区4区| 精品在线不卡| 亚洲不卡在线| 精品高清视频| 国产永久精品大片wwwapp| 国产综合 伊人色| 在线日韩网站| 五月天色婷婷综合| 1024精品一区二区三区| 99精品一级欧美片免费播放| 国产一区久久| 国产成人亚洲精品无码h在线| 美女网站一区二区| 日本50路肥熟bbw| 欧美国产成人在线| 久草视频精品在线| 伊人婷婷欧美激情| 中文字幕亚洲乱码熟女1区2区| 欧美视频在线一区二区三区| 午夜精品免费观看| 欧美精品aⅴ在线视频| 成人午夜福利视频| 日韩欧美的一区| 99久久久国产精品无码免费| 亚洲精品国产欧美| 日本精品在线| 8x海外华人永久免费日韩内陆视频| 一色桃子av在线| 国产成人91久久精品| 91蜜桃臀久久一区二区| 成人18视频| 成人春色在线观看免费网站| 日韩精品福利视频| 亚洲成人直播| 加勒比av中文字幕| 久久婷婷国产综合精品青草| 麻豆视频在线观看| 欧美日韩综合在线免费观看| 天天干视频在线观看| 亚洲精品短视频| 黄色精品免费看| 久久99久久99精品免观看粉嫩 | 黄色在线播放网站| 日本一欧美一欧美一亚洲视频| 久久gogo国模啪啪裸体| 亚洲高清视频在线观看| 久久精品九九| 国产精品一区二区人妻喷水| 亚洲最新视频在线观看| 久久亚洲国产成人精品性色| 亚洲一级二级在线| 91精品国产色综合久久不8| 亚洲人a成www在线影院| 国产网站在线| 国模精品娜娜一二三区| 国产一区二区中文| 国模大尺度视频| 亚洲乱码日产精品bd| 日本少妇性生活| 欧美电视剧在线看免费| 久久99精品久久久久久野外| 国产精品美乳一区二区免费 | 国产aaa免费视频| 国产 日韩 欧美大片| 看片网站在线观看| 日韩视频国产视频| 超碰caoporn久久| 亚洲va久久久噜噜噜久久天堂| 97人人精品| 妞干网在线视频观看| 国产成人av一区二区三区在线 | 亚洲福利视频久久| а√天堂8资源中文在线| 国产一区二区高清不卡| 亚洲一级高清| 好吊色视频一区二区三区| 亚洲h精品动漫在线观看| 少妇喷水在线观看| 欧美一区二区.| 精品久久久久久久| 青青视频免费在线观看| 欧美综合二区| 91激情视频在线观看| 亚洲国产一区视频| 日韩一级免费视频| 在线精品国产欧美| 成人黄色免费网站| 少妇熟女一区二区| 成人蜜臀av电影| 国产午夜精品理论片在线| 欧美精品自拍偷拍动漫精品| 很黄的网站在线观看| 99精品国产高清一区二区| 亚洲日本黄色| 久久久久亚洲av成人无码电影| 欧美天堂一区二区三区| 黄av在线免费观看| 国产欧美一区二区三区另类精品| 国产日本精品| 国产视频不卡在线| 日韩欧美国产视频| 亚洲男女视频在线观看| 国内精品小视频在线观看| 蜜桃视频欧美| 男人添女人下面高潮视频| 国产亚洲精品精华液| 国产精品久久久久久久免费| 久久全球大尺度高清视频| 国产亚洲一卡2卡3卡4卡新区| av中文字幕网址| 亚洲成精国产精品女| 精品久久久中文字幕人妻| 91国内在线视频| 日韩av密桃| 五月天激情小说| 欧美老女人在线| 免费h视频在线观看| 中文字幕日韩一区二区三区| 不卡视频一二三| 国产午夜精品无码| 国产亚洲精品高潮| 91蝌蚪精品视频| 在线观看亚洲色图| 亚洲国产精品一区二区www在线| 国产日韩精品在线看| 成人在线观看91| 捆绑紧缚一区二区三区视频| 国产成人亚洲欧洲在线| 日韩视频第一页| 亚洲成人一品| www日本在线观看|