Store
Store用來寫業務邏輯,
在MVIKotlin中用Store介面表示,
介面特性
- 三個引數:輸入
Intent、輸出State、Label, - 屬性
state回傳當前Store的State, - 可以在任何執行緒實體化,
- 函式
states(Observer<State>)用于訂閱State的更新,訂閱時他會發出Store的State,可以在任何執行緒呼叫,States總是在主執行緒上發出, - 函式
labels(Observer<Label>)用于訂閱Labels,可以在任何執行緒呼叫,Labels總是在主執行緒上發出, - 函式
accept(Intent)用于給Store補給Intents,只能在主執行緒呼叫, - 函式
init()用于初始化Store,如果可以的話會觸發Bootstrapper,只能在主執行緒呼叫, - 函式
dispose()用于釋放Store并取消它的所有異步操作,只能在主執行緒呼叫,
states(Observer<State>)與labels(Observer<Label>)通常不直接使用,可以使用擴展Reaktive與kotlinx.coroutines類別庫(詳見生命周期),只有在自定義擴展時會用到這些函式,
組件
任何Store最多只有三個組件:引導程式(Bootstrapper)、執行者(Executor)與縮減器(Reducer),
Reducer為什么叫縮減器:https://blog.csdn.net/uwenhao2008/article/details/79613717

