Android Flux 架構(gòu)

Android Flux 框架

github地址:https://github.com/tianwei0828/FluxDemo 歡迎大家star

參考facebook https://facebook.github.io/flux/ 提出的flux設(shè)計理念,將單向數(shù)據(jù)流動的思想應(yīng)用在Android App框架中,采用kotlin語言并結(jié)合主流開源框架(1、Rxkotlin 2、Retrofit 3、Okhttp 4、EventBus 5、rxbinding等)打造出Android Flux框架。

一、框架對比

1、MVC
各層職責(zé):
V:一般采用XML文件進(jìn)行界面的描述
C:Android的控制層的重任通常落在了眾多的Activity的肩上,控制V層和M層通信以此來達(dá)到分離視圖顯示和業(yè)務(wù)邏輯層
M:針對業(yè)務(wù)模型,建立的數(shù)據(jù)結(jié)構(gòu)和相關(guān)的類,與View無關(guān),而與業(yè)務(wù)相關(guān)的。對數(shù)據(jù)庫的操作、對網(wǎng)絡(luò)等的操作都應(yīng)該在Model里面處理
優(yōu)點:
1、易于理解
2、開發(fā)速度快
缺點:
1、XML作為V層,不能動態(tài)改變,這就需要Activity承擔(dān)部分V層邏輯
2、Activity不僅僅承擔(dān)C層,而且承擔(dān)了V層的邏輯
3、隨著界面及其邏輯的復(fù)雜度不斷提升,Activity類的職責(zé)不斷增加,以致變得龐大臃腫
4、V層和M層是相互可知的,這意味著兩層之間存在耦合
2、MVP
image
各層職責(zé):
V:XML+Activity+Fragment等
P:作為V層與M層交互的中間紐帶,處理與用戶交互的業(yè)務(wù)邏輯
M:同上
優(yōu)點:
1、Activity承擔(dān)的更多的是V層職責(zé),相比于MVC,將C層的邏輯從Activity抽離到P層,使得Activity不再臃腫
2、M層與V層完全分離,我們可以修改V層而不影響M層
3、邏輯放在P層中,可以脫離用戶接口來測試這些邏輯(單元測試)
4、我們可以將一個P用于多個V,而不需要改變P的邏輯。這個特性非常的有用,因為V的變化總是比M的變化頻繁
缺點:
1、P層與V層是通過接口進(jìn)行交互的,接口粒度不好控制。粒度太小,就會存在大量接口的情況,使代碼太過碎片化;粒度太大,解耦效果不好
2、V層與P層還是有一定的耦合度。一旦V層某個UI元素更改,那么對應(yīng)的接口就必須得改,數(shù)據(jù)如何映射到UI上、事件監(jiān)聽接口這些都需要轉(zhuǎn)變,牽一發(fā)而動全身
3、復(fù)雜的業(yè)務(wù)同時也可能會導(dǎo)致P層太大,代碼臃腫的問題依然不能解決
3、MVC與MVP的區(qū)別
1、MVP中V與M解耦
2、MVP中V與P是交互是通過接口來實現(xiàn)的
3、通常V與P是一對一的,但復(fù)雜的V可能綁定多個P來處理邏輯。而C是基于行為的,并且可以被多個V共享,C可以負(fù)責(zé)決定顯示哪個V
4、MVVM
各層職責(zé):
V:XML+Activity+Fragment等
VM:ViewModel的縮寫,可以理解成是V的數(shù)據(jù)模型和P的合體
M:同上
優(yōu)點:
1、DataBinding可以實現(xiàn)雙向的交互,這就使得V和VM之間的耦合程度進(jìn)一步降低,關(guān)注點分離更為徹底,同時減輕了Activity的壓力
2、提高可維護性。解決了MVP大量的手動V和M同步的問題,提供雙向綁定機制。提高了代碼的可維護性
缺點:
1、去除了P層,會導(dǎo)致V層依然過重
2、xml的可讀性非常差
3、數(shù)據(jù)綁定的聲明是指令式地寫在V的模版當(dāng)中的,這些內(nèi)容是沒辦法去打斷點debug的
5、MVI
image
工作流程:
1、用戶事件出發(fā)intents方法,intents方法將intent流merge
2、調(diào)用VM的processIntents,然后將intents轉(zhuǎn)發(fā)到PublishSubject中
3、在VM中將intents轉(zhuǎn)化成action
4、在VM中根據(jù)不同的action執(zhí)行不同的task,task返回對應(yīng)的result
5、在VM中通過reducer將result轉(zhuǎn)換成VS((ViewState))
6、在V中訂閱VM中的事件代理,并執(zhí)行render方法,根據(jù)不同的VS做相應(yīng)的UI展示
各層職責(zé):
V:Activity+Fragment,發(fā)射intents到VM,訂閱VM從而實現(xiàn)V的更新
I:V的intent,如點擊事件進(jìn)行的下一步操作,是一個class
M:準(zhǔn)確的說,應(yīng)該是VM(ViewModel),訂閱V的intents,處理intents然后發(fā)射VS(ViewState)
優(yōu)點:
1、數(shù)據(jù)單向流動
2、充分利用RxJava響應(yīng)式編程的優(yōu)點
缺點:
1、學(xué)習(xí)成本高,需要熟練掌握ReactiveX的操作符
2、VM承擔(dān)的任務(wù)太多,太過臃腫
3、V要承擔(dān)部分RxJava生命周期的管理

