XCoreRedux框架:Android UI组件化与Redux实践

XCoreRedux框架:Android UI组件化与Redux实践

@author: 莫川 https://github.com/nuptboyzhb/

XCoreRedux源码+Demo:https://github.com/nuptboyzhb/XCoreRedux

使用android studio打开该项目。

目录结构

  • demo

    基于xcore框架写的一个小demo
  • xcore

    XCoreRedux核心代码库
  • pics

    文档的pic资源

前言

  • Android开发当中的Code Architecture思考

    最近看了很多前端的框架,React、Flux、Redux等,React和Redux都是前端比较流行的框架。而android方面,Google官方貌似不太Care此事,业内也没有公认的优秀Architecture。与前端类似,在Android开发中,同样也面临着复杂的数据state管理的问题。在理解Store、Reducer和Action的基础上,最终,基于Redux+React的思想,提出了一个基于Android平台Redux框架,我给它起名叫作:XCoreRedux。本仓库就是XCoreRedux+UIComponent框架的实现。它表达的是一种思想,希望大家能够提出更好的意见。

XCoreRedux框架介绍

与前端的Redux框架类似,XCoreRedux框架的图示如下:

redux20160930

Action

Action 是把数据传到 store 的有效载体。它是store的唯一数据来源。我们一般是通过 store.dispatch()将action传到store中。Action一般需要两个参数:type类型和data数据。在XCoreRedux框架下,我们定义Action如下:


public class XCoreAction {//Action的类型public final String type;//Action携带的value,可为空public final Object value;public XCoreAction(String type, Object value) {this.type = type;this.value = value;}public XCoreAction(String type) {this(type, null);}@Overridepublic boolean equals(Object object) {...}@Overridepublic int hashCode() {...}
}

为了统一的管理Action,你可以实现一个ActionCreator,比如,demo中创建了一个联系人业务的Creator:


/*** @version mochuan.zhb on 16/9/28.* @Author Zheng Haibo* @Blog github.com/nuptboyzhb* @Company Alibaba Group* @Description 联系人 ActionCreator*/
public class ContactsActionCreator {public static final String ADD_ITEM = "AddContacts";public static final String ADD_TITLE = "addCategory";public static final String DELETE_LAST = "deleteLast";public static final String CHECK_BOX = "contactsCheck";public static XCoreAction addContacts(Contacts contacts) {return new XCoreAction(ADD_ITEM, contacts);}public static XCoreAction addCategory(Title title) {return new XCoreAction(ADD_TITLE, title);}public static XCoreAction deleteLast() {return new XCoreAction(DELETE_LAST);}public static XCoreAction checkBoxClick(ContactsWrapper contactsWrapper) {return new XCoreAction(CHECK_BOX, contactsWrapper);}
}

Action的概念比较好理解,下面我们看一下Reducer

Reducer

reducer的字面意思就是“减速器”。Action描述了事件,Reducer是决定如何根据Action更新状态(state),而这正是reducer要做的事情。Reducer的接口定义如下:


public interface IXCoreReducer<State> {State reduce(State state, XCoreAction xcoreAction);
}

就是根据输入的Action和当前的state,处理得到新的state。


(previousState, action) => newState

说的更直白一点,Reducer就是一些列 纯函数 的集合。如Demo中的项目所示:


public class ContactsReducer implements IXCoreReducer<List<XCoreRecyclerAdapter.IDataWrapper>> {/*** 添加联系人** @param contactsWrappers* @param contacts* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> addOneContacts(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, Contacts contacts) {......return wrappers;}/*** 添加标题** @param contactsWrappers* @param value* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> addOneTitle(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, Title value) {......return wrappers;}/*** 删除最后一个** @param contactsWrappers* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> deleteLast(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers) {List<XCoreRecyclerAdapter.IDataWrapper> wrappers = new ArrayList<>(contactsWrappers);if (wrappers.size() > 0) {wrappers.remove(wrappers.size() - 1);}return wrappers;}/*** 设置选择状态** @param contactsWrappers* @param value* @return*/private List<XCoreRecyclerAdapter.IDataWrapper> changeCheckBoxStatus(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, ContactsWrapper value) {value.isChecked = !value.isChecked;return contactsWrappers;}@Overridepublic List<XCoreRecyclerAdapter.IDataWrapper> reduce(List<XCoreRecyclerAdapter.IDataWrapper> contactsWrappers, XCoreAction xcoreAction) {switch (xcoreAction.type) {case ContactsActionCreator.ADD_ITEM:return addOneContacts(contactsWrappers, (Contacts) xcoreAction.value);case ContactsActionCreator.ADD_TITLE:return addOneTitle(contactsWrappers, (Title) xcoreAction.value);case ContactsActionCreator.DELETE_LAST:return deleteLast(contactsWrappers);case ContactsActionCreator.CHECK_BOX:return changeCheckBoxStatus(contactsWrappers, (ContactsWrapper) xcoreAction.value);...}return contactsWrappers;}
}