Bootstrapper
用于快速啟動Store,
如果Bootstrapper被傳遞給StoreFactory,它會在Store的初始化期間被執行,
Bootstrapper生產Actions給Executor處理,
Bootstrapper總是在主執行緒執行,Actions也只能在主執行緒調度,在Bootstrapper執行時可以自由切換執行緒,
Bootstrapper是有狀態的,不能作為單例使用,
Executor
Executor用來寫業務邏輯,所有的異步操作都發生在這里,
Executor接收并處理來自外部的Intents與來自內部的Actions,
Executor有兩種輸出:Messages與Labels,
-
Messages被傳遞給Reducer, -
Labels被直接發送到外部,
Executor持續訪問Store的State,在Message被調度后,新的State對Executor來說是可見的,
Executor總是在主執行緒執行,Messages與Labels也只能在主執行緒調度,在Action與Intents處理時可以自由切換執行緒,
Executor是有狀態的,不能作為單例使用,
Reducer
Reducer是一個函式,它接收來自Executor的Message與Store的State作為引數,回傳一個新的State,
Reducer在任何Message被生產后呼叫,呼叫回傳后應用并發送新的State,
Reducer總是在主執行緒呼叫,
創建Store
通常不需要直接實作Store介面,應該使用StoreFactory來創建,只需要將Bootstrapper、Executor和Reducer傳入并初始化State即可,可以在不同的情況下使用不同的StoreFactory并在需要的時候組合他們,
一些由MVIKotlin提供的Factory:
DefaultStoreFactory創建默認的Store實體,由mvikotlin-main模塊提供,LoggingStoreFactory包裝另一個StoreFactory并添加日志記錄,由mvikotlin-logging模塊提供,- TimeTravelStoreFactory 創建具有時間旅行功能的
Store,由mvikotlin-timetravel模塊提供,
初始化Store
默認情況下,Stores由StoreFactory自動初始化,可以通過設定StoreFactory.create(...)函式的autoInit引數為false來禁用自動初始化,
如果自動初始化被禁用,你應該使用
Store.init()函式進行手動初始化,
IDEA動態模板
用于快速創建新的Store:https://gist.github.com/arkivanov/34bb84e73e56c22a4e7c752421d5f02c
最簡單的例子
這個例子會創建一個簡單的計數器Store,它可以實作增加或減少它的值,
定義介面
首先,定義一個介面,它看起來像這樣:
internal interface CalculatorStore : Store<Intent, State, Nothing> {
sealed class Intent {
object Increment : Intent()
object Decrement : Intent()
}
data class State(
val value: Long = 0L
)
}
CalculatorStore介面本身可以標記為internal,因此這是一個模塊的具體實作,
同時,CalculatorStore有兩個Intents(Increment和Decrement)并且State有一個Long型別的屬性value,這是這個Store的公開API部分,
實作工廠
接下來是Store的實體化工廠:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
fun create(): CalculatorStore =
object : CalculatorStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "CounterStore",
initialState = State(),
reducer = ReducerImpl
) {
}
private object ReducerImpl : Reducer<State, Intent> {
override fun State.reduce(msg: Intent): State =
when (msg) {
is Intent.Increment -> copy(value = https://www.cnblogs.com/xuankaicat/p/value + 1L)
is Intent.Decrement -> copy(value = value - 1L)
}
}
}
我們只需要Reducer組件,它接受Intents并且通過遞增或遞減value的值來修改State,
工廠的create()函式使用作為依賴項傳遞的StoreFactory,
增加Executor
目前CalculatorStore只能遞增或遞減它的值,接下來我們需要實作計算從1加到value的總和,我們需要一個新的Intent:
internal interface CalculatorStore : Store<Intent, State, Nothing> {
sealed class Intent {
object Increment : Intent()
object Decrement : Intent()
data class Sum(val n: Int): Intent() // <-- 增加了這行
}
data class State(
val value: Long = 0L
)
}
目前的想法是CalculatorStore會接收Intent.Sum(N),計算從1加到value的總和并使用計算得到的值更新State,但是這個計算是很耗時的,所以我們應該讓它在后臺執行緒中執行,因此我們需要Executor,
為了讓Executor可以和Reducer進行通信,我們需要Messages:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
private sealed class Msg {
class Value(val value: Long) : Msg()
}
}
我們需要一個新的Reducer,現在它可以通過接收Messages來代替Intents了:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
private sealed class Msg {
class Value(val value: Long) : Msg()
}
private object ReducerImpl : Reducer<State, Msg> {
override fun State.reduce(msg: Msg): State =
when (msg) {
is Msg.Value -> copy(value = https://www.cnblogs.com/xuankaicat/p/msg.value)
}
}
}
Msg.Value(Long)用來替換State中value的值,
接下來是Executor,我們不需要實作整個介面,只需要擴展基本實作即可,
兩個由MVIKotlin提供的Executors基本實作:
- ReaktiveExecutor 基于Reaktive庫實作,由
mvikotlin-extensions-reaktive模塊提供, - CoroutineExecutor 基于協程實作,由
mvikotlin-extensions-coroutines模塊提供,
兩個都來嘗試一下:
ReaktiveExecutor
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
// ...
private class ExecutorImpl : ReaktiveExecutor<Intent, Nothing, State, Msg, Nothing>() {
override fun executeIntent(intent: Intent, getState: () -> State) =
when (intent) {
is Intent.Increment -> dispatch(Msg.Value(getState().value + 1))
is Intent.Decrement -> dispatch(Msg.Value(getState().value - 1))
is Intent.Sum -> sum(intent.n)
}
private fun sum(n: Int) {
singleFromFunction { (1L..n.toLong()).sum() }
.subscribeOn(computationScheduler)
.map(Msg::Value)
.observeOn(mainScheduler)
.subscribeScoped(onSuccess = ::dispatch)
}
}
// ...
}
ExecutorImpl繼承自ReaktiveExecutor并實作了executeIntent方法,executeIntent提供了一個Intent和一個當前State的提供者,對于Intent.Increment和Intent.Decrement只需要簡單地發送一個帶有由dispatch函式包裝的新的值的Message,但對于Intent.Sum需要用到Reaktive來進行多執行緒處理:在computationScheduler中求和,然后轉換到mainScheduler并使用dispatch來發送Message,
ReaktiveExecutor實作了Reaktive的 DisposableScope,它提供了許多擴展函式,我們使用了其中之一:subscribeScoped,這樣可以確保在Store被釋放時訂閱也可以被釋放,
CoroutineExecutor
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
// ...
private class ExecutorImpl : CoroutineExecutor<Intent, Nothing, State, Msg, Nothing>() {
override fun executeIntent(intent: Intent, getState: () -> State) =
when (intent) {
is Intent.Increment -> dispatch(Msg.Value(getState().value + 1))
is Intent.Decrement -> dispatch(Msg.Value(getState().value - 1))
is Intent.Sum -> sum(intent.n)
}
private fun sum(n: Int) {
scope.launch {
val sum = withContext(Dispatchers.Default) { (1L..n.toLong()).sum() }
dispatch(Msg.Value(sum))
}
}
}
// ...
}
ExecutorImpl繼承自CoroutineExecutor,求和程序在Default調度器中被執行,Message在Main執行緒中被調度,
CoroutineExecutor提供了名為scope的CoroutineScope屬性,我們可以用它來執行異步任務,scope的默認調度器是Dispatchers.Main,可以通過傳給CoroutineExecutor的建構式不同的CoroutineContext來重寫默認調度器,在Store被釋放時scope會自動取消,
發布Labels
Labels是由Store(或Executor)生產的一次性事件,一旦Labels被發布,它們會被當前所有的訂閱者接收并且不會快取,Executor有一個專門用來發布Label的函式:publish(Label),
創建Store
我們需要將Executor的構建工廠交給StoreFactory:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
fun create(): CalculatorStore =
object : CalculatorStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "CounterStore",
initialState = State(),
executorFactory = ::ExecutorImpl, // <-- 交付Executor的工廠
reducer = ReducerImpl
) {
}
// ...
}
為什么要使用構建工廠而不是直接使用一個Executor的實體呢?因為前者可以實作時間旅行功能,在除錯時間旅行事件時,它會在必要時創建單獨的執行器實體,并偽造它們的States,
增加Bootstrapper
當我們需要創建Store的一個新的實體時,它會保持一個初始化State并什么都不做,直到你提供了一個Intent,但有時需要引導啟動一個Store,在它被創建時做一些額外的事情,例如,它可以開始從服務端接收事件,或從資料庫中讀取一些資料,這就是Bootstrapper要做的事:生產Actions并交給Executor運行,就像Intents一樣,
CalculatorStore能夠計算從1到N的和,目前它會在接收到Intent.Sum(N)時執行這一步驟,讓我們使用Bootstrapper來讓CalculatorStore被創建時計算sum(100),Executor已經實作了計算的具體程序,所以我們只要發送一個觸發Action給Executor,就像Intent.Sum(N),
首先,添加一個Action:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
// ...
private sealed class Action {
class Sum(val n: Int): Action()
}
// ...
}
然后,在ReaktiveExecutor中處理這個Action:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
// ...
private class ExecutorImpl : ReaktiveExecutor<Intent, Action, State, Msg, Nothing>() {
override fun executeAction(action: Action, getState: () -> State) =
when (action) {
is Action.Sum -> sum(action.n)
}
// ...
}
// ...
}
或者在CoroutineExecutor中來處理,是一樣的:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
// ...
private class ExecutorImpl : CoroutineExecutor<Intent, Action, State, Msg, Nothing>() {
override fun executeAction(action: Action, getState: () -> State) =
when (action) {
is Action.Sum -> sum(action.n)
}
// ...
}
// ...
}
最后,我們需要觸發這個Action,我們需要將一個Bootstrapper傳給StoreFactory,對于這種簡單的情況,我們只需要使用SimpleBootstrapper:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
fun create(): CalculatorStore =
object : CalculatorStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "CounterStore",
initialState = State(),
bootstrapper = SimpleBootstrapper(Action.Sum(100)), // <-- 增加了這行
executorFactory = ::ExecutorImpl,
reducer = ReducerImpl
) {
}
// ...
}
SimpleBootstrapper只調度了提供的Actions,但有時我們可能需要其他的引導程式,例如后臺作業:
使用來自mvikotlin-extensions-reaktive模塊的ReaktiveBootstrapper:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
fun create(): CalculatorStore =
object : CalculatorStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "CounterStore",
initialState = State(),
bootstrapper = BootstrapperImpl, // <-- 傳BootstrapperImp給 StoreFactory
executorFactory = ::ExecutorImpl,
reducer = ReducerImpl
) {
}
private sealed class Action {
class SetValue(val value: Long): Action() // <-- 使用另一個Action
}
// ...
private class BootstrapperImpl : ReaktiveBootstrapper<Action>() {
override fun invoke() {
singleFromFunction { (1L..1000000.toLong()).sum() }
.subscribeOn(computationScheduler)
.map(Action::SetValue)
.observeOn(mainScheduler)
.subscribeScoped(onSuccess = ::dispatch)
}
}
private class ExecutorImpl : ReaktiveExecutor<Intent, Action, State, Msg, Nothing>() {
override fun executeAction(action: Action, getState: () -> State) =
when (action) {
is Action.SetValue -> dispatch(Msg.Value(action.value)) // <-- 處理Action
}
// ...
}
// ...
}
ReaktiveBootstrapper也實作了DisposableScope,就像ReaktiveExecutor一樣,所以我們也可以在這里使用subscribeScoped函式,
使用來自mvikotlin-extensions-coroutines模塊的CoroutineBootstrapper:
internal class CalculatorStoreFactory(private val storeFactory: StoreFactory) {
fun create(): CalculatorStore =
object : CalculatorStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "CounterStore",
initialState = State(),
bootstrapper = BootstrapperImpl,
executorFactory = ::ExecutorImpl,
reducer = ReducerImpl
) {
}
private sealed class Action {
class SetValue(val value: Long): Action()
}
// ...
private class BootstrapperImpl : CoroutineBootstrapper<Action>() {
override fun invoke() {
scope.launch {
val sum = withContext(Dispatchers.Default) { (1L..1000000.toLong()).sum() }
dispatch(Action.SetValue(sum))
}
}
}
private class ExecutorImpl : CoroutineExecutor<Intent, Action, State, Msg, Nothing>() {
override fun executeAction(action: Action, getState: () -> State) =
when (action) {
is Action.SetValue -> dispatch(Msg.Value(action.value))
}
// ...
}
// ...
}
CoroutineBootstrapper也提供了名為scope的CoroutineScope屬性,就和CoroutineExecutor一樣,所以我們可以用它來執行異步任務,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/440447.html
標籤:架構設計
上一篇:微服務工程中,基礎組件應用
