Android真響應式架構(gòu)——MvRx和Epoxy的結(jié)合

前言

Android真響應式架構(gòu)系列文章:

Android真響應式架構(gòu)——MvRx
Epoxy——RecyclerView的絕佳助手
Android真響應式架構(gòu)——Model層設計
Android真響應式架構(gòu)——數(shù)據(jù)流動性
Android真響應式架構(gòu)——Epoxy的使用
Android真響應式架構(gòu)——MvRx和Epoxy的結(jié)合

之前的文章講了MvRx,講了Epoxy,但是卻沒有講MvRx是怎么跟Epoxy結(jié)合起來的。這篇文章就來講講MvRx和Epoxy是怎么結(jié)合在一起進而實現(xiàn)數(shù)顯兩開花的(前面都省略掉,不管了直接開花吧)。

這是這個系列文章的最后一篇了,本意是想寫一篇MvRx在一個復雜界面上的實踐的,但是,我發(fā)現(xiàn)這個實例有點復雜,并且牽扯到過多的業(yè)務細節(jié),很難講清楚,所以就放棄了。

1. MvRx和Epoxy結(jié)合

在MvRx的sample中已經(jīng)展示了如何使用Epoxy,還是很簡單的。

abstract class BaseFragment : BaseMvRxFragment() {

    //Epoxy擴展的 RecyclerView,第二篇文章中講過
    protected lateinit var recyclerView: EpoxyRecyclerView
    
    protected val epoxyController by lazy { epoxyController() }

    //ViewModel中 State的任意改變都會調(diào)用該方法,這里也僅僅是通知 EpoxyController重建 Model
    override fun invalidate() {
        recyclerView.requestModelBuild()
    }

    abstract fun epoxyController(): ToDoEpoxyController
}

class ToDoEpoxyController(
    val buildModelsCallback: EpoxyController.() -> Unit = {}
) : AsyncEpoxyController() { //AsyncEpoxyController,異步的 EpoxyController,Model的構(gòu)建及diff均在子線程中,第二篇文章中講過
    override fun buildModels() {
        //委托給我們傳遞進來的 EpoxyController的擴展函數(shù)
        buildModelsCallback()
    }
}

代碼很簡單,關鍵就是如何在子類中實現(xiàn)buildModelsCallback。繼續(xù)往下看:


/**
 * Create a [ToDoEpoxyController] that builds models with the given callback.
 * When models are built the current state of the viewmodel will be provided.
 * 嗯,你看人家說的多好,就是這個意思。。。
 */
fun <S : MvRxState, A : MvRxViewModel<S>> BaseFragment.simpleController(
    viewModel: A,
    buildModels: EpoxyController.(state: S) -> Unit
) = ToDoEpoxyController {
    //構(gòu)建Models是異步的,可能fragment已經(jīng)不存在
    if (view == null || isRemoving) return@ToDoEpoxyController
    //MvRx給我們提供的 withState方法,線程安全,可以獲取當前的 State,便于我們構(gòu)建Models
    withState(viewModel) { state ->
        buildModels(state)
    }
}

//兩個ViewModel,如果你需要更多個ViewModel,可以繼續(xù)擴展
fun <A : BaseMvRxViewModel<B>, B : MvRxState, C : BaseMvRxViewModel<D>, D : MvRxState> BaseFragment.simpleController(
    viewModel1: A,
    viewModel2: C,
    buildModels: EpoxyController.(state1: B, state2: D) -> Unit
) = ToDoEpoxyController {
    if (view == null || isRemoving) return@ToDoEpoxyController
    withState(viewModel1, viewModel2) { state1, state2 ->
        buildModels(state1, state2)
    }
}

/**
 * 具體某個Fragment的實現(xiàn)
 */
class TaskListFragment : BaseFragment() {

    private val taskListViewModel: TaskListViewModel by fragmentViewModel()

    override fun epoxyController() = simpleController(viewModel, taskListViewModel) { state, taskListState ->
        //Epoxy生成的EpoxyModel,使用Kotlin的情況下可以采用這種DSL的寫法
        horizontalLoader {
            id("loader")
            loading(state.isLoading)
        }

        //...
    }
}

simpleController的幫助下,獲取當前State,并且重建EpoxyModels即可。Epoxy負責界面的響應式,MvRx實現(xiàn)狀態(tài)的管理,雙劍合璧,相得益彰。

2. 顯示Loading

MvRx給的sample中,Loading也是作為RecyclerView的一個item管理的。這固然是為了界面管理的統(tǒng)一,但是又有點矯枉過正了。一般情況下,我們還是希望Loading以浮動的形式呈現(xiàn)。前文提到MvRx使用Async來表示數(shù)據(jù)的加載狀態(tài),我們可以通過觀察State中的Async屬性來顯示Loading。以下是我的實踐:

//定義State的基類,所有State都包含屬性repoAsync
interface BaseState : MvRxState {
    val repoAsync: Async<Any> //僅僅是為了顯示Loading,泛型指定為Any
}

abstract class BaseViewModelFragment<S : BaseState, VM : BaseMvRxViewModel<S>> : BaseMvRxFragment() {
    //主ViewModel,F(xiàn)ragment可以包含有多個View,但是這個ViewModel有顯示Loading的作用
    protected abstract val viewModel: VM

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.subscribe { state ->
            if (state.repoAsync is Loading) {
                //顯示Loading
                startLoading()
            } else if (state.repoAsync.complete) { //Async包含complete屬性,Success或者Fail的情況下為 true
                //取消Loading
                stopLoading()
                if (state.repoAsync is Fail) {
                    //處理失敗信息
                    handleFail((state.repoAsync as Fail<Any>).error)
                }
            }
        }
    }
}

data class SomeState(...) : BaseState

class SomeViewModel(initialState: SomeState, private val repo: DataRepository) : BaseMvRxViewModel<SomeState>(initialState) {
    fun fetchData() {
        repo.fetchData() //假設返回的是Observable
            .excute {
                //把it(即Async)賦值給repoAsync就會顯示Loading;不需要顯示Loading,不賦值即可
                copy(repoAsync = it, ...)
            }
    }
}

如上,通過State基類中的repoAsync屬性,統(tǒng)一控制Loading的顯示。需要顯示Loading的請求,對repoAsync賦值即可(前提得是主ViewModel);不需要顯示Loading,不賦值即可。

總結(jié)

沒啥好總結(jié)的,一目了然。好吧,這一系列文章終于寫完了,歡迎留言與我交流。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容