Android學(xué)習(xí)之Flux架構(gòu)入門
Flux 架構(gòu)介紹
Flux 架構(gòu) 被Facebook使用來構(gòu)建他們的客戶端web應(yīng)用。跟Clean Architecture一樣,它不是為移動(dòng)應(yīng)用設(shè)計(jì)的,但是它的特性和簡單可以讓我們很好的在安卓項(xiàng)目中采用。
Flux模式最大的特點(diǎn)是單向的數(shù)據(jù)流,它的UI狀態(tài)更新模式繼承了MVC模式的設(shè)計(jì)思想。Flux并不是具體的框架,而是一套處理UI問題的模式,Android Flux同樣不是具體的框架,你不需要導(dǎo)入或者集成任何新的代碼就可以使用,而你需要做的事情是了解這套思想、遵循這種開發(fā)模式,查看我們提供的Android代碼示例,寫自己的代碼。
要理解Flux,有兩個(gè)關(guān)鍵的特點(diǎn)
1、數(shù)據(jù)流總是單向的
一個(gè)單向的數(shù)據(jù)流 是 Flux 架構(gòu)的核心,也是它簡單易學(xué)的原因。就如下面討論的,在進(jìn)行應(yīng)用測試的時(shí)候,它提供了非常大的幫助。
2、應(yīng)用被分成三個(gè)主要部分:
. View: 應(yīng)用的界面。這里創(chuàng)建響應(yīng)用戶操作的action
。
. Dispatcher: 中心樞紐,傳遞所有的action
,負(fù)責(zé)把它們運(yùn)達(dá)每個(gè)Store
。
. Store: 維護(hù)一個(gè)特定application domain
的狀態(tài)。它們根據(jù)當(dāng)前狀態(tài)響應(yīng)action
,執(zhí)行業(yè)務(wù)邏輯,同時(shí)在完成的時(shí)候發(fā)出一個(gè)change
事件。這個(gè)事件用于view
更新其界面。
這三個(gè)部分都是通過Action
來通信的:一個(gè)簡單的基本對象,以類型來區(qū)分,包含了和操作相關(guān)的數(shù)據(jù)。
Flux Android 架構(gòu)
在安卓開發(fā)中使用Flux設(shè)計(jì)規(guī)范的目的是建立一個(gè)在簡單性與易擴(kuò)展易測試之間都比較平衡的架構(gòu)。
第一步是找到Flux元素和安卓app組件之間的映射。
其中兩個(gè)元素非常容易找到與實(shí)現(xiàn)。
View: Activity o
或者Fragment
Dispatcher:
一個(gè)事件總線( event bus),在我的例子中將使用Otto,但是其它任何實(shí)現(xiàn)都應(yīng)該是ok的。
Actions
Actions
也不復(fù)雜。它們的實(shí)現(xiàn)和POJO
一樣簡單,有兩個(gè)主要屬性:
1、Type: 一個(gè)String,定義了事件的類型。
2、Data: 一個(gè)map,裝載了本次操作。
Store是Flux理論中最難的部分。
Stores
響應(yīng)Dispatcher
發(fā)出的Action
,執(zhí)行業(yè)務(wù)邏輯并發(fā)送change
事件。Stores
的唯一輸出是這單一的事件:change
。其它對Store
內(nèi)部狀態(tài)感興趣的組件必須監(jiān)聽這個(gè)事件,同時(shí)使用它獲取需要的數(shù)據(jù)。最后,stores
必須對外公開一個(gè)獲取application
狀態(tài)的接口。這樣,view
元素可以查詢Stores
然后相應(yīng)的更新UI。
這里通過一個(gè)簡單的小demo來講述整個(gè)流程。我們的界面上有一個(gè)Button
和一個(gè)TextView
,點(diǎn)擊Button
后讓TextView
顯示出文字。常規(guī)的實(shí)現(xiàn),直接在Activity
中完成邏輯,MVP
模式,在Presenter
層來進(jìn)行,對于Flux架構(gòu),我們要怎么實(shí)現(xiàn)呢。通過上圖我們可以看到,View
會(huì)產(chǎn)生Action
,然后被Dispatcher
進(jìn)行調(diào)度,經(jīng)過Store
相應(yīng)處理,將數(shù)據(jù)顯示出來。
如何產(chǎn)生Action
首先要知道Action
是什么樣
public class Action { private final String type; private final HashMap<String, Object> data; public Action(String type, HashMap<String, Object> data) { this.type = type; this.data = data; } public static Builder type(String type) { return new Builder().with(type); } public String getType() { return type; } public HashMap getData() { return data; } public static class Builder { private String type; private HashMap<String, Object> data; Builder with(String type) { if(type == null) { throw new IllegalArgumentException("Type may not be null."); } this.type = type; this.data = new HashMap<>(); return this; } public Builder bundle(String key, Object value) { if (key == null) { throw new IllegalArgumentException("Key may not be null."); } if(value == null) { throw new IllegalArgumentException("Value may not be null."); } data.put(key, value); return this; } public Action build() { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("At least one key is required."); } return new Action(type, data); } } }
每一個(gè)Action
有兩個(gè)屬性,一個(gè)來標(biāo)記Type
,另一個(gè)字段來存儲(chǔ)傳送的數(shù)據(jù),通過Map
來存放。
對于Action Type
,我們可以通過一個(gè)接口或者類來進(jìn)行記錄,將所有的類型保存在其中。方便我們的調(diào)用。
public interface ShowActions { String TODO_SHOW = "todo-show"; String GET_TEXT = "get-text"; }
如何創(chuàng)建Action
,定義一個(gè)類,專門用來根據(jù)我們可能會(huì)出現(xiàn)的各種View
的事件,定義出來各種Action
。
public class ActionsCreator { private static ActionsCreator instance; final Dispatcher mDispatcher; ActionsCreator(Dispatcher dispatcher){ mDispatcher = dispatcher; } public static ActionsCreator get(Dispatcher dispatcher) { if (instance == null) { instance = new ActionsCreator(dispatcher); } return instance; } public void create(String text) { mDispatcher.dispatch(ShowActions.TODO_SHOW, ShowActions.GET_TEXT, text); }
在我們準(zhǔn)備用ActionsCreator
來創(chuàng)建Action
的時(shí)候,我們并沒有直接new Action
這種方式來做,而是將其通過調(diào)度器,對其進(jìn)行了分發(fā)。這里的事件分發(fā),我們使用的是Otto
的Bus
來進(jìn)行事件的分發(fā)。
public class Dispatcher { private final Bus bus; private static Dispatcher instance; Dispatcher(Bus bus){ this.bus = bus; } public static Dispatcher get(Bus bus) { if (instance == null) { instance = new Dispatcher(bus); } return instance; } public void register(final Object cls) { bus.register(cls); } public void unRegister(final Object cls) { bus.unregister(cls); } public void emitChange(Store.StoreChangeEvent o) {post(o);} public void dispatch(String type, Object... data) { if(TextUtils.isEmpty(type)) { throw new IllegalArgumentException("Type must not be empty"); } if (data.length % 2 != 0) { throw new IllegalArgumentException("Data must be a valid list of key"); } Action.Builder actionBuilder = Action.type(type); for (int i = 0; i < data.length; i++) { String key = (String) data[i++]; Object value = data[i++]; actionBuilder.bundle(key, value); } post(actionBuilder.build()); } private boolean isEmpty(String type) { return TextUtils.isEmpty(type); } private void post(final Object event) { bus.post(event); } }
在調(diào)度的過程中,我們將傳遞進(jìn)來的數(shù)據(jù)進(jìn)行一個(gè)解析,然后根據(jù)數(shù)據(jù)創(chuàng)建出相應(yīng)的Action
,然后對Action
進(jìn)行分發(fā),這個(gè)時(shí)候關(guān)注了相應(yīng)的Action
的Store
就會(huì)開始根據(jù)相應(yīng)的Action
開始執(zhí)行相應(yīng)的操作。在Store
中,聲明了一個(gè)抽象方法onAction
來負(fù)責(zé)進(jìn)行對于Action
的判斷和分發(fā),然后定義了StoreChangeEvent
接口作為事件變化,當(dāng)有變化的時(shí)候,通過這個(gè)進(jìn)行傳遞,我們可以自己實(shí)現(xiàn)這個(gè)接口,然后在里面添加一些方法和字段用來攜帶數(shù)據(jù)。
public abstract class Store { final Dispatcher mDispatcher; protected Store(Dispatcher dispatcher) { this.mDispatcher = dispatcher; } void emitStoreChange() { mDispatcher.emitChange(changeEvent()); } abstract StoreChangeEvent changeEvent(); public abstract void onAction(Action action); public interface StoreChangeEvent {} }
我們自定義的Store類
public class ShowStore extends Store { private static ShowStore instance; private String showText; public ShowStore(Dispatcher dispatcher){ super(dispatcher); } public static ShowStore get(Dispatcher dispatcher) { if (instance == null) { instance = new ShowStore(dispatcher); } return instance; } @Subscribe public void onAction(Action action) { switch (action.getType()) { case ShowActions.TODO_SHOW : showText = ((String)action.getData().get(ShowActions.GET_TEXT)); Log.i("showText", showText); emitStoreChange(); break; default: break; } } public String getShowText(){ return showText; } @Override StoreChangeEvent changeEvent() { return new ShowChangeEvent(); } public class ShowChangeEvent implements StoreChangeEvent { } }
然后我們在View
也就是Activity
中訂閱了變化時(shí)間的方法,這個(gè)時(shí)候就可以實(shí)現(xiàn)對于View
中的數(shù)據(jù)的一個(gè)動(dòng)態(tài)更新。
@Subscribe public void showText (ShowStore.ShowChangeEvent event){ mTextView.setText(mShowStore.getShowText()); }
總結(jié)
通過Flux架構(gòu),使用的流程是,我們的View的事件會(huì)攜帶數(shù)據(jù),通過一個(gè)ActionsCreate創(chuàng)建一個(gè)Type的Action,實(shí)際完成過程是在Dispatcher的dispatch中,然后再將這個(gè)Action丟給訂閱了該Action的Store方法中,在這里完成各種邏輯,處理,甚至是可以發(fā)起網(wǎng)絡(luò)請求獲取數(shù)據(jù),處理完成,可以將結(jié)果封裝成一個(gè)事件,然后這個(gè)事件會(huì)再次通過調(diào)度器中的emitChangeEvent將事件傳遞給訂閱了該事件的函數(shù),而這個(gè)接收響應(yīng)事件的函數(shù)被我們定義在我們View中,從而實(shí)現(xiàn)對于我們View的更新。以上就是本文的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)Flux架構(gòu)有所幫助。
相關(guān)文章
Android實(shí)現(xiàn)帶有進(jìn)度條的按鈕效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)帶有進(jìn)度條的按鈕效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05Android自定義控件實(shí)現(xiàn)萬能的對話框
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)萬能對話框的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android中一個(gè)應(yīng)用實(shí)現(xiàn)多個(gè)圖標(biāo)的幾種方式
這篇文章主要給大家介紹了在Android中一個(gè)應(yīng)用如何實(shí)現(xiàn)多個(gè)圖標(biāo)的幾種方式,其中包括了多Activity + intent-filter方式、activity-alias方式以及網(wǎng)頁標(biāo)簽-添加快捷方式,分別給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒。2017-05-05Android中判斷l(xiāng)istview是否滑動(dòng)到頂部和底部的實(shí)現(xiàn)方法
下面小編就為大家分享一篇Android中判斷l(xiāng)istview是否滑動(dòng)到頂部和底部的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11Android開發(fā)之獲取短信驗(yàn)證碼后按鈕背景變化并且出現(xiàn)倒計(jì)時(shí)
在開發(fā)是經(jīng)常會(huì)遇到獲取短信驗(yàn)證碼,然后獲取驗(yàn)證碼后需要等待n秒倒計(jì)時(shí),這時(shí)是不能再次發(fā)送短信請求的,這是需要一個(gè)倒計(jì)時(shí)程序,本文給大家分享了實(shí)現(xiàn)此功能的代碼,需要的朋友參考下2016-01-01Android 實(shí)現(xiàn)圓圈擴(kuò)散水波動(dòng)畫效果兩種方法
這篇文章主要介紹了Android 實(shí)現(xiàn)圓圈擴(kuò)散水波動(dòng)畫效果兩種方法,需要的朋友可以參考下2018-05-05Android開發(fā)實(shí)現(xiàn)SubMenu選項(xiàng)菜單和子菜單示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)SubMenu選項(xiàng)菜單和子菜單,結(jié)合實(shí)例形式分析了Android開發(fā)中SubMenu選項(xiàng)菜單和子菜單的功能、配置、布局等相關(guān)操作技巧,需要的朋友可以參考下2019-03-03Android開發(fā)仿IOS滑動(dòng)開關(guān)實(shí)現(xiàn)代碼
這篇文章主要介紹了 android開發(fā)仿IOS滑動(dòng)開關(guān)實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05Android WebView開發(fā)之WebView與Native交互
隨著H5的廣泛使用,Android開發(fā)過程中免不了會(huì)使用網(wǎng)頁來做展示,那么web與native之間的通信就顯得尤其重要了,其實(shí)際上是JavaScript與java之間的通信。本文將為大家詳細(xì)介紹二者是如何實(shí)現(xiàn)交互的,需要的朋友可以參考一下2021-12-12