Android项目做了不少,难免遇到因为在项目架构上设计不合理或者根本没有形成统一的编程思想,导致各种意外的情况出现,最近新项目开始了,笔者就想着上网搜搜其他大神的一些项目架构搭建的资料然后结合自己的经验来搭建一个比较适合自己团队开发的框架,经过一段时间的酝酿,重新看了下mvc,mvp,mvvc思想,又了解了clean architecture,flux等等比较具体点的架构最终决定用Flux架构来搭建。
关于选用Flux作为项目框架的原因主要有以下两点:
- 逻辑比较清晰,比较容易使用和维护,也方便测试代码。
- Facebook出品,笔者还是觉得可以信赖的,嘻嘻~
好了,废话不多说,接下笔者会结合自己写的demo,谈谈自己使用flux的心得体会。
首先我们先看看Flux架构的整体消息流程:
从流程图中,我们可以看到上面有4个元素:
View :就是一个界面可以想象成一个activity或者fragment,Action就是点击了Activity里面事件操作的响应,例如:点击按钮可以触发一个Action,更新数据可以触发一个Action等等。
Action:对应View的各种响应。
Dispatcher:就是Action的处理中心,负责发送所有的Action。
-
Store:可以把Store理解为Activity(即View)里面对应的数据和逻辑处理的集合,简单理解为一个view就对应一个store,通过这样的方式来实现activity中ui的更新和数据逻辑处理的解耦。当Dispatcher发送Action的时候,Store会捕获对应Action,然后根据Action提供的信息处理相应的逻辑,处理完毕之后Store就会通过自身的回调去触发对应的View中的相应方法来进行ui的更新。
从流程图可以看出,数据的流动都是单向进行的,这个是Flux框架最大的特点,也是Flux框架简单实用的原因。大家可以想象下,逻辑和数据都在Store更新,View的更新都在Activity的唯一一个响应方法中更新,那么我们调试代码的时候是非常方便的,粑粑再也不用担心忘记在某个地方隐藏未知的view更新操作,因为所有view的更新操作我们只会从Activity的唯一一个地方实现。
说到这里。。可能大家有点困惑。。还是直接上代码说明吧。。我会先把自己的设计架构和大家讲解下,然后再分别用两个简单的demo说明。。先看看代码目录结构。。主要实现上面的4种元素:view,store,action,dispatcher,分别放在不同的包里面。
大家还可以看到在action包里面还有有一个base的包,我会慢慢说明这个包的作用是神马。在这之前我们回到最初的设计思想,用通俗的话来描述我们在代码应该实现怎样的效果,还是举个例子说明:activity里面有增,删,改等等操作,我们要实现在完成不同的操作之后更新对应activity的界面。结合我设计的框架说明如下:
- Action包:大家可以看到action下面有个base的包,主要介绍下里面的4个类:
- dispatcher包:里面只有一个Dispatcher类,并提供其一个单例,另外还有一个Store类型的list,另外还有三个方法,register,unregister,dispatch:分别表示注册,反注册,发送消息。
- store包:看到里面有个抽象类Store,里面就包括了一个view的所有处理逻辑和数据,事件总线我这里是用EventBus,也可以用Otto或者Rxbus作为事件总线,具体可以看看里面的说明:
(1)Action类:可以看到action类是抽象类,只可以继承,里面有两个变量,type和一个泛型data,其中type表示操作的类型,例如:增,删等等,值不能为空,data表示所带的参数,如:update(xxx,xxxx)操作带上参数是很正常的,因此我把传参用一个data分装起来,data允许为空,因此方法不带参数也是正常的。
public abstract class Action {
private final String type;
private final T data;
public Action(String type, T data) {
this.type = type;
this.data = data;
}
public String getType() {
return type;
}
public T getData() {
return data;
}
}
(2)ActionsCreator类:消息分发处理类,用于发送不同的action,里面包含Dispatcher,就如上文说到的,用Dispatcher来发送action。
public class ActionsCreator {
private static ActionsCreator instance;
final Dispatcher dispatcher;
public Dispatcher getDispatcher() {
return dispatcher;
}
ActionsCreator(Dispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public static ActionsCreator get(Dispatcher dispatcher) {
if (instance == null) {
instance = new ActionsCreator(dispatcher);
}
return instance;
}
public void sendMessage(Action action) {
dispatcher.dispatch(action);
}
}
(3)ActionsCreatorFactory类:一个抽象类,里面只有一个构造函数,用于获取actionsCreator,其实整个系统通过一个actionsCreator就可以对不同Action进行处理,但是随着view页面的增多,actionsCreator里面的方法就会越来越多,这会造成代码冗余也不利于我们定位检查代码。因此需要用其他类去继承这个工厂类,来实现对应view的不同操作。例如:Activity1对应着Activity1ActionsCreator来实现Activity1中的各种操作。
public abstract class ActionsCreatorFactory {
public ActionsCreator actionsCreator;
public ActionsCreatorFactory(Dispatcher dispatcher){
actionsCreator=ActionsCreator.get(dispatcher);
}
}
public class MainActionsCreator extends ActionsCreatorFactory {
public MainActionsCreator(Dispatcher dispatcher){
super(dispatcher);
}
public void setText(String text){
actionsCreator.sendMessage(new MainAction.MessageActionEntity().setText(text).buildWithType(MainAction.ACTION_NEW_MESSAGE));
}
}
(4)IActionEntityBuilder类:一个接口类,实现类通过建造者模式填充参数,最后返回一个Action。
public interface IActionEntityBuilder {
Action buildWithType(String type);
}
public class Dispatcher {
private static Dispatcher instance;
private final List stores = new ArrayList<>();
public static Dispatcher get() {
if (instance == null) {
instance = new Dispatcher();
}
return instance;
}
Dispatcher() {}
public void register(Context context,final Store store) {
if (!stores.contains(store)) {
store.register(context);
stores.add(store);
}
}
public void unregister(Context context,final Store store) {
store.unRegister(context);
stores.remove(store);
}
public void dispatch(Action action) {
post(action);
}
private void post(final Action action) {
for (Store store : stores) {
store.onAction(action);
}
}
}
public abstract class Store {
protected Store() { }
public void register(Context context){
EventBus.getDefault().register(context);
}
public void unRegister(Context context){
EventBus.getDefault().unregister(context);
}
/*传入操作类型,然后触发主界面更新 */
void emitStoreChange(String operationType) {
EventBus.getDefault().post(changeEvent(operationType));
}
public abstract StoreChangeEvent changeEvent(String operationType);
/*所有逻辑的处理,在实现类中可以简单想象成对应着一个Activity(View)的增删改查的处理 */
public abstract void onAction(Action action);
/** 返回到view中的对象,在activity得到这个对象,通过operationtype来判断响应的操作去更新对应的ui*/
public class StoreChangeEvent {
private String operationType;
public String getOperationType() {
return operationType;
}
public StoreChangeEvent(String operationType){
this.operationType=operationType;
}
}
}
说了一大堆,现在我们用两个小demo来说明下这个框架的使用,后面也会附上demo代码,欢迎大家下载。
demo1:实现点击发送按钮就把edittext里面的内容显示到中间的textView上面去,这个demo就是实现响应activity内的操作,如图
新建一个MainActivity,并实现对应的MainAction,MainActionCreator,MainSotore等等。
public class MainAction extends Action {
public static final String ACTION_NEW_MESSAGE = "new_message";
public MainAction(String type, MessageActionEntity data) {
super(type, data);
}
/*每个操作参数用该类包装起来,用建造者模式来定制传入的参数,要注意type一定不能为空 ,最后返回的就是一个Action了*/
public static class MessageActionEntity implements IActionEntityBuilder {
private String text;
public String getText() {
return text;
}
public MessageActionEntity setText(String text) {
this.text = text;
return this;
}
@Override
public Action buildWithType(String type) {
return new MainAction(type,this);
}
}
}
再看看看MainStore
public class MainStore extends Store {
private static MainStore singleton;
private Message mMessage = new Message();
public MainStore() {
super();
}
public String getMessage() {
return mMessage.getMessage();
}
/*对应一个view的所有逻辑处理*/
@Override
public void onAction(Action action) {
String operationType=action.getType();
switch (operationType) {
case MainAction.ACTION_NEW_MESSAGE:
MainAction.MessageActionEntity messageActionEntity=(MainAction.MessageActionEntity) action.getData();
/*获取传入的参数值,即Edittext的值*/
mMessage.setMessage(messageActionEntity.getText());
break;
default:
}
/*触发view事件的回调,用于更新view的ui*/
emitStoreChange(operationType);
}
@Override
public StoreChangeEvent changeEvent(String operationType) {
return new MainStoreEvent(operationType);
}
public class MainStoreEvent extends StoreChangeEvent{
public MainStoreEvent(String operationType){
super(operationType);
}
}
}
然后看看MainActionsCreator,MainActivity的所有操作都通过该类执行。点击发送按钮就是触发settext事件。
public class MainActionsCreator extends ActionsCreatorFactory {
public MainActionsCreator(Dispatcher dispatcher){
super(dispatcher);
}
public void setText(String text){
actionsCreator.sendMessage(new MainAction.MessageActionEntity().setText(text).buildWithType(MainAction.ACTION_NEW_MESSAGE));
}
}
最后看看MainActivity。。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private EditText vMessageEditor;
private Button vMessageButton;
private TextView vMessageView;
private Dispatcher dispatcher;
private MainStore store;
MainActionsCreator mainActionsCreator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDependencies();
setupView();
}
/*必须反注册*/
@Override
protected void onDestroy() {
super.onDestroy();
dispatcher.unregister(this, store);
}
private void initDependencies() {
dispatcher = Dispatcher.get();
mainActionsCreator=new MainActionsCreator(dispatcher);
store = new MainStore();
/*注册store*/
dispatcher.register(this, store);
}
private void setupView() {
vMessageEditor = (EditText) findViewById(R.id.message_editor);
vMessageView = (TextView) findViewById(R.id.message_view);
vMessageButton = (Button) findViewById(R.id.message_button);
vMessageButton.setOnClickListener(this);
findViewById(R.id.btn_next).setOnClickListener(this);
}
@Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.message_button) {
if (vMessageEditor.getText() != null) {
/*操作都是通过mainActionsCreator触发*/
mainActionsCreator.setText(vMessageEditor.getText().toString());
}
}else if(id==R.id.btn_next){
Intent intent=new Intent(this,SecondActivity.class);
startActivity(intent);
}
}
private void render(MainStore store) {
vMessageView.setText(store.getMessage());
}
/*Store里面ui响应会在此触发*/
@Subscribe
public void onEventMainThread(Object event) {
if (event instanceof MainStore.MainStoreEvent) {
/*在此判断是主界面的那个操作,并进行响应的ui更新*/
if(MainAction.ACTION_NEW_MESSAGE.equals(((MainStore.MainStoreEvent) event).getOperationType())){
render(store);
}
}else if(event instanceof SecondStore.SecondStoreEvent){
Toast.makeText(this, "主界面收到消息了", Toast.LENGTH_LONG).show();
}
}
}
demo2:实现不同view之间的消息传送,新定义一个SecondActivity如图:点击SecondActivity上的按钮,如果触发消息MainActivity可以收到的话,那么就会弹出一个土司。
实现流程就是定义对应SecondActivity的store,action等等,消息处理可以参考demo1,关键判断代码就看看上面的MainActivity中的onEventMainThread方法。
@Subscribepublic
void onEventMainThread(Object event) {
if (event instanceof MainStore.MainStoreEvent) {
if(MainAction.ACTION_NEW_MESSAGE.equals(((MainStore.MainStoreEvent) event).getOperationType())){
render(store);
}
/*在此过滤是否响应SecondActivity的请求*/
}else if(event instanceof SecondStore.SecondStoreEvent){
Toast.makeText(this, "主界面收到消息了", Toast.LENGTH_LONG).show();
}
}
至此两个demo就讲解完毕,最后通俗总结下使用我搭建的框架实现Flux的流程:
我们要实现在activity(view)中的操作处理,并响应操作进行ui更新,都要实现activity所对应的Action,ActionsCreator,Store,而且都是一一对应的关系。用上面的demo说明就是:MainActivity对应MainAction,MainStore,MainActionCreator,而且都是唯一对应的关系。当MainActivity操作时,就是通过MainActionCreator进行操作消息发送对应MainAction,然后在MainStore处理不同的MainAction操作,最后MainStore调用emitStoreChange(operationType); 方法来通知view的更新,view的更新操作统一在onEventMainThread中处理。
至此文章就全部讲完了,通过实现我实现的Flux架构,相信你的代码逻辑会变得简单,而且也容易找到错误,在新的项目中就会用该框架进行开发,我也会结合实际开发,把遇到的一些问题贴上来和大家分享,也希望大家可以在自己项目中把架构搭好,尽量少点bug哈~
最后附上Flux架构说明的英文和中文链接,当然还有代码链接:
英文链接
中文链接
code
这个demo也有参考中文链接上面的demo来修改的,这样的目的是为了更方便自己的项目使用,非常敬佩在网上开源分享和分析技术的大神们,谨以此文向大神们致敬!