Flux在Android中的使用

Flux.png

今天想和大家分享一下Flux,F(xiàn)lux是Facebook用來(lái)構(gòu)建Web應(yīng)用的架構(gòu),目前在React中被廣泛使用。有這幾點(diǎn)需要注意,F(xiàn)lux是模式而不是框架,是單向數(shù)據(jù)流,是MVC最好的實(shí)踐。 至于為什么要使用Flux,我們可以看一個(gè)Facebook遇到的問(wèn)題。

Facebook.png

一開(kāi)始,產(chǎn)品需求是聊天信息在右下角的chat View顯示,如果用戶沒(méi)有點(diǎn)擊,右上角出現(xiàn)紅色標(biāo)示,后來(lái)產(chǎn)品需要在中間的View中顯示,如果已經(jīng)被用戶看到,則右上角不出現(xiàn)紅色標(biāo)示,一段時(shí)間之后,產(chǎn)品又需要在左邊新增一個(gè)通訊錄列表,可以顯示最近的聊天信息,如果用戶看到,則右上角不出現(xiàn)紅色標(biāo)示。(沒(méi)有看錯(cuò),就是如此讓人不省心,哈哈。)
產(chǎn)品結(jié)構(gòu)如下:

Full System.png

然后我們看下代碼:

Chat Code.png

我們可以看到,隨著產(chǎn)品需求不斷地累加,代碼塊越來(lái)越臃腫,很不利于維護(hù),于是,F(xiàn)acebook推出了Flux,我們來(lái)看下Flux結(jié)構(gòu):

Flux結(jié)構(gòu)

各模塊的介紹:

  1. View:視圖層
  2. Action(動(dòng)作):視圖層發(fā)出的消息
  3. Dispatcher(派發(fā)器):接收、派遣Action
  4. Store(數(shù)據(jù)層):接受派遣的Action,執(zhí)行業(yè)務(wù)邏輯,提醒Views更新
流程圖.png

我們來(lái)看看在Android中如何使用,例子是一個(gè)TODO App,輸入Todo內(nèi)容,點(diǎn)擊添加按鈕,顯示輸入的Todo列表:

ToDo_App.png

TodoActionCreator

Action生成器,創(chuàng)建CREATE和GET_ALL Action

public class TodoActionCreator {    
public static final String ID = "id";    
public static final String TEXT = "text";    
public static final int ACTION_CREATE = 0;    
public static final int ACTION_GET_ALL = 1;    
private Dispatcher dispatcher;    
......
public void create(String text) {        
  dispatcher.dispatch(ACTION_CREATE, TEXT, text);    
  }    
public void getAll(){        
  dispatcher.dispatch(ACTION_GET_ALL);    
  }
}

Dispatcher

public class Dispatcher {    
private static Dispatcher dispatcher;    
private Store.OnStoreChangeListener onStoreChangeListener;    
public Dispatcher(Store.OnStoreChangeListener onStoreChangeListener) {
  this.onStoreChangeListener = onStoreChangeListener;    
  }    
......
public void dispatch(int type, Object... data){        
  HashMap<String, Object> hashMap = new HashMap<>();        
  int i = 0;        
  while (i < data.length) {            
  String key = (String) data[i++];            
  Object value = data[i++];            
  hashMap.put(key, value);        
  }        
  Action action = new Action(type, hashMap);        
  if (!isTodoStore(action)) {            
  return;        
  }        
  Store store;        
  if (isTodoStore(action)) {            
  store = new TodoStore(onStoreChangeListener); 
  store.onAction(new Action(type, hashMap));        
  }   
  }    
  private boolean isTodoStore(Action action) {        
  return action.getType() == TodoActionCreator.ACTION_GET_ALL || action.getType() == TodoActionCreator.ACTION_CREATE;    
  }
}

TodoStore

public class TodoStore extends Store{    
public TodoStore(OnStoreChangeListener onStoreChangeListener) {        
  super(onStoreChangeListener);    
  }    
@Override    
public void onAction(Action action) {        
  switch (action.getType()){            
  case TodoActionCreator.ACTION_CREATE:                
  create((String) action.getHashMap().get(TodoActionCreator.TEXT)); 
  emitStoreChange();                
  break;            
  case TodoActionCreator.ACTION_GET_ALL:                
  emitStoreChange();                
  break;        
  }    
  }    
@Override    
StoreChangeEvent changeEvent() {        
  StoreChangeEvent storeChangeEvent = new StoreChangeEvent();          
  storeChangeEvent.value = TodoRepositories.getTodos();        
  return storeChangeEvent;    
  }    
private void create(String text) {          
  TodoRepositories.getTodos().add(new Todo(new Random().nextInt(), text));    
  }
}

