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())
}
}