二、facebook flux結(jié)構(gòu)以及數(shù)據(jù)流

image
1、Action
在flux框架中,都是以事件也就是我們這里談到的action為驅(qū)動,不論是用戶的點擊事件還是我們接收到某一個系統(tǒng)廣播所需做的響應(yīng)
2、Dispatcher
事件的分發(fā)器,它將事件分發(fā)到注冊在其中的每一個store
3、Store
接收事件分發(fā)器發(fā)來的action,并響應(yīng)對應(yīng)的action,將結(jié)果發(fā)射出去
4、View
負(fù)責(zé)展示UI以及響應(yīng)用戶事件并發(fā)送相應(yīng)action

三、Android flux結(jié)構(gòu)以及數(shù)據(jù)流

image

facebook flux框架是為RN設(shè)計的,應(yīng)用在安卓上我做了一些調(diào)整

1、增加Repository,其作用是為某一個業(yè)務(wù)提供其所需要的全部數(shù)據(jù)功能,且將RxJava的生命周期統(tǒng)一管理
2、增加Model,其作用不言而喻,以流的形式提供各種數(shù)據(jù)

四、fluxlib目錄結(jié)構(gòu)

image
1、actions
Action:事件class
BaseActionType:通用的事件類型
ActionCreater:Action的創(chuàng)建器,根據(jù)ActionType以及data即可創(chuàng)建Action的實例
示例:ActionCreater.createAction(BaseActionType.REGISTER,store)//創(chuàng)建將store注冊到Dispatcher的Action
2、bus
Bus:對EventBus的定制化封裝
3、component
3.1、activities
Activity:定義Activity的接口
BaseActivity:基類Activity,主要處理生命周期相關(guān)以及加載View的事情
3.2、repository
Repository:定義Repository的接口
BaseRepository:主要管理Model生命周期
SystemNetRepository:系統(tǒng)網(wǎng)絡(luò)變化的Repository
3.3、stores
Store:定義Store的接口
BaseStore:實現(xiàn)注冊和注銷時的行為以及發(fā)射事件的通用方法
SystemNetStore:系統(tǒng)網(wǎng)絡(luò)變化的Store,在網(wǎng)絡(luò)發(fā)生變化時,發(fā)射相應(yīng)的事件,BaseView會監(jiān)聽這些事件
3.3、views
View:定義View的接口
BaseView:進(jìn)行View的填充以及針對Activity生命周期所做的通用事務(wù)以及類似于顯示toast這類通用View事務(wù)
4、datas
Datas.kt:數(shù)據(jù)類
5、dispatcher
Dispatcher:維護store的注冊表以及分發(fā)Action給所有注冊的store
6、events
BaseDataEvent:帶有數(shù)據(jù)Event的基類
BaseEmptyEvent:無數(shù)據(jù)Event的基類
CommonDataEvents.kt:通用數(shù)據(jù)類
NetConnectedEvent:網(wǎng)絡(luò)連接Event
NoNetConnectedEvent:網(wǎng)絡(luò)斷開的Event
7、views
ConfirmDialog:自定義通用確認(rèn)Dialog
LoadingDialog:自定義loading dialog
8、BaseApp
Application基類

五、如何使用

1、目錄結(jié)構(gòu)
image
2、以一個獲取天氣的Demo App來說明如何使用本框架
1、HomeActivity
class HomeActivity : AbstractActivity() {
    private lateinit var homeStore: HomeStore
    override fun initToolBar() {
        //設(shè)置toolbar相關(guān)屬性
        toolBar.show()
        toolBar.setTitle("當(dāng)日天氣")
        toolBar.showBack()
    }

