Android Flux初体验

sky12l 8年前
   <h2>前言</h2>    <ul>     <li>在一次日常逛知乎的时候发现了Android Flux,相比与之前的开发,自己还从未接触到这样的开发模式,于是经过查阅资料后,自己动手尝试体验了一番,并加以总结记录。</li>    </ul>    <h2>简介</h2>    <ul>     <li>Flux源于非死book在14年提出的一种Web前端架构,主要是用来处理复杂的UI逻辑的一致性问题。经过实践后发现,这种架构可以很好的应用于Android平台,相对于其他的MVC/MVP/MVVM等模式,拥有良好的文档和更具体的设计,比较适合于快速开发实现。</li>    </ul>    <h2>核心思想</h2>    <h2>单向数据流</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fa8b3f8b8604bde3ceab86283f825764.png"></p>    <p>Android Flux</p>    <ul>     <li>如图所示,Flux的核心思想是单向的数据流,所谓单向数据流,就是当用户进行操作的时候,会从View层发出一个Action,这个Action通过Dispatcher流向Store里面,触发Store对状态进行改动,然后再由Store触发新的状态通知到View进行重新渲染。</li>    </ul>    <h2>Action</h2>    <ul>     <li>用户操作界面时会触发一个Action,这个Action即是数据的封装,Dispatcher会将这个Action分发到Store</li>     <li>Action的创建一般被封装到一个有语义的Helper类,( <strong>ActionCreator</strong> ),所谓有语义,就是根据不同的业务创建不同的Action,然后把这个Action传递到Dispatcher,进行Action的分发</li>    </ul>    <h2>Dispatcher</h2>    <ul>     <li>一个应用中只有一个Dispatcher,Dispatcher管理所有的数据流,(即内部管理的是所有的Store),它是Action与Store联系的中心枢纽</li>     <li>实际上,它管理的是Store注册的一系列回调接口,本身没有其他的逻辑,仅仅是把Action分发到Store</li>     <li>由此可知,所有的数据流必须从这里经过,依次分发到已注册的Store中</li>    </ul>    <h2>Store</h2>    <ul>     <li>Store包含应用的状态和逻辑,通常在此类中实现对Action的逻辑处理,并把处理的结果通知到View中</li>     <li>Store会把自己注册在Dispatcher上并提供一个回调的接口,用于处理Action,当Dispatcher分发Action时,内部管理的Store就会回调这个用于处理Action的接口</li>     <li>通过Dispatcher发送Action来更新Store的内部状态,当Store更新后,它会发送一个事件声明自己的状态已经发送了改变,(通常是View接收),然后View会读取这些变化并更新自己</li>    </ul>    <h2>View</h2>    <ul>     <li>即是Activity和Fragment,负责监听Store发送的事件并更新界面</li>     <li>通常一个Activity对应一个Store,但是如果Activity包含许多Fragment,也可以让每个Fragment对应自己的Store</li>    </ul>    <h2>代码实践</h2>    <ul>     <li>本项目模拟了一个登陆的过程,Activity由两个EditText和一个Button组成,当username和password均为123时,模拟登陆成功;否则模拟登陆失败。</li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9bb2fa2680d2e0b145ce83662151ebb0.jpg"></p>    <p>Flux Test</p>    <h2>Action</h2>    <pre>  <code class="language-java">/**   * 简单的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>    <pre>  <code class="language-java">/**   * 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>    <ul>     <li>Action是简单的POJO类型,采用泛型对数据进行封装,只提供了两个字段 type 和 data ,分别记录Action的类型和数据</li>     <li>LoginAction是Action的具体业务实现,实现非常简单,只添加了一个Action类型字段 LOGIN_ACTION</li>    </ul>    <h2>Dispatcher</h2>    <pre>  <code class="language-java">/**   * 一个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>    <ul>     <li>Store会在这里注册自己的回调接口,Dispatcher会把Action分发到注册的Store,所以它会提供一些公有方法来注册监听和分发消息</li>     <li>Dispatcher对外仅暴露3个公有方法:      <ul>       <li>register(final Store store) 用来注册每个Store的回调接口</li>       <li>unregister(final Store store) 用来接触Store的回调接口</li>       <li>dispatch(Action action) 用来触发Store注册的回调接口</li>      </ul> </li>     <li>用一个ArrayList来管理Stores,对于一个更复杂的app可能需要精心设计store所管理的数据结构</li>    </ul>    <h2>Store</h2>    <pre>  <code class="language-java">/**   * 处理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>    <ul>     <li>这里Store是个抽象类,处理具体业务逻辑的子类必须实现 onAction(Action action) 和 changeEvent() 方法</li>     <li>采用EventBus来向view发送事件,因此必须提供 register(Object view) 和 unRegister(Object view) 方法给外部注册和注销EventBus</li>    </ul>    <h2>LoginStore</h2>    <pre>  <code class="language-java">/**   * 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>    <ul>     <li>处理登录逻辑的LoginStore类,继承自Store,可以看到,在 onAction(Action) 进行登录逻辑的验证</li>     <li>在登录逻辑的处理过程中,通过改变 LoginReponseBean 的状态来区分登录成功与否,处理完逻辑之后,调用父类的 emitStoreChange() 方法发送事件通知view更新</li>     <li>请注意,这里只提供LoginReponseBean的 get 方法,Store内部状态的更新只能是通过Dispatcher改变</li>    </ul>    <h2>MainActivity</h2>    <pre>  <code class="language-java">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>    <ul>     <li>一般说来,一个Activity有多少个业务就有多少个Store</li>     <li>在 initVariables() 进行Store、Dispatcher和ActionCreator的初始化和注册</li>     <li>在 onResume() 和 onDestroy() 方法里进行EventBus的注册和注销</li>     <li>当用户点击登录按钮时,由ActionCreator调用具体的业务方法 login(String username, String password) 使Dispatcher分发LoginAction到LoginStore,当LoginStore处理完LoginAction发送事件到MainActivity,MainActivity层接收到事件,获取LoginStore的状态,更新界面。</li>    </ul>    <h2>流程总结</h2>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/12ac5767830ebe98ff75a2e446bef402.jpg"></p>    <p>流程总结</p>    <h2>小结</h2>    <ul>     <li>以上是我对Android Flux开发模式的第一次实践,可以看到,采用Android Flux的开发模式有如下好处:      <ul>       <li>View层只负责渲染界面和触发Action,具体业务逻辑由Store实现,高度解耦</li>       <li>要理解一个Store可能发生的状态变化,只看 onAction(Action action) 中的逻辑处理即可</li>       <li>由于数据(Action)是单向流动,因此Debug的时候变得轻松很多,可以快速定位Bug的发生地点</li>      </ul> </li>     <li>以上是个人对于Anroid Flux的理解 ,有不足之处望指出一起讨论,一起学习~O(∩_∩)O哈哈</li>    </ul>    <h2>关于Android Flux</h2>    <p> </p>    <p>来自:http://www.jianshu.com/p/31a071f6bc62</p>    <p> </p>