日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

Airbnb开源框架,真响应式架构——MvRx

發布時間:2024/5/14 HTML 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Airbnb开源框架,真响应式架构——MvRx 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.



今日科技快訊


2月21日凌晨,三星美國舊金山舉行Galaxy Unpacked 2019新品發布會。正式發布年度旗艦Galaxy S10系列手機以及折疊手機Galaxy Fold,還推出了首款5G手機。

Galaxy Fold此前曾在2018年11月開發者大會上亮相,但當時并沒宣布具體參數,今天這是它終于定妝出場。它的形態是兩折的,仿佛一個錢包,外表是一塊4.6英寸的狹長屏幕,打開后,內部是一塊7.3英寸的屏幕。三星Galaxy S10系列一共包含三款機型,分別是S10、S10+、S10e。相比去年的S9系列,S10系列外觀最大的變化在于采用Dynamic AMOLED屏幕設計,網友俗稱“挖孔屏”,其中S10為6.1英寸2K分辨率雙曲全視屏、S10+為6.4英寸2K分辨率雙曲全視屏,S10e由于定位相比前兩者更低,所以仍然使用1080P非雙曲全視屏。


作者簡介


明天就是周六啦,提前祝大家周末愉快!

本篇來自?珞澤珈群?的投稿文章,和大家分享了一個 Airbnb 的開源庫,響應式架構的MvRx,希望對大家有所幫助!

?珞澤珈群?的博客地址

https://www.jianshu.com/u/679d8deee5ae


前言


Airbnb 最近開源了一個庫,他們稱之為 Android 界的 Autopilot——MvRx(ModelView ReactiveX 的縮寫,讀作 mavericks)。這個庫并不“單純”,它其實是一個架構,已經被應用在了 Airbnb 幾乎所有的產品上。地址是:

https://github.com/airbnb/MvRx

這個庫綜合運用了以下幾種技術

  • Kotlin (MvRx is Kotlin first and Kotlin only)

  • Android Architecture Components

  • RxJava

  • React (概念上的)

  • Epoxy (可選但推薦)

光看這個清單,也知道事情并不簡單。利用這個庫我們可以方便地構建出 MVVM 架構的APP,讓開發更加的簡單、高效。


真響應式架構


響應式(React)架構并沒有什么定義,只是我覺得這么描述 MvRx 比較準確。這里所說的響應式架構是指,數據響應式以及界面響應式。數據響應式大體指數據以流的形式呈現(RxJava 那套東西),界面響應式大體指數據驅動界面更新,界面顯示與數據狀態保持一致。

以如上的定義來看,在 RxJava 的幫助下,幾乎所有架構都可以實現數據響應式,因為數據響應式實際上是 Model 層的設計。但是界面響應式則基本上沒有哪個框架實現了,最接近的應該是Android Architecture Components,但是Android Architecture Components 并沒有保證界面與數據狀態的一致,我們通過 LiveData 通知界面更新,只是把數據帶給了界面,界面顯示與數據狀態并不一定是一致的(例如,LiveData 攜帶了下一頁的數據,界面只是把該數據加到了 RecyclerView 的后面,數據并沒有完全代表了當前界面的狀態)。而 MvRx 真正實現了界面的響應式,所以我稱之為真響應式架構。

如果你了解過 Flutter,那么 MvRx 很容易理解,因為兩者都采用了響應式構建的思想,以下是關于 Flutter 的描述,把它替換為 MvRx 也基本上適用。

Flutter 組件采用現代響應式框架構建,這是從 React 中獲得的靈感,中心思想是用組件 (widget) 構建你的 UI。 組件描述了在給定其當前配置和狀態時他們顯示的樣子。當組件狀態改變,組件會重構它的描述 (description),Flutter 會對比之前的描述,以確定底層渲染樹從當前狀態轉換到下一個狀態所需要的最小更改。

由于 Flutter 的實現不受原生的限制,它完全用另外一套方式實現了界面的渲染,并且響應式在設計之初就是 Flutter 的核心,所以在 Flutter 中任何組件(可以理解為 Android 中的 View)都是響應式的,都可以確定它從當前狀態轉換到下一個狀態所需要的最小更改,顯然這一點在原生 Android 上是實現不了的。而 MvRx 在原生 Android 的基礎上幾乎實現了所有界面的響應式,這一點還是非常厲害的。

命令式 MVP 與響應式 MVVM

