前言
背景知識(shí)旭斥,Android真響應(yīng)式架構(gòu)——MvRx彩掐。
MvRx是什么铭若?最簡單的解釋是搪搏,Mv(ModelView)和Rx(ReactiveX)狭握,MvRx是AAC ModelView和RxJava的結(jié)合,但這只是表象疯溺,MvRx最核心的價(jià)值在于论颅,它是React理念在原生Android開發(fā)領(lǐng)域的一種實(shí)現(xiàn),并且這種實(shí)現(xiàn)充分利用了AAC和Kotlin的能力囱嫩,以一種相對簡單的方式實(shí)現(xiàn)恃疯,降低了它的理解和使用門檻,對客戶端是比較友好的墨闲。那什么是React的理念呢今妄?我認(rèn)為關(guān)鍵有兩點(diǎn):
- View由狀態(tài)(State)表征
- 狀態(tài)由事件(Event,或者稱為動(dòng)作鸳碧、意圖盾鳞,不同地方有不同的叫法)單向驅(qū)動(dòng)
如上圖所示,使用State數(shù)據(jù)渲染View瞻离,View產(chǎn)生Event事件腾仅,發(fā)送給StateStore,StateStore根據(jù)舊的State構(gòu)造出新的State套利,再傳遞給View推励。這種單向數(shù)據(jù)流的架構(gòu)在其它地方也被稱作MVI(Model-View-Intent)架構(gòu)。
那么實(shí)現(xiàn)這么一套架構(gòu)復(fù)雜嗎肉迫?其實(shí)還是很復(fù)雜的验辞,其中涉及到State的管理,對State的觀察昂拂,View如何由State表征等等一系列問題受神。但是,如果借助現(xiàn)有的一些庫格侯,整個(gè)實(shí)現(xiàn)也并不復(fù)雜鼻听。State用Kotlin data class來表示,從View到StateStore的連接借助ViewModel來實(shí)現(xiàn)联四,State的管理StateStore使用RxJava中的Subject來完成撑碴,對State的觀察使用LifecycleObserver+RxJava Observer來實(shí)現(xiàn),從State到View的渲染借助Epoxy來完成朝墩。這就是MvRx的設(shè)計(jì)思路醉拓,所以MvRx本身的源碼并不多,也不復(fù)雜。本文主要對MvRx的核心源碼StateStore和State Observer進(jìn)行解析亿卤。源碼版本com.airbnb.android:mvrx:1.5.1
愤兵。
1. 太長不看
每個(gè)MvRxViewModel都包含一個(gè)MvRxStateStore,通過MvRxViewModel對MvRxStateStore進(jìn)行的setState
,getState
的操作會(huì)被分別放進(jìn)兩個(gè)隊(duì)列中排吴,并且會(huì)給MvRxStateStore中的一個(gè)BehaviorSubject
發(fā)送一個(gè)信號秆乳,BehaviorSubject
收到信號后,會(huì)在一個(gè)單獨(dú)的線程從setState
,getState
的隊(duì)列中取元素钻哩,先從setState
隊(duì)列中取屹堰,之后是getState
隊(duì)列(也就是說異步getState獲取的State是之前還沒執(zhí)行的setState執(zhí)行之后的最新的State),直至getState
隊(duì)列都為空街氢;每次從setState
隊(duì)列取一個(gè)元素(元素類型State.()->State
扯键,一般稱之為reducer
)后,都會(huì)執(zhí)行這個(gè)reducer
珊肃,完成從舊State到新State的更新荣刑,然后通知StateObservable
,這樣外部就能觀察到State的變化了近范。
上圖出自Unidirectional data flow on Android using Kotlin嘶摊,他把Event稱為Action,這篇文章從概念上解釋了什么是Android單向數(shù)據(jù)流评矩,如果你不關(guān)心具體的源碼實(shí)現(xiàn)叶堆,只是想從概念上對單向數(shù)據(jù)流建立一個(gè)圖像,那么推薦你看看這篇文章斥杜。
2. MvRxStateStore
如上所述虱颗,StateStore的職責(zé)就是對State的管理,主要包括接收外部輸入蔗喂,由舊生新忘渔;讓外部可以觀察State的變化,基本上就這些缰儿。MvRx定義了一個(gè)接口MvRxStateStore明確了這些職責(zé):
interface MvRxStateStore<S : Any> : Disposable {
val state: S //同步方式獲取 state
fun get(block: (S) -> Unit) //異步方式獲取 state
fun set(stateReducer: S.() -> S) // 更新 state畦粮,由舊生新
val observable: Observable<S> //外部觀察 state
}
獲取State的方式有兩種:同步和異步,獲取State并不等同于觀察State乖阵,一般是為了根據(jù)當(dāng)前State決定下一步的動(dòng)作宣赔,是一種一次性的行為。
每個(gè)MvRxViewModel都必須明確它的State瞪浸,并且每個(gè)MvRxViewModel也都包含一個(gè)MvRxStateStore:
interface MvRxState
abstract class BaseMvRxViewModel<S : MvRxState>(
initialState: S,
debugMode: Boolean,
private val stateStore: MvRxStateStore<S> = RealMvRxStateStore(initialState)
) : ViewModel() {
//通過stateStore同步獲取state
internal val state: S
get() = stateStore.state
protected fun setState(reducer: S.() -> S) {
if (debugMode) {
//debug模式下會(huì)進(jìn)行一些驗(yàn)證
//核心邏輯就是儒将,reducer運(yùn)行兩遍,兩遍運(yùn)行得到的 state要求相等对蒲,這基本上保證了reducer是個(gè)“純函數(shù)”
} else {
stateStore.set(reducer)
}
}
protected fun withState(block: (state: S) -> Unit) {
stateStore.get(block)
}
}
MvRxState只是一個(gè)標(biāo)記接口钩蚊,所以State可以是任意類贡翘,只要它實(shí)現(xiàn)了MvRxState接口。但是砰逻,BaseMvRxViewModel包含了對State的驗(yàn)證邏輯鸣驱,要求State必須是Kotlin data class,并且其所有屬性必須是不可變的(immutable)蝠咆,不能在data class中定義var
屬性丐巫,并且不能使用諸如MutableList
,MutableMap
之類的可變集合勺美,這是為了方便MvRxStateStore對State的管理,強(qiáng)制要求改變State必須通過MvRxStateStore由舊生新碑韵。
每個(gè)MvRxViewModel都包含一個(gè)MvRxStateStore赡茸,默認(rèn)值是RealMvRxStateStore
,并且構(gòu)造時(shí)就傳入了初始State祝闻,初始State對于RealMvRxStateStore
而言是很重要的占卧。
BaseMvRxViewModel
對MvRxStateStore
的使用是很直接的,其實(shí)對MvRxStateStore.observable
也是很直接的使用联喘,但是observable
的觀察者比較復(fù)雜华蜒,我們之后再看。
3. RealMvRxStateStore
class RealMvRxStateStore<S : Any>(initialState: S) : MvRxStateStore<S> {
//State Subject
private val subject: BehaviorSubject<S> = BehaviorSubject.createDefault(initialState)
//刷新“隊(duì)列”的信號
private val flushQueueSubject = BehaviorSubject.create<Unit>()
//“隊(duì)列”實(shí)體豁遭,Jobs類的定義之后會(huì)看到
private val jobs = Jobs<S>()
//State Observable
override val observable: Observable<S> = subject
//同步獲取 state
override val state: S
get() = subject.value!! //必然不為null叭喜,因?yàn)閟ubject創(chuàng)建的時(shí)候提供了初始值 initialState
init {
//在一個(gè)單獨(dú)的線程刷新隊(duì)列,避免setState的競爭
flushQueueSubject.observeOn(Schedulers.newThread())
//在flushQueueSubject收到信號的時(shí)候蓖谢,刷新隊(duì)列捂蕴,state由舊生新的核心邏輯就在flushQueues方法中
.subscribe({ flushQueues() }, ::handleError)
.registerDisposable()
}
//異步獲取 state
override fun get(block: (S) -> Unit) {
//入getState隊(duì)列,然后發(fā)送信號
jobs.enqueueGetStateBlock(block)
flushQueueSubject.onNext(Unit)
}
override fun set(stateReducer: S.() -> S) {
//入setState隊(duì)列闪幽,然后發(fā)送信號
jobs.enqueueSetStateBlock(stateReducer)
flushQueueSubject.onNext(Unit)
}
}
一個(gè)小的背景知識(shí)啥辨,Subject
是RxJava中一種既是Observable
又是Observer
的類,而BehaviorSubject
是Subject
幾個(gè)子類中最“正扯㈦纾”的那一個(gè)溉知,把它自己接收到的數(shù)據(jù),再發(fā)送出去腕够。RealMvRxStateStore
中包含兩個(gè)BehaviorSubject
级乍,一個(gè)就是我們的State Subject,另一個(gè)是信號Subject燕少。當(dāng)set,get State的時(shí)候卡者,會(huì)把要執(zhí)行的內(nèi)容入隊(duì)列,然后向信號Subject發(fā)送信號客们,在RealMvRxStateStore
的構(gòu)造函數(shù)中就已經(jīng)注冊了信號Subject的觀察者——flushQueues()
崇决,信號會(huì)在一個(gè)新的線程中被接收到材诽,接收到信號就會(huì)在這個(gè)新線程中執(zhí)行flushQueues()
,雖然我們還沒有看flushQueues()
的具體內(nèi)容恒傻,但想想也知道肯定就是從隊(duì)列中取內(nèi)容然后執(zhí)行脸侥,如果是setState會(huì)設(shè)置“新State”給State Subject,這樣新的state就傳遞了出去盈厘,Done睁枕。
來看看“隊(duì)列”的實(shí)體類Jobs:
class Jobs<S> {
private val getStateQueue = LinkedList<(state: S) -> Unit>()
private var setStateQueue = LinkedList<S.() -> S>()
@Synchronized
fun enqueueGetStateBlock(block: (state: S) -> Unit) {
getStateQueue.add(block)
}
@Synchronized
fun enqueueSetStateBlock(block: S.() -> S) {
setStateQueue.add(block)
}
@Synchronized
fun dequeueGetStateBlock(): ((state: S) -> Unit)? {
//getStateQueue為空,就會(huì)返回 null
return getStateQueue.poll()
}
//出隊(duì)所有setStateQueue
@Synchronized
fun dequeueAllSetStateBlocks(): List<(S.() -> S)>? {
if (setStateQueue.isEmpty()) return null
val queue = setStateQueue
setStateQueue = LinkedList()
return queue
}
}
相當(dāng)之無聊沸手,就兩個(gè)LinkedList
外遇,進(jìn)進(jìn)隊(duì)出出隊(duì)。RealMvRxStateStore
的set
和get
方法一般都會(huì)是在后臺(tái)線程中執(zhí)行契吉,對setStateQueue
,getStateQueue
進(jìn)出隊(duì)是會(huì)有多線程同步的問題跳仿,所以這些方法都加了鎖。
好了捐晶,現(xiàn)在一切都準(zhǔn)備好了菲语,就剩下flushQueues()
的具體實(shí)現(xiàn)了:
class RealMvRxStateStore<S : Any>(initialState: S) : MvRxStateStore<S> {
//不常用的 tailrec功能,即尾遞歸優(yōu)化惑灵,會(huì)把遞歸調(diào)用改為循環(huán)的模式
private tailrec fun flushQueues() {
//先出隊(duì) setStateQueue并且執(zhí)行
flushSetStateQueue()
//然后再出隊(duì) getStateQueue
val block = jobs.dequeueGetStateBlock() ?: return
block(state)
//遞歸調(diào)用山上,直至getStateQueue為空
flushQueues()
}
private fun flushSetStateQueue() {
//所有setStateQueue全部出隊(duì),挨個(gè)執(zhí)行
val blocks = jobs.dequeueAllSetStateBlocks() ?: return
for (block in blocks) {
val newState = state.block()
// state不同才發(fā)送
if (newState != state) {
subject.onNext(newState)
}
}
}
}
正如我們預(yù)期的那樣英支,flushQueues()
核心邏輯就是出隊(duì)然后執(zhí)行佩憾,只不過執(zhí)行順序是有講究的。在MvRxModelView中干花,我們經(jīng)常這么寫:
getState { state ->
if (state.isLoading) return
//或者是發(fā)送一個(gè)網(wǎng)絡(luò)請求鸯屿,總之最后會(huì)調(diào)用 setState
setState { state ->
state.copy(...)
}
}
也就是說,getStateQueue
隊(duì)列中的內(nèi)容時(shí)常包含著入隊(duì)setStateQueue
把敢,那么考慮下面一個(gè)問題:
getStateA {
setStateA {}
}
getStateB {
setStateB {}
}
如果是像上面這樣的調(diào)用順序寄摆,那么我們期望的執(zhí)行順序是getStateA->setStateA->getStateB->setStateB
,你仔細(xì)思考一下flushQueues()
的出隊(duì)順序修赞,得到的執(zhí)行順序正是我們期望的那樣婶恼。
最后,調(diào)用subject.onNext(newState)
柏副,通過RealMvRxStateStore
的override val observable: Observable<S> = subject
勾邦,新state狀態(tài)就被傳遞了出去。注意割择,flushQueues()
是在一個(gè)單獨(dú)的新線程中執(zhí)行眷篇,每個(gè)RealMvRxStateStore
都會(huì)新建一個(gè)線程,多個(gè)RealMvRxStateStore
之間不存在競爭荔泳。
這里補(bǔ)充一個(gè)小細(xì)節(jié)蕉饼,
flushSetStateQueue
方法中會(huì)依次遍歷setStateQueue
出隊(duì)的各個(gè)元素虐杯,并且對每個(gè)元素執(zhí)行完reducer操作之后,都會(huì)把得到的新的State傳遞出去subject.onNext(newState)
昧港,其實(shí)還有另外一種選擇擎椰,那就是“狀態(tài)折疊”,假設(shè)setStateQueue
出隊(duì)元素有兩個(gè)A,B创肥,我們可以A,B依次執(zhí)行达舒,但是只對外傳遞最終的newStateB
,A作為中間狀態(tài)就不對外傳遞了叹侄,這樣可以提高效率巩搏,但是這樣會(huì)引發(fā)一個(gè)問題,oldState->newStateA->newStateB
趾代,如果只傳遞最終的狀態(tài)newStateB
塔猾,newStateA
就會(huì)消失了,至于這是否是我們想要的結(jié)果稽坤,這取決于實(shí)際情況,所以“狀態(tài)折疊”會(huì)提高效率糯俗,但是可能會(huì)有問題尿褪,不進(jìn)行“狀態(tài)折疊”會(huì)些許降低效率,但是總是一個(gè)不會(huì)出錯(cuò)方案得湘,MvRx也是從最初的“狀態(tài)折疊”調(diào)整為現(xiàn)在的不再折疊杖玲。
3. MvRxLifecycleAwareObserver
經(jīng)過RealMvRxStateStore
的一番操作,新的State通過Observable被傳遞了出去淘正,要想觀察State的變化就需要一個(gè)Observer摆马,同時(shí),這個(gè)Observe還需要感知生命周期鸿吆,所以這個(gè)這個(gè)觀察者應(yīng)該是RxJava Observer + LifecycleObserver囤采,在MvRx中的實(shí)現(xiàn)就是MvRxLifecycleAwareObserver
。
有一點(diǎn)需要明確惩淳,無論我們以何種方式觀察State的變化,觀察整個(gè)State,還是觀察State中的某幾個(gè)屬性磺送,在View中觀察歉摧,還是在ViewModel中觀察,最終都會(huì)調(diào)用BaseMvRxViewModel
的subscribeLifecycle
方法:
abstract class BaseMvRxViewModel<S : MvRxState> {
private fun <T : Any> Observable<T>.subscribeLifecycle(
lifecycleOwner: LifecycleOwner? = null,
deliveryMode: DeliveryMode,
subscriber: (T) -> Unit
): Disposable {
//觀察者邏輯在主線程執(zhí)行
return observeOn(AndroidSchedulers.mainThread())
.resolveSubscription(lifecycleOwner, deliveryMode, subscriber)
.disposeOnClear()
}
private fun <T : Any> Observable<T>.resolveSubscription(
lifecycleOwner: LifecycleOwner? = null,
deliveryMode: DeliveryMode, //忽略這個(gè)參數(shù)
subscriber: (T) -> Unit
): Disposable = if (lifecycleOwner == null || FORCE_DISABLE_LIFECYCLE_AWARE_OBSERVER) {
//沒有提供生命周期激蹲,或者調(diào)試狀態(tài)棉磨,直接觀察
this.subscribe(subscriber)
} else {
//綁定生命周期,那么觀察者會(huì)被包裝成 MvRxLifecycleAwareObserver
this.subscribeWith(
MvRxLifecycleAwareObserver(
lifecycleOwner,
onNext = Consumer { value ->
subscriber(value)
}
)
)
}
}
如果提供的觀察者subscriber
綁定了生命周期学辱,那么它會(huì)被包裝成MvRxLifecycleAwareObserver
乘瓤。既然我們提供了生命周期环形,那么就是想實(shí)現(xiàn)類似LiveData
那樣的數(shù)據(jù)觀察模式,生命周期≥STARTED時(shí)馅扣,才通知觀察者斟赚。MvRxLifecycleAwareObserver
就是實(shí)現(xiàn)這樣的邏輯。
//實(shí)現(xiàn)了三個(gè)接口 LifecycleObserver, Observer<T>, Disposable差油,我們忽略Disposable的相關(guān)邏輯拗军,主要看LifecycleObserver, Observer
internal class MvRxLifecycleAwareObserver<T : Any>(
private var owner: LifecycleOwner?,
private val activeState: Lifecycle.State = State.STARTED,
private val deliveryMode: DeliveryMode = RedeliverOnStart,
private var lastDeliveredValueFromPriorObserver: T?,
private var sourceObserver: Observer<T>?,
private val onDispose: () -> Unit
) : AtomicReference<Disposable>(), LifecycleObserver, Observer<T>, Disposable {
//次構(gòu)造函數(shù),就是把sourceObserver一個(gè)參數(shù)拆分成onNext, onError, onComplete, onSubscribe四個(gè)參數(shù)
constructor(
owner: LifecycleOwner,
activeState: Lifecycle.State = DEFAULT_ACTIVE_STATE,
deliveryMode: DeliveryMode = RedeliverOnStart,
lastDeliveredValue: T? = null,
onComplete: Action = Functions.EMPTY_ACTION,
onSubscribe: Consumer<in Disposable> = Functions.emptyConsumer(),
onError: Consumer<in Throwable> = Functions.ON_ERROR_MISSING,
onNext: Consumer<T> = Functions.emptyConsumer(),
onDispose: () -> Unit
) : this(owner, activeState, deliveryMode, lastDeliveredValue, LambdaObserver<T>(onNext, onError, onComplete, onSubscribe), onDispose)
//上次未傳輸?shù)闹敌罾磦鬏斒且驗(yàn)?Lifecycle未活躍
private var lastUndeliveredValue: T? = null
//上次的值发侵,可能已經(jīng)傳輸過了
private var lastValue: T? = null
//Lifecycle處于未活躍狀態(tài)時(shí)則上鎖
private val locked = AtomicBoolean(true)
private val isUnlocked
get() = !locked.get()
override fun onSubscribe(d: Disposable) {
if (DisposableHelper.setOnce(this, d)) {
//開始觀察時(shí),也會(huì)對生命周期進(jìn)行觀察
owner!!.lifecycle.addObserver(this)
sourceObserver!!.onSubscribe(this)
}
}
//觀察 Lifecycle ON_DESTROY事件
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
owner!!.lifecycle.removeObserver(this)
if (!isDisposed) {
dispose()
}
owner = null
sourceObserver = null
}
//觀察 Lifecycle任意事件
@OnLifecycleEvent(Event.ON_ANY)
fun onLifecycleEvent() {
updateLock()
}
private fun updateLock() {
//每個(gè)Lifecycle事件都更新鎖妆偏,處于活躍狀態(tài)時(shí)解鎖刃鳄,否則加鎖
if (owner?.lifecycle?.currentState?.isAtLeast(activeState) == true) {
unlock()
} else {
lock()
}
}
override fun onNext(nextValue: T) {
if (isUnlocked) {
sourceObserver!!.onNext(nextValue)
} else {
//記錄下未傳輸?shù)闹? lastUndeliveredValue = nextValue
}
lastValue = nextValue
}
override fun onError(e: Throwable) {
if (!isDisposed) {
lazySet(DisposableHelper.DISPOSED)
sourceObserver!!.onError(e)
}
}
override fun onComplete() {
sourceObserver!!.onComplete()
}
private fun unlock() {
if (!locked.getAndSet(false)) {
return
}
if (!isDisposed) {
val valueToDeliverOnUnlock = //根據(jù) deliveryMode確定要傳輸?shù)闹? lastUndeliveredValue = null
//只有Lifecycle處于活躍狀態(tài),并且沒被dispose钱骂,才會(huì)傳輸值
if (valueToDeliverOnUnlock != null) {
onNext(valueToDeliverOnUnlock)
}
}
}
private fun lock() {
locked.set(true)
}
}
MvRxLifecycleAwareObserver
既是RxJava Observer又是LifecycleObserver叔锐,每個(gè)Lifecycle的Event事件,都會(huì)導(dǎo)致MvRxLifecycleAwareObserver
鎖標(biāo)記的更新见秽,Lifecycle處于活躍時(shí)locked = false
愉烙,否則locked = true
,只有在Lifecycle處于活躍時(shí)才會(huì)調(diào)用我們定義的觀察邏輯sourceObserver
解取,否則只是把值記錄下來步责,待到活躍時(shí)再傳輸。最終sourceObserver
接收到的值還和deliveryMode
的取值有關(guān)禀苦,這是個(gè)sealed class蔓肯,只有兩種類型:RedeliverOnStart
和UniqueOnly
,顧名思義振乏,在RedeliverOnStart
模式下蔗包,值會(huì)被重新傳輸給我們的觀察者sourceObserver
,哪怕這個(gè)值之前已經(jīng)傳輸過慧邮;而UniqueOnly
模式下則只會(huì)傳輸還未傳輸?shù)闹灯摇1热缡謾C(jī)不斷的息屏亮屏,假設(shè)這期間State的值沒有變化赋咽,那么在RedeliverOnStart
模式下旧噪,每次亮屏?xí)r,sourceObserver
都會(huì)接收到之前的值脓匿;UniqueOnly
模式下則不會(huì)接收到任何值(所以UniqueOnly
模式一般適用于一次性的行為淘钟,例如Toast,Snackbar,Log等)。簡明起見陪毡,關(guān)于deliveryMode
的邏輯米母,在上述代碼中都被刪除了勾扭。
4. 總結(jié)
MvRx最核心的邏輯就是MvRxStateStore
了,這是實(shí)現(xiàn)單向數(shù)據(jù)流的關(guān)鍵铁瞒,MvRx利用了RxJava Subject的特性妙色,非常簡明地完成了StateStore的功能。State管理的核心邏輯其實(shí)就是一個(gè)多向輸入慧耍,單向輸出的過程身辨,而Subject的功能正契合這一需求,這使得RxJava Subject成了Android平臺(tái)實(shí)現(xiàn)單向數(shù)據(jù)流的不二之選——直到Kotlin coroutines的出現(xiàn)芍碧。
關(guān)于Android單向數(shù)據(jù)流/MvRx更多的思考請看Android單向數(shù)據(jù)流——Side Effect煌珊。