    override fun initView(savedInstanceState: Bundle?) {
        super.initView(savedInstanceState)
        //初始化HomeView
        val homeView = HomeView(this)
        addView(homeView)
    }

    override fun initData(savedInstanceState: Bundle?) {
        //初始化HomeStore
        homeStore = HomeStore()
        //將homeStore注冊到Dispatcher中
        ActionCreator.createRegisterAction(homeStore)
    }

    override fun releaseResource() {
        //頁面銷毀時,將homeStore從Dispatcher中注銷
        ActionCreator.createUnregisterAction(homeStore)
    }
}
2、HomeView
class HomeView(activity: Activity) : BaseView(activity) {
    override fun getViewLayoutId(): Int {
        //加載布局
        return R.layout.activity_home
    }

    private val btnGetWeatherInfo = realView.findViewById<Button>(R.id.btnGetWeatherInfo)
    private val tvWeatherInfo = realView.findViewById<TextView>(R.id.tvWeatherInfo)

    init {
        //設(shè)置點擊查詢天氣按鈕的監(jiān)聽
        RxView.clicksThrottle1s(btnGetWeatherInfo)
                .subscribe {
                    //發(fā)送GET_WEATHER_INFO Action
                    ActionCreator.createAction(ActionType.GET_WEATHER_INFO, "上海")
                    showLoading()
                }
    }

    private fun showWeatherInfo(weatherInfo: WeatherInfo) {
        tvWeatherInfo.text = weatherInfo.toString()
    }

    //查詢天氣成功
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onWeatherInfoEvent(event: WeatherInfoEvent) {
        Logger.e("onWeatherInfoEvent event: $event")
        hideLoading()
        showWeatherInfo(event.weatherInfo)
    }

    //查詢天氣失敗
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onFluxExceptionEvent(event: FluxExceptionEvent) {
        Logger.e("onFluxExceptionEvent event: $event")
        hideLoading()
        showToastShort(event.fluxException.userMessage)
    }
}
3、HomeStore
class HomeStore : BaseStore() {
    private var homeRepository: HomeRepository? = null
    override fun register() {
        super.register()
        //初始化HomeRepository
        homeRepository = HomeRepository()
    }

    override fun unregister() {
        super.unregister()
        //銷毀HomeRepository
        homeRepository!!.destroy()
        homeRepository = null
    }

    override fun <T> onActionDispatch(type: Int, data: T?) {
        when (type) {
            //接收到GET_WEATHER_INFO Action后從homeRepository獲取天氣信息
            ActionType.GET_WEATHER_INFO -> {
                homeRepository!!.getWeatherInfo(data as String, object : WeatherInfoCallback {
                    override fun onSuccess(weatherInfo: WeatherInfo) {
                        //獲取天氣信息成功,發(fā)射天氣信息
                        postDataEvent(WeatherInfoEvent(weatherInfo))
                    }

                    override fun onError(fluxException: FluxException) {
                        //獲取天氣信息失敗,發(fā)射失敗信息
                        postDataEvent(FluxExceptionEvent(fluxException))
                    }
                })
            }
        }
    }
}
4、HomeRepository
class HomeRepository : BaseRepository() {
    //初始化天氣的Model
    private val weather = Weather()
    fun getWeatherInfo(city: String, weatherInfoCallback: WeatherInfoCallback) {
        //調(diào)用model的方法,進(jìn)行網(wǎng)絡(luò)請求
        weather.getWeatherInfo(city)
                .subscribe(object : FluxDemoSingleObserver<WeatherInfo>() {
                    override fun onSubscribe(d: Disposable) {
                        super.onSubscribe(d)
                        add(d)
                    }

                    override fun onSuccess(t: WeatherInfo) {
                        weatherInfoCallback.onSuccess(t)
                    }

                    override fun onError(e: RxException) {
                        weatherInfoCallback.onError(FluxException(e.msg, "獲取天氣信息失敗,請重試", e.code))
                    }
                })
    }
}
5、Weather
//獲取天氣的Model
class Weather {
    //獲取天氣信息的Single流
    fun getWeatherInfo(city: String): Single<WeatherInfo> {
        return HttpRequest.create(WeatherApi::class.java)
                .getWeatherInfo(city)
                .compose(RxJavaUtil.applySingleMainSchedulers())
                .compose(NetRxJavaUtil.applySingleFeedTransformer())
    }
}

好了,Android Flux框架就介紹到這里,水平有限,還請各位大佬多多指教,如果有什么問題和優(yōu)化建議歡迎給我留言,我會及時回復(fù)大家,謝謝!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容