MVP 模式在 Android 界一直很流行,因為它比較好理解。其核心思想是,通過接口隔離數據與顯示,數據的變動通過接口回調的方式去通知界面更新。這正是典型的命令式 M-V(數據-顯示)鏈接。在這種模式下 View 層是完全被動的,完全受控于 Presenter 層的命令。這種模式并沒有什么大問題,只是有一些不太方便之處,主要體現在 M-V 的緊密鏈接,導致復用比較困難,要么 View 層需要定義不必要的接口(這樣 Presenter 可以復用),要么就需要為幾乎每個 View 都定義一個對應的 Presenter,想想都心累。

不同于 MVP 通過接口的方式來隔離數據與顯示,MVVM 是使用觀察者的方式來隔離數據與顯示。以 Android Architecture Components 構建的 MVVM 模式為例,View 通過觀察 LiveData 來驅動界面更新。MVVM 帶來的主要好處是打破了 M-V 的緊密鏈接,ViewModel 復用變得很簡單,View 層需要什么數據觀察什么數據即可。將 View 抽離為觀察者,可以實現響應式 MVVM 架構,只是 View 本身不是響應式的。

以我的實踐來看 Android Architecture Components 構建的 MVVM 的主要問題是,RxJava 與 LiveData 的銜接并不方便,還有就是按照 Google 給出的 sample,數據加載的狀態需要和數據本身打包在一起,然后通過 LiveData 傳遞出去,我覺得這不是一個好的做法。我在實踐中是在 Observer 的 onSubscribe,onNext,onError 方法中分別對不同的 MutableLiveData 賦值,然后在 View 中去觀察這些 LiveData 來更新界面的。說實話,這很丑陋,但是比 Google 給出的 sample 要方便許多。

MvRx 的真響應式 MVVM

MvRx 構建的 MVVM 模式,完美地解決了上述的問題。MvRx 放棄了LiveData,使用State 來通知 View 層數據的改變(當然仍然是可感知生命周期的)。MvRx 可以方便地把RxJava Observable 的請求過程包裝成 Ansyc 類,不僅可以改變 State 來通知 View 層,而且也包含了數據加載的狀態(成功、失敗、加載中等)。如果結合 Airbnb 的另一個開源庫 Epoxy,那么幾乎可以做到真正的響應式,即View層在數據改變時僅僅描述當前數據狀態下界面的樣子,Epoxy 可以幫我們實現與之前數據狀態的比較,然后找出差別,僅更新那些有差別的 View 部分。這是對 MvRx 的大致描述。下面來看看 MvRx 是如果使用的。


MvRx的使用


MvRx 的重要概念

MvRx 有四個重要的概念,分別是 State、ViewModel、View 和 Async。

  • State

包含界面顯示的所有數據,實現類需是繼承自 MvRxState 的 immutable Kotlin data class。像是這樣

data?class?TasksState(
????val?tasks:?List<Task>?=?emptyList()
,
????val?taskRequest:?Async<List<Task>>?
=?Uninitialized,
????val?isLoading:?Boolean?=?false,
????val?lastEditedTask:?String??=?null
)?:?MvRxState?//MvRxState?僅是一個標記接口

State 的作用是承載數據,并且應該包含有界面顯示的所有數據。當然可以對界面進行拆分,使用多個State共同決定界面的顯示。

State必須是不可變的(immutable),即State的所有屬性必須是val的。只有ViewModel 可以改變 State,改變 State 時一般使用其 copy 方法,創建一個新的 State對象。

可以把 MvRx 的 State 類比成 Architecture Components 中的 LiveData,它們的相同點是都可以被 View 觀察,不同點是,State 的改變會觸發 View 的 invalidate()方法,從而通知界面重繪。

  • ViewModel

完全繼承自 Architecture Components中的 ViewModel,ViewModel 包含有除了界面顯示之外的業務邏輯。此外,最關鍵的一點是,ViewModel 還包含有一個State,ViewModel 可以改變 State 的狀態,然后 View 可以觀察 State 的狀態。實現類需繼承 BaseMvRxViewModel,并且必須向 BaseMvRxViewModel 傳遞 initialState(代表了View 的初始狀態)。像是這樣

class?TasksViewModel(initialState:?TasksState)?:?BaseMvRxViewModel<TasksState>(initialState)
  • View

