阅读 651

Android组件化模块化在移动应用开发中 (序)

组件化模块化

[TOC] 在移动应用上由于应用本身与使用者相贴近,日常开发中难免的需求变更朝令夕改、朝三暮四,尤其遇到没啥规范流程的或者形式主义流程;

关于组件化模块化是什么及其优点不再赘述,在此补充点优点而已:减少名言的联想

Linus:Read the fucking source code

我们的终极目标不仅是解耦等。简化、轻量化框架,减少过分约束而给予组件/模块内充分的自由,从初始设计已有两年余,由于重复搬砖没得时间记录, 文章中尽量不贴fucking code,有良好的思路大家都会有很好合适自身需求的实现。

组件 模块

鉴于网上很多文章对于组件模块的定义众说纷纭,再次声明本文定义,参考资料来源维基百科

基于组件的软件工程(Component-based software engineering,简称CBSE**)或基于组件的开发(Component-Based Development,简称CBD)是一种软件开发范型。它是现今软件复用理论实用化的研究热点,在组件对象模型的支持下,通过复用已有的构件,软件开发者可以“即插即用”地快速构造应用软件。这样不仅可以节省时间和经费,提高工作效率,而且可以产生更加规范、更加可靠的应用软件

模块 是指由数个基础功能组件组成的特定功能组件,可用来组成具完整功能之系统、设备或程序。模块通常都会具有相同的制程逻辑,更改其组成组件可调适其功能或用途。

综上组件Component是一种工具功能性质,而模块Module具有系统功能性质。例如jetpack中的构架组件.

模型model与模块还是区分对待。

由简单到复杂

长 短之相形,难 易之相成

任何系统都是由简单到复杂逐渐演化构成。类似于生态系统。稳定而强大的系统均是由简单的逐渐进化为复杂的系统。简单系统需要具备可进化性,其高一级系统需要有可退化性。而进化的必要因素时间

从AndroidX JetPack开始,构件一个兼具进化与退化的弹性系统框架。适应目的,分离表现层显示层,重复常用功能视图层操作形式已经定型的提取VM/模型组件,如视频播放 已知用户需求暂停,快进,声音,亮度;接口提供给了UI层,但UI层又要重新写SeekBar,Button 等UI控件,进一步看Seek Bar等也有自身的一些约定成俗的习惯性响应,这种VM组件是否可以直接包含这些UI控件,由视图层使用时传入,VM层仅仅与控件互动与Seek Bar的进度和与Button点击,而不关心UI层传入的是什么样子的控件,也不关心控件的位置,这些全都是UI层的事情。其实可以传入对应的接口控件,但当前控件没有进化到这一代。

Model模型层做提供实质性需求的业务。如视频播放,用什么播放器,自带的第三方的由mondel接管。用户需求是从视图层发起请求,VM层处理视图发起后的请求给Model去处理,Model处理后结果由VM层得知,VM又由更新数据去更新UI。

完成于此后。除非播放器更新换代,添加了其他功能(预览帧,GIF截取)VM,与M均不需要更新code.重复的搬砖仅仅是将UI搬运。

我们的组件化目标

首先肯定是解耦,防止代码污染;其次单模块调试,方便调试加快编译速度,提高专注效率;然后是框架轻量尽量不入侵,不过分限制模块,给模块内自由;能快速构建其一个基础功能齐全的Hello World;

最终效果

外核保护内核,仅暴露少量Base类接口,这些类的具体实现都在内核包中实现,可混淆后发布aar到项目私有maven,由开发版的外核配置依赖具体版本的内核;外核包中的少量类仅有及少量代码;

网络请求

	//登录请求
    private void click() {
        getViewModel(LoginViewModel.class)
                .sendAction(new RequestAction()
                        .setTipsArgs(new VmTipsArgs.Builder().tipMessage("请求失败了").build())).
                doOnData((action, data) -> {
                    AppLog.d(TAG, "发起登录成功 doOnData1=", data);
                    showToast("登录成功1" + data);
                })
                .doOnData((action, data) -> {
                    AppLog.d(TAG, "发起登录成功 doOnData2=", data);
                    showToast("登录成功2" + data);
                })
                .doOnDone(action -> {
                    AppLog.d(TAG, "发起登录完成1");
                    showToast("登录结束");
                })
                .doOnListener(action -> {
                    AppLog.d(TAG, "开始登录 loading....");
                    showToast("loading....");
                })
                .doOnDone(action -> {
                    AppLog.d(TAG, "发起登录完成2");
                })
                .doOnError((action, e) -> {
                    AppLog.d(TAG, "登录失败");
                })
        ;
    }
复制代码

日志

MainActivity  doOnListener开始登录 loading....  
MainActivity  doOnData发起登录成功 doOnData1=  "{uid:123}" 
MainActivity  doOnData发起登录成功 doOnData2=  "{uid:123}"  
MainActivity  doOnDone发起登录完成1  
MainActivity  doOnDone发起登录完成2  
复制代码

组件化模块化需要解决的问题

  1. 组件隔离,模块独立,模块可单独调试;

    每人只能看到框架代码和调试外壳代码,其他人的代码眼不见心不烦;(组件隔离:A组件的部分功能可抛出由其他组件B完成;模块独立:模块有自己的小生态,我在此为其添加统一的调试外壳,而不是变身app,浪费转换时间,也不需要写两份清单文件)

  2. 模块初始化注册【代码插桩;反射初始化等;ASM】推荐模块初始化注册插件

  3. 组件/模块之间的通讯,相互调用,数据传递【modular-event ,LiveData-Bus ,本地广播等】

  4. 组件/模块怎么通知其他组件响应某个事件(组件的视听功能)

  5. 模块界面之间的跳转,因为为了解耦,组件间不可以直接互相调用【ARouter等】

  6. 主项目获取并显示组件的fragment

  7. 组件间如何集成并调试

  8. 获取其他组件的数据或服务(发派信使携带任务,完成返回)

  9. 资源命名重复,资源本身重复冗余

  10. 组件之间避免过度依赖年轻的组件,基础组件避免依赖不成熟的第三方

  11. 其他...

举例包含其中3,4,5,6的一个业务场景:

多个模块中的UI需要组合显示在某同一个界面中,牵扯数据刷新,模块间数据传递,及相互调用等问题

解决问题

关注下面的标签,发现更多相似文章
评论