MainActivity

public class MainActivity extends AppCompatActivity implements Store.OnStoreChangeListener, View.OnClickListener{    
private RecyclerView rvContent;    
private EditText etTodo;    
private Button btnAdd;    
private ContentAdapter mContentAdapter;    
private TodoActionCreator todoActionCreator;    
@Override    
protected void onCreate(Bundle savedInstanceState) {          
  super.onCreate(savedInstanceState);          
  setContentView(R.layout.activity_main);        
  rvContent = (RecyclerView) findViewById(R.id.rvContent);          
  rvContent.setLayoutManager(new LinearLayoutManager(this));//這里用線性顯示 類似于listview        
  etTodo = (EditText) findViewById(R.id.etTodo);        
  btnAdd = (Button) findViewById(R.id.btnAdd);          
  btnAdd.setOnClickListener(this);        
  mContentAdapter = new ContentAdapter(new ArrayList<Todo>(),this);          
  rvContent.setAdapter(mContentAdapter);        
  todoActionCreator = TodoActionCreator.get(Dispatcher.get(this));          
  todoActionCreator.getAll();    
}    
@Override    
public void OnStoreChange(Store.StoreChangeEvent storeChangeEvent) {          
  mContentAdapter.update((ArrayList<Todo>) storeChangeEvent.value);            
  mContentAdapter.notifyDataSetChanged();    }    
@Override    
public void onClick(View v) {        
  switch (v.getId()){            
  case R.id.btnAdd:                
  String text = etTodo.getText().toString();                  
  todoActionCreator.create(text);                
  etTodo.setText("");                
  break;        
  }    
}}

代碼鏈接: http://pan.baidu.com/s/1c1oovss 密碼: 9jnk

VS MVC

我們可以拿MVC比較一下,理想中MVC如下:

MVC_理想.png

但在使用過(guò)程,一不小心就增加了View和Model之間的交互,導(dǎo)致Model和View之間的錯(cuò)綜復(fù)雜,不利于維護(hù)和測(cè)試。

MVC_現(xiàn)實(shí).png
MVC結(jié)構(gòu).png

而Flux則可以維持?jǐn)?shù)據(jù)的單向性,如下:

Flux結(jié)構(gòu).png

總結(jié)

Flux優(yōu)點(diǎn)

  1. Improved data consistency 改善數(shù)據(jù)統(tǒng)一
  2. Easier to pinpoint root of bug 方便找到bug
  3. More meaningful unit tests 產(chǎn)生更多有意義的單元測(cè)試

Flux缺點(diǎn)

  1. Dispatcher 容易臃腫,復(fù)雜
  2. 代碼過(guò)于模板化
  3. 移植現(xiàn)有代碼比較困難

參考

Flux開(kāi)發(fā)文檔

原始地址: http://m.itdecent.cn/p/d6f9d2ee64a2,歡迎關(guān)注我的微博

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

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評(píng)論 25 708
  • Android項(xiàng)目做了不少,難免遇到因?yàn)樵陧?xiàng)目架構(gòu)上設(shè)計(jì)不合理或者根本沒(méi)有形成統(tǒng)一的編程思想,導(dǎo)致各種意外的情況出...
    Forrest32閱讀 6,219評(píng)論 11 34
  • 在一處懸崖邊上,住著一群狼,但這兒的狼王是母狼,而并非是公狼。 這事情的原因要從半年前的意外說(shuō)起了。 那是一個(gè)寒風(fēng)...
    孤櫻落_淡秋離閱讀 587評(píng)論 1 3
  • 很久沒(méi)有寫(xiě)字了,今天卻突然有了寫(xiě)點(diǎn)什么的沖動(dòng)。大學(xué)一年來(lái)其實(shí)什么收獲都沒(méi)有,卻還在混混度日,有想做什么的沖動(dòng),卻一...
    安塢閱讀 681評(píng)論 0 0
  • 戀戀風(fēng)塵練練新
    一一無(wú)痕閱讀 326評(píng)論 44 22

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