通过上面的Reducer实现,我们可以看出,Reducer就是一些列函数的集合,其中一个关键函数reduce,它按照action的不同type执行不同的方法处理。

Store

store字面意思是存储。在Redux框架下,Store和DataBase,File没有关系,它可不是持久化存储的意思。Store是负责管理数据源的状态,负责把Action和Reducer联系到一起。Store的职责为:

  • 1.保存数据源的当前状态state
  • 2.对外提供dispatch方法,更新state
  • 3.通过subscribe注册监听器,当state变化时,通知观察者
  • 4.提供getState方法,获取当前的state

Store的Java实现:


public class XCoreStore<State> {private final IXCoreReducer<State> mIXCoreReducer;//数据处理器-reducerprivate final List<IStateChangeListener<State>> listeners = new ArrayList<>();//观察者private volatile State state;//Store存储的数据private XCoreStore(IXCoreReducer<State> mIXCoreReducer, State state) {this.mIXCoreReducer = mIXCoreReducer;this.state = state;}/*** 内部dispatch** @param xCoreAction*/private void dispatchAction(final XCoreAction xCoreAction) throws Throwable {synchronized (this) {state = mIXCoreReducer.reduce(state, xCoreAction);}for (IStateChangeListener<State> listener : listeners) {listener.onStateChanged(state);}}/*** 创建Store** @param reducer* @param initialState* @param <S>* @return*/public static <S> XCoreStore<S> create(IXCoreReducer<S> reducer, S initialState) {return new XCoreStore<>(reducer, initialState);}public State getState() {return state;}public void dispatch(final XCoreAction action) {try {dispatchAction(action);} catch (Throwable e) {e.printStackTrace();}}/*** 注册接口;添加观察者,当state改变时,通知观察者** @param listener*/public void subscribe(final IStateChangeListener<State> listener) {listeners.add(listener);}/*** 注销** @param listener*/public void unSubscribe(final IStateChangeListener<State> listener) {listeners.remove(listener);}/*** 状态改变的回调接口** @param <S> 状态*/public interface IStateChangeListener<S> {void onStateChanged(S state);}}

在Android中,一个Redux页面(Fragment或者Activity) 只有一个单一的 store。当需要拆分数据处理逻辑时,应该使用 reducer组合,而不是创建多个Store。

搭配UIComponent

与前端的Redux搭配React类似,XCoreRedux搭配UIComponent。

UI组件化(UIComponent)

在前段的React框架下,我们常常听说组件的概念:‘UI组件’。那么什么是UI组件呢?以下图为例:

xcoreredux_demo
红色的区域为“普通组件”,绿色的区域为两种不同类型的“Item组件”。因此,在UIComponent里,组件分两种:普通组件和item组件(或称为cell组件)。

普通组件

  • 单组件,比如一个自定义的Widget,就是一样View。比如自定义的CircleImageView等。
  • 容器组件,由ViewGroup派生出的组件。有FrameLayout、LinearLayout、RelativeLayout等。还有些常见的列表组件,比如ListView或者RecyclerView的组件等。

普通组件在XCore中是以FrameLayout的形式封装的,编写一个普通组件只需要实现如下方法:

  • 1.public int getLayoutResId()

    返回组件的布局资源Id
  • 2.public void onViewCreated(View view)

    View初始化
  • 3.实现XCoreStore中的IStateChangeListener接口,在onStateChanged中做数据绑定

    为了使UI组件能够与Store进行关联,UI组件可以实现IStateChangeListener接口,然后作为观察者,观察Store的state变化。然后在onStateChanged方法中做数据绑定。
    

Item组件(Cell组件)

对于前端来说,item组件和普通组件并没有什么不同。但是对于Android或者iOS而言,item组件和普通组件是有本质区别的。以ReyclerView为例,Item组件在同一种类型下是会复用的。在XCoreRedux框架中,定义Item组件,需要继承自XCoreItemUIComponent,它本身并不是一个View。它只需要实现的方法有:

