Redux is a predictable state container for JavaScript apps Redux是 JavaScript 状态容器,提供可预测化的状态管理。
Redux 文档开头就总结了Redux的意义。当然也非常详细的解释了Redux的三大原则和各模块作用,想入门请看Redux中文文档。
当你已经理清楚了Action,Reducer,Store直接的关系之后,你会有这样的疑问,Action怎么传递到Reducer中的?Reducer生成的新state又是怎么触发对应页面的刷新的?这个问题我也不想讲,因为这篇文章写的非常好-深入到源码:解读 redux 的设计思路与用法,我其实想从Android开发者的角度谈一谈直接对Redux思想的理解和Redux for Android的尝试。
说干就干,这里模拟一个网络请求,先看效果。
再来看代码:
BaseReduxActivity 中使用Event来桥接View和state的改动,相当于 react-redux中的 connect
/**
*
* Created by mobilexu on 30/6/16.
*/
public abstract class BaseReduxActivity extends AppCompatActivity {
public EventBus eventBus;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventBus = EventBus.getDefault();
eventBus.register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
eventBus.unregister(this);
}
/**
* 接收想要监听的状态变化
*
* @param state
*/
public void onEvent(State state) {
onStateChange(state);
}
protected abstract void onStateChange(State state);
}
Action中提供一个type,和一个string->Object的HashMap
/**
* Created by mobilexu on 2/7/16.
*/
public class Action {
private int type;
private HashMap<String, Object> hashMap;
public int getType() {
return type;
}
public Action(int type) {
this.type = type;
}
public Action(int type, HashMap<String, Object> hashMap) {
this.type = type;
this.hashMap = hashMap;
}
public HashMap<String, Object> appendHashParam(String key, Object object) {
if (hashMap == null) {
hashMap = new HashMap<>();
}
hashMap.put(key, object);
return hashMap;
}
public HashMap<String, Object> getHashMap() {
return hashMap;
}
}
ActionCreater简单的提供一下dispatch方法
/**
* Created by mobilexu on 3/7/16.
*/
public class ActionCreater {
public void dispatch(Action action) {
Store.getInstance().dispatch(action);
}
}
Reduce和State目前只是个接口
/**
* Created by mobilexu on 2/7/16.
*/
public interface Reduce {
State handleAction(Action action);
}
/**
* Created by mobilexu on 2/7/16.
*/
public interface State {
}
Store中提供了Reducer的注册和dispatch的真正实现,还有就是桥接了EventBus用于桥接view和state的改变
/**
* Created by mobilexu on 2/7/16.
*/
public class Store {
private static Store defaultInstance;
private EventBus eventBus;
private List<Reduce> reducers;
public static Store getInstance() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new Store();
}
}
}
return defaultInstance;
}
private Store() {
eventBus = EventBus.getDefault();
reducers = new ArrayList<>();
}
/**
* 合并reduce
*
* @param reduceList
*/
public void combineReducers(Reduce... reduceList) {
for (Reduce reduce : reduceList) {
reducers.add(reduce);
}
}
/**
* 添加单个reduce
*
* @param reduce
*/
public void addReduce(Reduce reduce) {
reducers.add(reduce);
}
/**
* 移除单个Reduce
*
* @param reduce
*/
public void removeReduce(Reduce reduce) {
reducers.remove(reduce);
}
/**
* 清空reducers
*/
public void clearAllReduce() {
reducers.clear();
}
/**
* 发送一个Action
*
* @param action
*/
public void dispatch(Action action) {
for (Reduce reduce : reducers) {
State localState = reduce.handleAction(action);
if (localState != null) {
eventBus.post(localState);
}
}
}
}
最后看一下如何使用 Reducer可以在Application中统一添加,也可以在各种的页面添加。
public class SecondActivity extends BaseReduxActivity {
private TextView ButtonTwo;
private SecondReduce reduce;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
ButtonTwo = (TextView) findViewById(R.id.buttonTwo);
init();
render(null);
}
private void init() {
reduce = new SecondReduce();
Store.getInstance().addReduce(reduce);
ButtonTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new SecondActionCreater().changeText();
}
});
}
private void render(SecondState state) {
if (state == null)
return;
if (state.isLoading()) {
ButtonTwo.setText("。。。");
} else {
ButtonTwo.setText(state.getText());
}
}
@Override
protected void onStateChange(State state) {
if (state instanceof SecondState) {
render((SecondState) state);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Store.getInstance().removeReduce(reduce);
}
}
SecondActionCreater模拟了一次类似网络请求的操作,发送了俩个Action出去
public class SecondActionCreater extends ActionCreater {
public void changeText() {
dispatch(new Action(SecondReduce.ACTION_SECOND_SHOWLOADING));
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
showText();
}
}, 3000);
}
private void showText() {
Action action = new Action(SecondReduce.ACTION_SECOND_SHOWTEXT);
action.appendHashParam("text", new Random().nextInt(10) + "文本");
dispatch(action);
}
}
SecondReduce 收到Action之后,根据type对数据进行加工
public class SecondReduce implements Reduce {
public static final int ACTION_SECOND_SHOWLOADING = 3;
public static final int ACTION_SECOND_SHOWTEXT = 4;
@Override
public State handleAction(Action action) {
switch (action.getType()) {
case ACTION_SECOND_SHOWLOADING:
return new SecondState(true, null);
case ACTION_SECOND_SHOWTEXT:
return new SecondState(false, action.getHashMap().get("text").toString());
default:
return null;
}
}
}
SecondState 简直就是个ViewModle
public class SecondState implements State {
private boolean isLoading;
private String text;
public SecondState(boolean isLoading, String text) {
this.isLoading = isLoading;
this.text = text;
}
public boolean isLoading() {
return isLoading;
}
public void setLoading(boolean loading) {
isLoading = loading;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
整个过程,将操作都放在了SecondActionCreater 中,其实这个时候,ActionCreater就是MVP中的presenter,而Reducer就是生成ViewModle的过滤器,他不负责原始数据的采集,只是根据Action的描述,对数据进行一次加工,最后Reducer产生了一个State,通过EventBus维护的state和activity关系,进行了分发。
机智的读者会发现,redux 建议只维护一个全局 state ,而上面的框架中state是分散的,这里我有这样的考虑,原始app和web开发不太相同,一个工具app,从打开使用到最后关闭,可能会遍历几十个页面,这个时候如果维护全局一个state,那么势必会有不要的性能开销,而且这里我也可以通过监听不同的state来实现特殊情况下的需求。
@Override
protected void onStateChange(State state) {
if (state instanceof ChangeTextState) {
render((ChangeTextState) state);
} else if (state instanceof SecondState) {
renderSecond((SecondState) state);
}
}
上面简单写了一个redux for Android的框架,源码可以这里看到,下面的愿景是将DataBinding结合,那个时候,ActionCreater相当于Presenter,state相当于ViewModel,那这个架构就叫PVVM。
猝不及防的吹了个牛逼,不要见怪。不过还是欢迎大家提pr。