上一篇文章发布之后又摸了小半年的鱼,前段时间精神状态确实很懒,写的东西都带有记流水账的性质。所以这次决定写点干货。
换了新工作之后,到新公司接手了公司以前的老项目。整个项目都是用coordova、ionic混合开发完成的。啃了两周的项目代码之后,觉得之前留下的摊子确实不好收拾,和产品确认过眼神之后,果断决定重启炉灶。
具体过程就不多啰嗦了,虽然说的是混合开发,不过总体来说仍然是原生为主,部分页面通过webview嵌入H5页面的方式来做展示,并不是网上所谓的一站式混合开发。不过个人认为这种方式才是更符合当前需求的一种方式,至于所谓的ionic之类的,demo很美好,需求很残酷。因为混合开发的缘故,从项目管理的角度和后期的维护性考虑,需要把H5相关的插件代码和原生代码分开,所以这里用到了模块化开发的方式。虽然这个项目目前只有我一个人,不过也正好可以试验一下自己以前想用而被上面无情pass掉的各种新技术。所以这篇文章主要介绍的是自己摸索并结合网上资料总结的一种组件化/模块化开发方式。对于这两种称谓,我个人没有太去纠结区别,所以下文可能会出现混用。本文的目的在于分享一种思路,大家就不要在意这些细节了。
-
需求场景
公司的老项目重构任务是要求把以前的ionic混合开发项目进行拆分,将大部分功能和主入口实现原生化,而商城这种业务变化较快的模块仍然保持H5方式展示。在第一版本的时候因为时间关系,只做了基础模块和主入口的原生化,剩下的大部分模块仍然是用老的coordova项目的代码打了一个aar包进来加载。也是在这个时候,因为项目中嵌入了大量配合coordova的代码,而这部分代码下个版本是注定要扔掉的,作为一个有强迫症的人,自然是不能容忍主项目里面有这些代码的,所以模块化的坑就这么挖起来了。
-
模块化开发的优势
一是结构清晰,各个模块的代码实现分离,不会搅在一起。在代码review或者二次开发的时候一目了然,不会满世界去找代码。
二是协同开发的时候更灵活,不用再等同组其他同事的模块开发完成后才能运行app,自己负责的模块稍加修改就可以当做主app直接跑起来。
三是便于维护。每个模块的代码、布局文件、资源文件可以随时从项目中通过gradle配置去除掉。 -
基本思路
公司接收的项目是一个货运平台app。按照功能划分,有货运模块、交易模块、货源模块、商城模块和个人中心。其中后两个是原项目的H5模块中的老代码,前三个是全部实现了原生化。所以基本结构就是原生入口,下分五个原生子页面。后两个原生子页面加载webview进行H5页面展示。
这里先附上一张基本的项目结构图。
一个基本的项目结构下面来说说这个项目的结构是怎么一步步整个成这样的。
1.基本结构
app模块是每个项目初始都有的,实质上仍然是一个module。Android Stuido项目中,我们和代码打交道的大部分时间都在module中。当然,具体的module机制我们不去深究,有时间可以另开一坑去研究。下面的三个module大家可以理解为java中的library,当然这样说不准确。
按照之前的业务逻辑划分,项目结构可以先大致分为如下的结构: 其中的箭头方向表示了引用关系
app作为主要的module,主要承担了应用启动以及最上层的通用逻辑。比如在这个例子中,应用启动、首页展示和模块切换的业务逻辑都在这个模块。
而下属的三个子模块承担了各自细分的业务,并且可以被app模块或者其他模块引用,每个模块只负责和考虑自己的业务。以此达到业务逻辑和代码分离的逻辑。
2.系统层分离
当然,仅仅这样是不够的。因为这样有一个问题,在AS中,module的引用是单向的。如果A module引用了B module,那么对A来讲,B是可见的,B的所有公开功方法理论上都可以在A中使用,但是对B来说,A是不可见的。所以,相面这样的结构出现的问题就是我们有大量的底层通用方法都放在app模块中,对于子模块来讲是不可见的,子模块无法引用封装好的底层方法,例如网络请求,图片加载,文件拷贝等,这肯定是不行的。所以这个结构还得再优化。
按照module单向引用的原则,我们可以把公共底层通用方法单独分出来作为一个moduleBase,同时这个moduleBase也是最底层的module,保证对于其他module来说它都是可见的。这样还有一个好处就是这个moduleBase中的方法和配置大部分都是可以高度复用的。在新开其他项目的时候这个模块可以直接迁移到新项目中,而不用在为代码分离和剔除浪费时间。所以,项目的结构进一步细化成了如下的结构:
到这里,一个基本的结构就算完成了,但是这个结构仍然不完善。
上述的模块如果在使用中,有很大概率会遇到一个问题,部分的实体类、自定义view、布局文件或者资源文件在各个模块都需要用到,但是这些如果放在系统层的moduleBase里面,又会破坏系统层的通用性。所以,我们还需要一个公共层moduleCommon来专门提供上层业务逻辑模块的公共资源。直接上图:3.公共层分离
4.细化与扩展
其实在第三步之后整个项目结构已经算是比较完整了,不过随着项目的不断扩大,模块仍然还是需要不断细分。这里说一下整个项目结构的一种规划思路。
在上述的项目结构中,严格来说只有三层:系统层、业务逻辑和app层。
其中app层是相对简单的,只要包含应用启动和初始化的一些额外操作和逻辑。而业务层包含了所有划分出来的子功能模块,为了方便项目结构的显示方便,我们可以把这些模块放在统一的文件夹下
":moduleUser"
变成":moduleCore:moduleUser"
,多级以次类推。同样,对中间层我们也可以采用类似的方式,因为中间层不仅会包含一些我们自定义的通用实体类等文件,还有可能会有我们常用的二次封装过的三方库和开源库。这些库有些本身就是一个module需要我们引入,有些则是我们经过二次封装的module。这些module同样不适合放在系统层,所以也可以归到公共层里,例如在我的项目里就有支付宝SDK,高德地图等通用moudle 最后在说一下系统层,虽然说这一层叫系统层,但是各个module之间实际上并不一定是平级关系的。比如在我的项目中我引入了greenDAO数据库框架。这个我作为一个单独的数据库模块放在了系统层,但是在层级上是最底层,被moduleBase引用。
最后,附上完整的设计思路和项目结构: 整体结构设计 这里需要说明一下各个模块之间的引用关系。建议各个模块在引用的时候都采用implementation的方式全部引用。例如app模块需要在gradle配置文件中引入所有需要用到的模块,这样可以不用考虑api方式产生的子模块继承引入的问题。例如app模块引入了moduleBill模块,而moduleBill引入了muduleA模块,那么app还需不需要引入这个模块呢?使用implementation的方式虽然繁琐了一点,但是也最大程度的保证了项目结构的清晰。
实际项目结构
到这里,一个基本的组件化项目结构就搭建完成了。这一部分主要是介绍了模块划分的逻辑和依据,偏向思想。实际开发过程中,因为各个module之间的引用关系还有很多的坑,这一部分我会在下一篇文章详细说明。这里就不在赘述。
最后,这种组件化的结构只是自己在实际项目开发中自己结合网上的资料摸索出来的,并不一定是最好的或最合理的,如果各位有更好的方法或思路,欢迎大家留言交流。
网友评论
这里是不是路径有错误? 不应该是com.example.ebookmodule.ShelfActivity ,为什么前面还有个主app的路径,我觉得这里是错了吧?