  • View onCreateView(LayoutInflater inflater,ViewGroup container);
    与Fragment的onCreateView类似,它负责创建item的布局View
  • void onViewCreated(View view);
    与Fragment的onViewCreated类似,在此写View的初始化
  • public String getViewType();
    Item组件对于数据源的类型
  • public void bindView(IXCoreComponent coreComponent,
    XCoreRecyclerAdapter coreRecyclerAdapter,
    XCoreRecyclerAdapter.IDataWrapper data,
    int pos);
    数据绑定,当Adapter调用bindViewHolder时,会回调bindView方法。

Item组件需要通过Adapter,与对应的列表组件联系起来。针对Android常用的RecyclerView,XCoreRedux提供了插件式的通用XCoreRecyclerAdapter。

含列表组件下的XCoreRedux框架图

XCoreRedux20161002

与之前的不同之处在于,这里把整个列表封装成一个列表组件,对外提供注册Item,比如XCoreRecyclerViewComponent组件源码。


public class XCoreRecyclerViewComponent extends XCoreUIBaseComponent implements XCoreStore.IStateChangeListener<List<XCoreRecyclerAdapter.IDataWrapper>> {private SwipeRefreshLayout mSwipeRefreshLayout;private RecyclerView mRecyclerView;private RecyclerView.LayoutManager mLayoutManager;private XCoreRecyclerAdapter mXCoreRecyclerAdapter;public XCoreRecyclerViewComponent(Context context) {super(context);}public XCoreRecyclerViewComponent(Context context, AttributeSet attrs) {super(context, attrs);}public XCoreRecyclerViewComponent(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic final int getLayoutResId() {return R.layout.xcore_recyclerview_component;}@Overridepublic void onViewCreated(View view) {//初始化ViewmSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.xcore_refresh_layout);mSwipeRefreshLayout.setEnabled(false);mRecyclerView = (RecyclerView) findViewById(R.id.xcore_rv);//初始化RecyclerViewmLayoutManager = new LinearLayoutManager(getContext());mRecyclerView.setLayoutManager(mLayoutManager);mXCoreRecyclerAdapter = new XCoreRecyclerAdapter(this);mRecyclerView.setAdapter(mXCoreRecyclerAdapter);}public SwipeRefreshLayout getSwipeRefreshLayout() {return mSwipeRefreshLayout;}public RecyclerView getRecyclerView() {return mRecyclerView;}public RecyclerView.LayoutManager getLayoutManager() {return mLayoutManager;}public XCoreRecyclerAdapter getXCoreRecyclerAdapter() {return mXCoreRecyclerAdapter;}/*** 当状态发生变化时,自动通知** @param status*/@Overridepublic void onStateChanged(List<XCoreRecyclerAdapter.IDataWrapper> status) {mXCoreRecyclerAdapter.setDataSet(status);mXCoreRecyclerAdapter.notifyDataSetChanged();}/*** 对外提供item组件的注册** @param xCoreItemUIComponent* @return*/public XCoreRecyclerViewComponent registerItemComponent(XCoreItemUIComponent xCoreItemUIComponent) {mXCoreRecyclerAdapter.registerItemUIComponent(xCoreItemUIComponent);return this;}public void setRefreshEnable(boolean enable) {mSwipeRefreshLayout.setEnabled(enable);}
}

我们在使用该组件时,只需要:

1.在XML中添加组件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!-- 头部组件--><com.example.haibozheng.myapplication.components.container.HeaderComponentandroid:id="@+id/recycler_view_header_component"android:layout_width="match_parent"android:layout_height="wrap_content" /><!-- 列表组件--><com.github.nuptboyzhb.xcore.components.impl.XCoreRecyclerViewComponentandroid:id="@+id/recycler_view_component"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

2.初始化

        ...//创建数据源的storemContactsListXCoreStore = XCoreStore.create(new ContactsReducer(), new ArrayList<XCoreRecyclerAdapter.IDataWrapper>());//创建RecyclerView的UI组件mXCoreRecyclerViewComponent = (XCoreRecyclerViewComponent) findViewById(R.id.recycler_view_component);//注册item组件模板mXCoreRecyclerViewComponent.registerItemComponent(new TextItemComponent()).registerItemComponent(new ImageItemComponent());//创建头部组件mHeaderComponent = (HeaderComponent) findViewById(R.id.recycler_view_header_component);//添加观察者mContactsListXCoreStore.subscribe(mXCoreRecyclerViewComponent);mContactsListXCoreStore.subscribe(mHeaderComponent);...

组件之间通信

Item组件与列表组件及普通组件之间的通信。在本Demo中使用的EventBus是轻量级的otto。每一个继承自XCoreUIBaseComponent的组件,都已经在onCreate和onDestroy中分别进行了注册和反注册。使用时,只需要使用@Subscribe 注解来指定订阅方法。因此,在任意地方都可以调用:

XCoreBus.getInstance().post(action);

小优化

对于数据绑定方面,做了两个优化:
1.把数据通过Wrapper包装
2.使用UIBinderHelper做流式绑定,比如:


public class ImageItemComponent extends XCoreItemUIComponent implements View.OnClickListener {private UIBinderHelperImpl mUIBinderHelperImpl;...@Overridepublic void bindView(IXCoreComponent coreComponent,XCoreRecyclerAdapter coreRecyclerAdapter,XCoreRecyclerAdapter.IDataWrapper data,int pos) {mContactsWrapper = (ContactsWrapper) data;mUIBinderHelperImpl.from(R.id.item_content_tv).setText(mContactsWrapper.bindContentText()).from(R.id.item_pic_iv).setImageUrl(mContactsWrapper.getAvatarUrl()).from(R.id.item_title_tv).setText(mContactsWrapper.bindItemTitle()).from(R.id.checkbox).setButtonDrawable(mContactsWrapper.isChecked ? R.mipmap.checkbox_checked : R.mipmap.checkbox_normal).setOnClickListener(this);}...
}

后续

  • 1.异步
  • 2.Middleware中间件
  • 3.与Rx结合

参考文献

    1. Redux中文文档
    1. Flux and Android
    1. AndroidFlux一览

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gigaset ME/pure/pro体验:就是这个德味

Gigaset是何方神圣&#xff1f;可能大多数人都没有听过。但如果说起西门子&#xff0c;那各位肯定就会“哦”地一声明白了。实际上&#xff0c;Gigaset就是西门子旗下的手机品牌&#xff0c;当年世界上第一部数字无绳电话就是该品牌的产物&#xff0c;所以这次Gigaset在智能手机…

IOS 资料备份

2019独角兽企业重金招聘Python工程师标准>>> 利用本地服务器边下载视频边播放 目前还没有做好&#xff0c;下面是参考资料&#xff0c;做个备份&#xff1b; 参考资料&#xff1a; http://blog.csdn.net/wxw55/article/details/17557295 http://www.code4app.com/io…

BZOJ 1854: [Scoi2010]游戏( 二分图最大匹配 )

匈牙利算法..从1~10000依次找增广路, 找不到就停止, 输出答案. ----------------------------------------------------------------------------#include<bits/stdc.h>using namespace std;const int MAXL 10009, MAXR 1000009;struct edge {int to;edge* next;} E[MA…

Android下文件的压缩和解压(Zip格式)

Zip文件结构 ZIP文件结构如下图所示&#xff0c; File Entry表示一个文件实体,一个压缩文件中有多个文件实体。 文件实体由一个头部和文件数据组&#xff0c;Central Directory由多个File header组成&#xff0c;每个File header都保存一个文件实体的偏移&#xff0c;文件最后由…

MPI多机器实现并行计算

最近使用一个系统的分布式版本搭建测试环境&#xff0c;该系统是基于MPI实现的并行计算&#xff0c;MPI是传统基于msg的系统&#xff0c;这个框架非常灵活&#xff0c;对程序的结构没有太多约束&#xff0c;高效实用简单&#xff0c;下面是MPI在多台机器上实现并行计算的过程。…

Jenkins_获取源码编译并启动服务(二)

一、创建Maven项目二、设置SVN信息三、设置构建触发器四、设置Maven命令五、设置构建后发邮件信息&#xff08;参考文章一&#xff09;六、设置构建后拷贝文件到远程机器并执行命令来自为知笔记(Wiz)

正确理解ThreadLocal

想必很多朋友对 ThreadLocal并不陌生&#xff0c;今天我们就来一起探讨下ThreadLocal的使用方法和实现原理。首先&#xff0c;本文先谈一下对ThreadLocal的理 解&#xff0c;然后根据ThreadLocal类的源码分析了其实现原理和使用需要注意的地方&#xff0c;最后给出了两个应用场…

matlab 画三维花瓶,精美花瓶建模教程

1、首先&#xff0c;草图单位为mm&#xff0c;进入前视图绘制如图草图&#xff0c;花瓶的基本形状轮廓2、然后对草图进行旋转3、旋转出曲面后&#xff0c;在顶部边线新建一个基准面4、继续在前视图绘制草图&#xff0c;如图绘制一弧线5、然后进行旋转6、可以得到图示的两个曲面…

PKI系统相关知识点介绍

公钥基础设施&#xff08;Public Key Infrastructure&#xff0c;简称PKI&#xff09;是目前网络安全建设的基础与核心&#xff0c;是电子商务安全实施的基本保障&#xff0c;因此&#xff0c;对PKI技术的研究和开发成为目前信息安全领域的热点。本文对PKI技术进行了全面的分析…

Arduino 控制超声波测距模块

一.实物图 二.例子代码 用到数字2 和3 引脚,还有两个就是vcc GND两个阴脚,用模块连线比较简单 转载于:https://www.cnblogs.com/caoguo/p/4785700.html

Linux安装source-code-pro字体

2019独角兽企业重金招聘Python工程师标准>>> 1.下载source-code-pro字体 从GitHub下载 https://github.com/adobe-fonts/source-code-pro/releases 2.解压文件&#xff0c;将OTF格式的文件夹重新命名一下&#xff0c;这里我命名为source-code-pro&#xff0c;然后将…

PI

并不是所有东西都可以套PI的&#xff0c;只有满足上述这类的数学关系才可以。 转速经过PI调节得到电流也是有原因的。从下图中可以发现&#xff0c;转速 k*Iq/s&#xff0c;s是拉普拉斯算子&#xff0c;所以也是满足积分&#xff0c;比例关系的。 转载于:https://www.cnblogs.…

AOP之AspectJ简单使用

为什么80%的码农都做不了架构师&#xff1f;>>> 参考文章&#xff1a; 使用AspectJ在Android中实现Aop 深入理解Android之AOP自动打印日志主要知识点&#xff1a; 主要是JPoint、pointcuts、advice以及他们之间的关系可以通过aj文件、或AspectJ注解的Java文件实现A…

WinSCP实现Ubuntu与 Windows 文件共享方法

2019独角兽企业重金招聘Python工程师标准>>> WinSCP是一个Windows环境下使用SSH的开源图形化SFTP客户端。同时支持SCP协议。它的主要功能就是在本地与远程计算机间安全的复制文件。WinSCP绿色中文版 一款基于SSH安全高效的FTP上传软件。WinSCP 可以执行所有基本的文…

PHP获取QQ等级,php仿QQ等级太阳显示函数

开头先引述下QQ等级的算法&#xff1a;设当前等级为N&#xff0c;达到当前等级最少需要的活跃天数为D&#xff0c;当前活跃天数为Dc&#xff0c;升级剩余天数为Dr&#xff0c;则&#xff1a;从而推出:好了&#xff0c;引述完成&#xff0c;懒得写字了&#xff0c;贴出代码&…

Bugfree实用心得_转

转自&#xff1a;http://blog.csdn.net/benkaoya/article/details/8719257 本博下有许多实用技巧 1. 什么是问题跟踪系统 问题跟踪系统&#xff08;Issue Tracking System&#xff09;是专门用于记录、跟踪和管理各类问题的软件。 问题跟踪系统出现于上世纪80年代&#xff0c;…

【qxbt day1】 P2367 语文成绩

今天学了 差分********* 很明白 然后 配合着luogu上的题写一下吧 裸的差分 当时一直打暴力60分 交了十几次 今天才知道 查询修改什么的是差分 直接看题把 输入输出格式输入格式&#xff1a; 第一行有两个整数n&#xff0c;p&#xff0c;代表学生数与增加分数的次…

python会什么比c慢

众所周知&#xff0c;python执行速度比c慢。原因为何&#xff1f; 先来看下面这张图&#xff1a; python的传统运行执行模式&#xff1a;录入的源代码转换为字节码&#xff0c;之后字节码在python虚拟机中运行。代码自动被编译&#xff0c;之后再解释成机器码在CPU中执行。 补充…

Json字符串处理

2019独角兽企业重金招聘Python工程师标准>>> pom.xml <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.7</version> </dependency> 编写GsonUtils类 // // Source c…

用脚本控制虚拟机

#############用脚本控制虚拟机给file.sh 一个权限chmod x file.sh转载于:https://blog.51cto.com/forever8/1863587