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

简介: ## XCoreRedux框架:Android UI组件化与Redux实践 @author: 莫川 [https://github.com/nuptboyzhb/](https://github.com/nuptboyzhb/) XCoreRedux源码+Demo:[https://github.com/nuptboyzhb/XCoreRedux](https://github.com/nup

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);
    }

    @Override
    public boolean equals(Object object) {
       ...
    }

    @Override
    public 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;
    }

    @Override
    public 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;//数据处理器-reducer
    private 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);
    }

    @Override
    public final int getLayoutResId() {
        return R.layout.xcore_recyclerview_component;
    }

    @Override
    public void onViewCreated(View view) {
        //初始化View
        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.xcore_refresh_layout);
        mSwipeRefreshLayout.setEnabled(false);
        mRecyclerView = (RecyclerView) findViewById(R.id.xcore_rv);
        //初始化RecyclerView
        mLayoutManager = 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
     */
    @Override
    public 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.HeaderComponent
        android:id="@+id/recycler_view_header_component"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <!-- 列表组件-->
    <com.github.nuptboyzhb.xcore.components.impl.XCoreRecyclerViewComponent
        android:id="@+id/recycler_view_component"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

2.初始化

        
        ...
        //创建数据源的store
        mContactsListXCoreStore = 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;

    ...

    @Override
    public 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结合

参考文献

目录
相关文章
|
22天前
|
前端开发 编解码 数据格式
浅谈响应式编程在企业级前端应用 UI 开发中的实践
浅谈响应式编程在企业级前端应用 UI 开发中的实践
20 0
浅谈响应式编程在企业级前端应用 UI 开发中的实践
|
23天前
|
消息中间件 安全 数据处理
Android为什么不能在子线程更新UI
Android为什么不能在子线程更新UI
26 0
|
1月前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
36 4
|
1月前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【2月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。随着Kotlin语言的流行,其异步编程解决方案——协程(Coroutines),为Android应用带来了革命性的并发处理能力。本文将深入探讨Kotlin协程的核心概念、设计原理以及在Android应用中的实际应用案例,旨在帮助开发者掌握这一强大的工具,从而提升应用的性能和响应能力。
|
24天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
2天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
12天前
|
编解码 Android开发 UED
安卓UI/UX设计原则:打造引人入胜的用户体验
【4月更文挑战第13天】本文探讨了安卓UI/UX设计的关键原则,包括一致性、简洁性、反馈、清晰性、效率和适应性。一致性要求视觉和行为保持一致,利用系统UI;简洁性减少用户行动,简化导航;反馈需即时且明确;清晰性强调表达清晰,布局有序;效率关注性能优化和任务简化;适应性涉及多设备适配和用户多样性。遵循这些原则,可创建出色应用,提供无缝用户体验。设计应持续迭代,适应技术发展和用户需求。
|
16天前
|
XML 移动开发 Android开发
构建高效安卓应用:采用Jetpack Compose实现动态UI
【4月更文挑战第10天】 在现代移动开发中,用户界面的流畅性和响应性对于应用的成功至关重要。随着技术的不断进步,安卓开发者寻求更加高效和简洁的方式来构建动态且吸引人的UI。本文将深入探讨Jetpack Compose这一革新性技术,它通过声明式编程模型简化了UI构建过程,并提升了性能与跨平台开发的可行性。我们将从基本概念出发,逐步解析如何利用Jetpack Compose来创建具有数据动态绑定能力的安卓应用,同时确保应用的高性能和良好用户体验。
15 0
|
17天前
|
XML Java Android开发
Android之UI基础控件
Android之UI基础控件
|
18天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。