Android Flux初體驗
前言
- 在一次日常逛知乎的時候發現了Android Flux,相比與之前的開發,自己還從未接觸到這樣的開發模式,于是經過查閱資料后,自己動手嘗試體驗了一番,并加以總結記錄。
簡介
- Flux源于非死book在14年提出的一種Web前端架構,主要是用來處理復雜的UI邏輯的一致性問題。經過實踐后發現,這種架構可以很好的應用于Android平臺,相對于其他的MVC/MVP/MVVM等模式,擁有良好的文檔和更具體的設計,比較適合于快速開發實現。
核心思想
單向數據流
Android Flux
- 如圖所示,Flux的核心思想是單向的數據流,所謂單向數據流,就是當用戶進行操作的時候,會從View層發出一個Action,這個Action通過Dispatcher流向Store里面,觸發Store對狀態進行改動,然后再由Store觸發新的狀態通知到View進行重新渲染。
Action
- 用戶操作界面時會觸發一個Action,這個Action即是數據的封裝,Dispatcher會將這個Action分發到Store
- Action的創建一般被封裝到一個有語義的Helper類,( ActionCreator ),所謂有語義,就是根據不同的業務創建不同的Action,然后把這個Action傳遞到Dispatcher,進行Action的分發
Dispatcher
- 一個應用中只有一個Dispatcher,Dispatcher管理所有的數據流,(即內部管理的是所有的Store),它是Action與Store聯系的中心樞紐
- 實際上,它管理的是Store注冊的一系列回調接口,本身沒有其他的邏輯,僅僅是把Action分發到Store
- 由此可知,所有的數據流必須從這里經過,依次分發到已注冊的Store中
Store
- Store包含應用的狀態和邏輯,通常在此類中實現對Action的邏輯處理,并把處理的結果通知到View中
- Store會把自己注冊在Dispatcher上并提供一個回調的接口,用于處理Action,當Dispatcher分發Action時,內部管理的Store就會回調這個用于處理Action的接口
- 通過Dispatcher發送Action來更新Store的內部狀態,當Store更新后,它會發送一個事件聲明自己的狀態已經發送了改變,(通常是View接收),然后View會讀取這些變化并更新自己
View
- 即是Activity和Fragment,負責監聽Store發送的事件并更新界面
- 通常一個Activity對應一個Store,但是如果Activity包含許多Fragment,也可以讓每個Fragment對應自己的Store
代碼實踐
- 本項目模擬了一個登陸的過程,Activity由兩個EditText和一個Button組成,當username和password均為123時,模擬登陸成功;否則模擬登陸失敗。
Flux Test
Action
/**
- 簡單的POJO類型,只提供兩個字段:type 和 data, 分別記錄Action的類型和數據
Created by ckerv on 16/12/4.
*/
public class Action<T> {
private String type;
private T data;
public Action(String type, T data) {
this.type = type;
this.data = data;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}</code></pre>
/**
Created by ckerv on 16/12/5.
*/
public class LoginAction extends Action<LoginBean> {
public static final String LOGIN_ACTION = "login.action";
public LoginAction(String type, LoginBean data) {
super(type, data);
}
}</code></pre>
- Action是簡單的POJO類型,采用泛型對數據進行封裝,只提供了兩個字段 type 和 data ,分別記錄Action的類型和數據
- LoginAction是Action的具體業務實現,實現非常簡單,只添加了一個Action類型字段 LOGIN_ACTION
Dispatcher
/**
- 一個app通常只有一個Dispatcher類,內部進行對Store的管理
- 此類的作用是將action分發到store,是連接action和store的中心樞紐
Created by ckerv on 16/12/4.
*/
public class Dispatcher {
private static Dispatcher INSTANCE;
private List<Store> mStores;
private Dispatcher() {
mStores = new ArrayList<>();
}
public static Dispatcher getInstance() {
if(INSTANCE == null) {
synchronized (Dispatcher.class) {
if(INSTANCE == null) {
INSTANCE = new Dispatcher();
}
}
}
return INSTANCE;
}
/**
- 注冊store,即把store添加到list中
@param store
*/
public void register(Store store) {
this.mStores.add(store);
}
/**
- 注銷store,從list中移除
@param store
*/
public void unRegister(Store store) {
this.mStores.remove(store);
}
/**
- 分發action
@param action
*/
public void dispatch(Action action) {
post(action);
}
/**
- 向注冊的store list依次分發acion
- @param action
*/
private void post(Action action) {
for (Store store : mStores) {
store.onAction(action);
}
}
}</code></pre>
- Store會在這里注冊自己的回調接口,Dispatcher會把Action分發到注冊的Store,所以它會提供一些公有方法來注冊監聽和分發消息
- Dispatcher對外僅暴露3個公有方法:
- register(final Store store) 用來注冊每個Store的回調接口
- unregister(final Store store) 用來接觸Store的回調接口
- dispatch(Action action) 用來觸發Store注冊的回調接口
</li>
- 用一個ArrayList來管理Stores,對于一個更復雜的app可能需要精心設計store所管理的數據結構
</ul>
Store
/**
- 處理action的基類,通常可根據不同的業務邏輯實現{@link #onAction(Action)}
- 通過EventBus傳遞數據
Created by ckerv on 16/12/4.
*/
public abstract class Store {
private static final EventBus mBus = EventBus.getDefault();
protected Store() {
}
public void register(Object view) {
this.mBus.register(view);
}
public void unRegister(Object view) {
this.mBus.unregister(view);
}
/**
- post事件到view層,進行UI的后續處理
*/
protected void emitStoreChange() {
this.mBus.post(changeEvent());
}
/**
* 處理Action的邏輯,子類必須實現此方法
* @param action
*/
public abstract void onAction(Action action);
/**
* post事件
* @return
*/
protected abstract StoreChangeEvent changeEvent();
public class StoreChangeEvent{}
}</code></pre>
- 這里Store是個抽象類,處理具體業務邏輯的子類必須實現 onAction(Action action) 和 changeEvent() 方法
- 采用EventBus來向view發送事件,因此必須提供 register(Object view) 和 unRegister(Object view) 方法給外部注冊和注銷EventBus
LoginStore
/**
Created by ckerv on 16/12/5.
*/
public class LoginStore extends Store {
public LoginStore() {
super();
loginResponseBean = new LoginResponseBean();
}
private LoginResponseBean loginResponseBean;
public LoginResponseBean getLoginResponseBean() {
return loginResponseBean;
}
@Override
public void onAction(Action action) {
switch (action.getType()) {
case LoginAction.LOGIN_ACTION:
LoginBean loginBean = (LoginBean) action.getData();
if(loginBean.getUserName().equals("123") && loginBean.getPassWord().equals("123")) {
loginResponseBean.setSuccess(true);
} else {
loginResponseBean.setSuccess(false);
}
break;
default:break;
}
emitStoreChange();
}
@Override
protected StoreChangeEvent changeEvent() {
return new StoreChangeEvent();
}
}</code></pre>
- 處理登錄邏輯的LoginStore類,繼承自Store,可以看到,在 onAction(Action) 進行登錄邏輯的驗證
- 在登錄邏輯的處理過程中,通過改變 LoginReponseBean 的狀態來區分登錄成功與否,處理完邏輯之后,調用父類的 emitStoreChange() 方法發送事件通知view更新
- 請注意,這里只提供LoginReponseBean的 get 方法,Store內部狀態的更新只能是通過Dispatcher改變
MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText mEtUsername;
private EditText mEtPassword;
private Button mBtnLogin;
private ActionCreator mActionCreator;
private Dispatcher mDispatcher;
private LoginStore mStore;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initVariables();
initViews();
}
private void initVariables() {
mStore = new LoginStore();
mDispatcher = Dispatcher.getInstance();
mActionCreator = ActionCreator.getInstance(mDispatcher);
mDispatcher.register(mStore);
}
@Override
protected void onResume() {
super.onResume();
mStore.register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mStore.unRegister(this);
}
private void initViews() {
mEtUsername = (EditText) findViewById(R.id.main_et_username);
mBtnLogin = (Button) findViewById(R.id.main_btn_login);
mEtPassword = (EditText) findViewById(R.id.main_et_password);
mBtnLogin.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.main_btn_login :
mActionCreator.login(mEtUsername.getText().toString(), mEtPassword.getText().toString());
break;
default : break;
}
}
@Subscribe
public void onChangeEvent(Store.StoreChangeEvent changeEvent) {
if(mStore.getLoginResponseBean().isSuccess()) {
Toast.makeText(MainActivity.this, "登錄成功", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "登錄失敗", Toast.LENGTH_LONG).show();
}
}
}</code></pre>
- 一般說來,一個Activity有多少個業務就有多少個Store
- 在 initVariables() 進行Store、Dispatcher和ActionCreator的初始化和注冊
- 在 onResume() 和 onDestroy() 方法里進行EventBus的注冊和注銷
- 當用戶點擊登錄按鈕時,由ActionCreator調用具體的業務方法 login(String username, String password) 使Dispatcher分發LoginAction到LoginStore,當LoginStore處理完LoginAction發送事件到MainActivity,MainActivity層接收到事件,獲取LoginStore的狀態,更新界面。
流程總結

流程總結
小結
- 以上是我對Android Flux開發模式的第一次實踐,可以看到,采用Android Flux的開發模式有如下好處:
- View層只負責渲染界面和觸發Action,具體業務邏輯由Store實現,高度解耦
- 要理解一個Store可能發生的狀態變化,只看 onAction(Action action) 中的邏輯處理即可
- 由于數據(Action)是單向流動,因此Debug的時候變得輕松很多,可以快速定位Bug的發生地點
- 以上是個人對于Anroid Flux的理解 ,有不足之處望指出一起討論,一起學習~O(∩_∩)O哈哈
關于Android Flux
來自:http://www.jianshu.com/p/31a071f6bc62