一般而言是一個繼承自 BaseMvRxFragment 的 Fragment。BaseMvRxFragment 實現了接口 MvRxView,這個接口有一個 invalidate() 方法,每當 ViewModel 的 state 發生改變時 invalidate() 方法都會被調用。View 也可以觀察 State 中的某個或某幾個屬性的變化,View 是沒辦法改變 State 狀態的,只有 ViewModel 可以改變 State 的狀態。

  • Async

代表了數據加載的狀態。Async 是一個Kotlin sealed class,它有四種類型:Uninitialized, Loading, Success, Fail(包含了一個名為 error 的屬性,可以獲取錯誤類型)。Async 重載了操作符 invoke,除了在 Success 返回數據外,其它情況下都返回null:

var?foo?=?Loading()
println(foo())?//?null
foo?=?Success<Int>(5)
println(foo())?//?5
foo?=?Fail(IllegalStateException("bar"))
println(foo())?//?null

在 ViewModel 中可以通過擴展函數execute把Observable<T>的請求過程包裝成Asnyc<T>,這可以方便地表示數據獲取的狀態(下面會有介紹)。

以上四個核心概念是怎么聯系到一起的呢?請看下圖:

圖中沒有包含 Asnyc,State 可包含若干個 Asnyc,用來表示數據加載的狀態,便于顯示Loading 或者加載錯誤信息等。

按照理想情形,View 不需要主動觀察 State,State 的任意改變都會調用 View 的invalidate方法,在 invalidate 方法中根據當前的 State(在 View 中通過 ViewModel 的withState 方法獲取 State)直接重繪一下 View 即可。然而這太過于理想,實際上可以通過 selectSubscribe,asyncSubscribe 等方法觀察 State 中某個屬性的改變,根據特定的屬性更新 View 的特定部分。

以上是 MvRx 的四個核心概念。下面以官方 sample 為例,展示一下 MvRx 應該怎樣使用。

如何使用

ToDo Sample,架構界的 Hello World。界面長這個樣子。

以下以首界面為例,介紹應該如何使用 MvRx。

  • State的使用

//待辦事的定義,包含有id,?title,?description以及是否完成標志complete
data?class?Task(
????var?title:?String?=?"",
????var?description:?String?=?"",
????var?id:?String?=?UUID.randomUUID().toString(),
????var?complete:?Boolean?=?false
)

data?class?TasksState(
????val?tasks:?List<Task>?=?emptyList(),?//界面上的待辦事
????val?taskRequest:?Async<List<Task>>?=?Uninitialized,?//代表請求的狀態
????val?isLoading:?Boolean?=?false,?//是否顯示Loading
????val?lastEditedTask:?String??=?null?//上次編輯的待辦事ID
)?:?MvRxState

State 包含了這個界面要顯示的所有數據。

  • ViewModel 的使用

具體的業務邏輯并不重要,主要看 ViewModel 是如何定義的。

/**
?*?必須有一個initialState
?*?source是數據源,可以是數據庫,也可以是網絡請求等(例子中是數據庫)
?**/

class?TasksViewModel(initialState:?TasksState,?private?val?source:?TasksDataSource)?:?MvRxViewModel<TasksState>(initialState)?{
????//工廠方法,必須實現MvRxViewModelFactory接口
????companion?object?:?MvRxViewModelFactory<TasksViewModel,?TasksState>?{
????????/**
?????????*?主要用途是通過依賴注入傳入一些參數來構造ViewModel
?????????*?TasksState是MvRx幫我們構造的(通過反射)
?????????**/

????????override?fun?create(viewModelContext:?ViewModelContext,?state:?TasksState):?BaseMvRxViewModel<TasksState>?{
????????????//例子中并沒有使用依賴注入,而是直接獲取數據庫
????????????val?database?=?ToDoDatabase.getInstance(viewModelContext.activity)
????????????val?dataSource?=?DatabaseDataSource(database.taskDao(),?2000)
????????????return?TasksViewModel(state,?dataSource)
????????}
????}

????init?{
????????//方便調試,State狀態改變時打印出來
????????logStateChanges()
????????//初始加載任務
????????refreshTasks()
????}

????//獲取待辦事
????fun?refreshTasks()?{
????????source.getTasks()
????????????.doOnSubscribe?{?setState?{?copy(isLoading?=?true)?}?}
????????????.doOnComplete?{?setState?{?copy(isLoading?=?false)?}?}
????????????//execute把Observable包裝成Async
????????????.execute?{?copy(taskRequest?=?it,?tasks?=?it()??:?tasks,?lastEditedTask?=?null)?}
????}

????//新增或者更新待辦事
????fun?upsertTask(task:?Task)?{
????????//通過setState改變?State的狀態
????????setState?{?copy(tasks?=?tasks.upsert(task)?{?it.id?==?task.id?},?lastEditedTask?=??task.id)?}
????????//因為是數據庫操作,一般不會失敗,所以沒有理會數據操作的狀態
????????source.upsertTask(task)
????}

????//標記任務完成與否
????fun?setComplete(id:?String,?complete:?Boolean)?{
????????setState?{
????????????//沒有這個任務,拉倒;this指之前的?State,直接返回之前的?State意思就是無需更新
????????????val?task?=?tasks.findTask(id)??:?return@setState?this
????????????//這個任務已經完成了,拉倒
????????????if?(task.complete?==?complete)?return@setState?this
????????????//找到這個任務,并更新
????????????copy(tasks?=?tasks.copy(tasks.indexOf(task),?task.copy(complete?=?complete)),?lastEditedTask?=?id)
????????}
????????//數據庫更新
????????source.setComplete(id,?complete)
????}

????//清空已完成的待辦事
????fun?clearCompletedTasks()?=?setState?{
????????source.clearCompletedTasks()
????????copy(tasks?=?tasks.filter?{?!it.complete?},?lastEditedTask?=?null)
????}

????//刪除待辦事
????fun?deleteTask(id:?String)?{
????????setState?{?copy(tasks?=?tasks.delete?{?it.id?==?id?},?lastEditedTask?=?id)?}
????????source.deleteTask(id)
????}
}

ViewModel 實現了業務邏輯,其核心作用就是與 Model 層(這里的 source)溝通,并更新 State。這里有幾點需要說明:

  • 按照 MvRx 的要求,ViewModel 可以沒有工廠方法,這樣的話 MvRx 會通過反射構造出 ViewModel(當然這一般不可能,畢竟 ViewModel 一般都包含 Model 層)。如果 ViewModel 包含有除 initialState 之外的其它構造參數,則需要我們實現工廠方法。如上所示,必須通過伴生對象實現 MvRxViewModelFactory 接口。

  • 只能在ViewModel中更新State。更新State有兩種方法,setState或者 execute。setState 很好理解,直接更新 State 即可。其定義如下

  • abstract?class?BaseMvRxViewModel<S?:?MvRxState>?{
    ????//參數是State上的擴展函數,會接收到上次?State的值
    ????protected?fun?setState(reducer:?S.()?->?S)?{
    ????????//...
    ????}
    }

    因為 State 是 immutable Kotlin data class,所以一般而言都是通過 data class 的 copy方法返回新的 State。execute 是一個擴展方法,其定義如下

    abstract?class?BaseMvRxViewModel<S?:?MvRxState>?{
    ????/**
    ?????*?Helper?to?map?an?observable?to?an?Async?property?on?the?state?object.
    ?????*/

    ????//參數依然是State上的擴展函數
    ????fun?<T>?Observable<T>.execute(
    ????????stateReducer:?S.(Async<T>)
    ?->?S
    ????)?=?execute({?it?},?null,?stateReducer)

    ????/**
    ?????*?Execute?an?observable?and?wrap?its?progression?with?AsyncData?reduced?to?the?global?state.
    ?????*
    ?????*?@param?mapper?A?map?converting?the?observable?type?to?the?desired?AsyncData?type.
    ?????*?@param?successMetaData?A?map?that?provides?metadata?to?set?on?the?Success?result.
    ?????*????????????????????????It?allows?data?about?the?original?Observable?to?be?kept?and?accessed?later.?For?example,
    ?????*????????????????????????your?mapper?could?map?a?network?request?to?just?the?data?your?UI?needs,?but?your?base?layers?could
    ?????*????????????????????????keep?metadata?about?the?request,?like?timing,?for?logging.
    ?????*?@param?stateReducer?A?reducer?that?is?applied?to?the?current?state?and?should?return?the
    ?????*?????????????????????new?state.?Because?the?state?is?the?receiver?and?it?likely?a?data
    ?????*?????????????????????class,?an?implementation?may?look?like:?`{?copy(response?=?it)?}`.
    ?????*
    ?????*??@see?Success.metadata
    ?????*/

    ????fun?<T,?V>?Observable<T>.execute(
    ????????mapper:?(T)
    ?->?V,
    ????????successMetaData:?((T)?->?Any)??=?null,
    ????????stateReducer:?S.(Async<V>)?->?S
    ????):?Disposable?{
    ????????setState?{?stateReducer(Loading())?}

    ????????return?map?{
    ????????????????val?success?=?Success(mapper(it))
    ????????????????success.metadata?=?successMetaData?.invoke(it)
    ????????????????success?as?Async<V>
    ????????????}
    ????????????.onErrorReturn?{?Fail(it)?}
    ????????????.subscribe?{?asyncData?->?setState?{?stateReducer(asyncData)?}?}
    ????????????.disposeOnClear()?//ViewModel?clear的時候dispose
    ????}
    }

    execute 方法可以把 Observable 的請求過程包裝成 Async,我們都知道訂閱 Observable 需要有 onNext,onComplete,onError 等方法,execute 就是把這些個方法包裝成了統一的 Async 類。前面已經說過,Async是sealed class,只有四個子類:Uninitialized, Loading, Success, Fail。這些子類完美的描述了一次請求的過程,并且它們重載了 invoke 操作符(Success 情況下返回請求的數據,其它情況均為 null)。因此經??吹竭@樣的樣板代碼:

    fun?<T>?Observable<T>.execute(
    ????stateReducer:?S.(Async<T>)
    ?->?S
    )

    /**
    ?*?根據上面execute的定義,我們傳遞過去的是State上的以Async<T>為參數的擴展函數
    ?*?因此下面的it參數是指?Async<T>,it()是獲取請求的結果,tasks?=?it()??:?tasks?表示只在請求?Success時更新State
    ?**/

    fun?refreshTasks()?{
    ????source.getTasks()
    ????????//...
    ????????.execute?{?copy(taskRequest?=?it,?tasks?=?it()??:?tasks,?lastEditedTask?=?null)?}
    }
    • View 的使用

    abstract?class?BaseFragment?:?BaseMvRxFragment()?{
    ????//activityViewModel是MvRx定義的獲取ViewModel的方式
    ????//按照規范必須使用activityViewModel、fragmentViewModel、existingViewModel(都是Lazy<T>類)獲取ViewModel
    ????protected?val?viewModel?by?activityViewModel(TasksViewModel::class)

    ????//Epoxy的使用
    ????protected?val?epoxyController?by?lazy?{?epoxyController()?}

    ????override?fun?onViewCreated(view:?View,?savedInstanceState:?Bundle?)?{
    ????????//可以觀察State中某個(某幾個)屬性的變化
    ????????viewModel.selectSubscribe(TasksState::tasks,?TasksState::lastEditedTask)?{?tasks,?lastEditedTask?->
    ????????????//...
    ????????}

    ????????//觀察Async屬性
    ????????viewModel.asyncSubscribe(TasksState::taskRequest,?onFail?=?{
    ????????????coordinatorLayout.showLongSnackbar(R.string.loading_tasks_error)
    ????????})
    ????}

    ????//State的改變均會觸發
    ????override?fun?invalidate()?{
    ????????//Epoxy的用法
    ????????recyclerView.requestModelBuild()
    ????}

    ????abstract?fun?epoxyController():?ToDoEpoxyController
    }

    class?TaskListFragment?:?BaseFragment()?{
    ????//另一個ViewModel
    ????private?val?taskListViewModel:?TaskListViewModel?by?fragmentViewModel()

    ????//Epoxy的使用
    ????override?fun?epoxyController()?=?simpleController(viewModel,?taskListViewModel)?{?state,?taskListState?->
    ????????//?We?always?want?to?show?this?so?the?content?won't?snap?up?when?the?loader?finishes.
    ????????horizontalLoader?{
    ????????????id("loader")
    ????????????loading(state.isLoading)
    ????????}

    ????????//...
    ????}
    }

    按照MvRx的規范,View通過activityViewModel(ViewModel被置于Activity中), fragmentViewModel(ViewModel被置于 Fragment 中), existingViewModel(從Activity中獲取已存在的 ViewModel) 來獲取ViewModel,這是因為,以這幾種方式獲取ViewModel,MvRx 會幫我們完成如下幾件事:

  • activityViewModel, fragmentViewModel, existingViewModel其實都是 Kotlin 的Lazy 子類,顯然會是懶加載。但是它不是真正的“懶”,因為在這些子類的構造函數中會添加一個對 View 生命周期的觀察者,在 ON_CREATE 事件發生時會構造出ViewModel,也就是說 ViewModel 最晚到 ON_CREATE 時即被構造完成(為了及早發出網絡請求等)。

  • 通過反射構造出 State,ViewModel。

  • 調用 ViewModel 的 subscribe 方法,觀察 State 的改變,如果改變則調用 View 的invalidate 方法。

  • 當 State 發生改變時,View 的 invalidate 方法會被調用。invalidate被調用僅說明了State 發生了改變,究竟是哪個屬性發生的改變并不得而知,按照 MvRx 的“理想”,哪個屬性發生改變并不重要,只要 View 根據當前的 State“重繪”一下 View 即可。這里“重繪”顯然指的不是簡單地重繪整個界面,應該是根據當前 State“描繪”當前界面,然后與上次界面作比較,只更新差異部分。顯然這種“理想”太過于高級,需要有一個幫手來完成這項任務,于是就有了 Epoxy(其實是先有的 Epoxy)。

    Epoxy 簡單來說就是 RecyclerView的高級助手,我們只需要定義某個數據在RecyclerView 的 ItemView 上是如何顯示的,然后把一堆數據扔給 Epoxy 就行了。Epoxy會幫我們分析這次的數據跟上次的數據有什么差別,只更新差別的部分。如此看來Epoxy真的是MvRx的絕佳助手。關于Epoxy有非常多的內容,查看Epoxy——RecyclerView 的絕佳助手了解更多。

    Epoxy 雖然“高級”,但也僅僅適用于 RecyclerView。因此可以看到 MvRx 的例子中把所有界面的主要部分都以 RecyclerView 承載,例如,Loading 出現在 RecyclerView 的頭部;如果界面是非滾動的,就把界面作為RecyclerView唯一的元素放入其中,等等。這都是為了使用 Epoxy,使開發模式更加統一,并且更加接近于完全的響應式。但是總有些情形下界面不適合用 RecyclerView 展示,沒關系,我們還可以單獨觀察 State 中的某(幾)個屬性的改變(這幾乎與 LiveData 沒有差別)。例如:

    //觀察兩個屬性的改變,任意一個屬性方式了改變都會調用
    ????viewModel.selectSubscribe(TasksState::tasks,?TasksState::lastEditedTask)?{?tasks,?lastEditedTask?->
    ????????//根據屬性值做更新
    ????}

    ????//觀察Async屬性,可以傳入onSuccess、onFail參數
    ????//和上面觀察普通屬性沒有區別,只是內部幫我們判斷了Async是否成功
    ????viewModel.asyncSubscribe(TasksState::taskRequest,?onFail?=?{
    ????????coordinatorLayout.showLongSnackbar(R.string.loading_tasks_error)
    ????})


    問題


    使用 MvRx 有幾個問題需要注意

    State 是 immutable Kotlin data class,Kotlin 幫我們生成了equals方法(即調用每個屬性的 equals 方法),在 ViewModel 中通過 setState,execute 方法更新State時,只有更新后的 State 確實與上一次的 State 不相等時,View 才會收到通知。經常犯的錯誤是這樣的:

    data?class?CheckedData(
    ????val?id:?Int,
    ????val?name:?String,
    ????var?checked:?Boolean?=?false
    )

    //List的equals方法的實現是,項數相同,并且每項都equals
    data?class?SomeState(val?data:?List<CheckedData>?=?emptyList())?:?MvRxState

    class?SomeViewModel(initialState:?SomeState)?:?MvRxViewModel<SomeState>(initialState)?{
    ????fun?setChecked(id:?Int)?{
    ????????setState?{
    ????????????copy(data?=?data.find?{?it.id?==?id?}?.checked?=?true)
    ????????}
    ????}
    }

    這樣做是不行的(也是不允許的),SomeState 的 data 雖然改變了,但對比上一次的SomeState,它們是相等的,因為前后兩個 SomeState 的 data 指向了同一塊內存,必然是相等的,因此不會觸發 View 更新。需要這么做:

    fun?<T>?List<T>.update(newValue:?(T)?->?T,?finder:?(T)?->?Boolean)?=?indexOfFirst(finder).let?{?index?->
    ????if?(index?>=?0)?copy(index,?newValue(get(index)))?else?this
    }

    fun?<T>?List<T>.copy(i:?Int,?value:?T):?List<T>?=?toMutableList().apply?{?set(i,?value)?}

    //最好修改為如下定義,防止直接修改checked屬性
    data?class?CheckedData(
    ????val?id:?Int,
    ????val?name:?String,
    ????//只讀的
    ????val?checked:?Boolean?=?false
    )

    class?SomeViewModel(initialState:?SomeState)?:?MvRxViewModel<SomeState>(initialState)?{
    ????fun?setChecked(id:?Int)?{
    ????????setState?{
    ????????????copy(data?=?data.update({?it.copy(checked?=?true)?},?{?it.id?==?id?}))
    ????????}
    ????}
    }

    這樣前后兩個 SomeState 的 data 指向不同的內存,并且這兩個 data 確實不同,會觸發View 更新。

    緊接著上一點來說,對于 State 而言,如果改變的值與上次的值相同是不會引起 View更新的,這是很合理的行為。但是,如果確實需要在State不變的情況下更新View(例如 State 中包含的某個屬性更新頻繁,你不想創造太多新對象;或者某些屬性只能在原來的對象上更新,例如 SparseArray,查看源碼后發現,壓根兒就不能在State 的屬性中使用 SparseArray),那么 MvRx 的確沒有辦法。別忘了,MvRx 與Android Architecture Components 是并行不悖的,你總是可以使用 LiveData 去實現。對于 MutableLiveData 而言,設置相同的值還是會通知其觀察者,是MvRx 很好的補充。(但是,并不推薦這么做,因為使用 LiveData 會破壞 State 的不可變性,等于你繞開了 MvRx,用另外一種方式去傳遞數據,這不利于數據的統一,也不利于數據界面的一致,不到萬不得已不推薦這么做。)

    MvRx 構建初始的 initialState 和 ViewModel 都使用的是反射,并且 MvRx 支持通過 Fragment 的 arguments 構造 initialState,然而,大多數時候,ViewModel 的initialState是確定的,完全沒有必要通過反射獲取。如果使用 MvRx 規范中的fragmentViewModel 等方式獲取,反射是不可避免的,如果追求性能的話,可以通過拷貝fragmentViewModel的代碼,去除其中的反射,構建自己的獲取ViewModel的方法。

    雖說 MvRx 為 ViewModel 的構建提供了工廠方法,并且這些工廠方法主要目的也是為了依賴注入,但實際上如果真的結合dagger依賴注入的話,你會發現構造ViewModel 變得比較麻煩。而且這種做法并沒有利用 dagger multiBindings 的優勢。實際上dagger可以為ViewModel提供非常友好且便利的ViewModelProvider.Factory類(這在Android Architecture Components的sample中已經有展示),但是MvRx卻沒有提供一種方法來使用自定義的ViewModelProvider.Factory類(見Issues)。

    在我看來,MvRx 最大的特點是響應式,最大的問題也是響應式。因為這種開發模式,與我們之前培養的命令式的開發思維是沖突的,開始的時候總會有種不適應感。最重要的是切換我們的思維方式。


    總結


    總的來說,MvRx 提供了一種 Android 更純粹響應式開發的可能性。并且以 Airbnb 的實踐來看,這種可能性已經被擴展到相當廣的范圍。MvRx 最適合于那些復雜的RecyclerView 界面,通過結合 Epoxy,不僅可以大大提高開發效率,而且其提供的響應式思想可以大大簡化我們的思維。其實,有了 Epoxy 的幫助,絕大部分界面都可以放入RecyclerView 中。對于不適宜使用 RecyclerView 的界面,或者 RecyclerView 之外的一些界面元素,MvRx 至少也提供了與 Android Architecture Components 相似的能力,并且其與 RxJava 的結合更加的友好。

    MvRx 的出現非常符合安迪-比爾定律,硬件的升級遲早會被軟件給消耗掉,或者換種更積極的說法啊,正是因為硬件的發展才給了軟件開發更多的創造力。想想 MvRx,由于 State是 Immutable 的,每次更新 View 必然會產生新的 State;想實現真正的響應式,也必然需要浪費更多的計算力,去幫我們計算界面真正更新的部分(實際上我們是可以提前知曉的)。但我覺得這一切都是值得的,畢竟這些許的算力對于現在的手機來說不值一提,但是對于“人”的效率的提升卻是巨大的。還是那句話,最關鍵的因素還是人啊!


    總結

    以上是生活随笔為你收集整理的Airbnb开源框架,真响应式架构——MvRx的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。