企业级 Android 模块化平台建设



对本文有任何问题,可加我的个人微信询问:kymjs666

本文将以模块化开发的场景作为切入点(基础架构组请将每个组件理解为一个独立模块) 首先,模块化开发这个普通的技术点我不打算再讲了,而且随着移动端技术越来越成熟,也没必要再去讲一些过时的东西了。
如果你还不懂什么是模块化开发,可以查看我2016年写的博客:《Android业务组件化开发实践https://kymjs.com/code/2016/10/18/01/

首先,这是一篇有门槛的文章,如果你们是只有两三个人的团队,那建议还是别折腾了,太浪费时间。如果是比较大的团队而且项目模块化已经完成了的,那还是可以考虑一下的,毕竟可以节省出来不少模块间联调与跨模块沟通的时间。

模块间联调

事实上我们的模块独立性已经做的非常好了,可以做到每个模块的增删,代码零改动,一行都不需要改。原理请见这篇文章:《优雅移除模块间耦合
但不可避免的,还是有三四个强业务模块,必须依赖全局Service才能工作的。
还有就是某些模块虽然不依赖外部模块,但需要一些全局信息,例如id用户名这种。但是如果使用mock数据,又会增大工作量,开发不愿意做。
解决办法其实很简单,Android Studio是支持工程compile module的。如何做?来看张图:

开源实验室-kymjs张涛

setting.gradle文件中,可以指定一个project位置,这里就可以将一个外部工程中的模块导入到APP工程中了。
然后这个useFeature,通过在local.properties文件中配置,就可以达到每个人修改自己的模块不会影响到其他人了。

顺带一提,也有另一种实现方案是直接将工程中的module单独作为一个git仓库,然后在外层(工程级)仓库的.gitignore中配置忽略掉module。这样在本地看来就是一个工程级仓库中包含了多个module仓库。这种方式也可以达到目的,但我个人不推荐使用这种方式。

让模块引用与aar引用互斥

解决了前一个问题,在模块联调与源码修改的时候是非常方便了的。但是实际开发中,如果你直接这么做,一定会碰到有类冲突的情况。
举一个例子:
base模块是每个模块都会依赖的,此刻base被打为了一个aar包,在使用的时候直接compile
login模块依赖了base模块。
此时如果我想调试base模块,用上面的方法把base的源码导入,就会发生,项目中不仅依赖了base的源码,同时还引入了baseaar,就会造成类冲突。

解决办法也是有的,就是本节标题:让模块引用与aar引用互斥。

可以通过自定义插件的形式,定义一个特定的关键词,比如像atlas就定义叫bundleCompile。在编译的时候,让这个bundleCompile继承自compile,并检测到如果setting.gradle文件中引入了module的源码就排除掉module对应的aar

gradle插件代码太长了,又是得继承指定类,又是得按照编译流程来麻烦的要死,其实还有一种简单的办法,就是直接在gradle脚本代码里面自己定义一个方法,方法名叫bundleCompile,也可以达到同样的目的。

防偷懒,我就只贴截图了,自己动手敲一遍吧。

开源实验室-kymjs张涛

组件平台建设

组件平台建设,这其实是一个企业级开发很重要的点,也是很多人所很难想明白的点。
就像我前面说的,如果你们是只有两三个人的团队建议别折腾了,没必要,口头约束比什么事都写下来要快很多,而且很方便。但如果是比较大的团队可能有什么事你去跟每个人说一遍的时间,都得花一天时间了。

现在每个公司应该都会有所谓的:架构组、基础组、移动横向组,这样的部门。在开发与日常工作时他们通常是做一些面向开发者的事情,例如网络统计、WebView(web容器)、统一配置管理这种的功能。
放大后,我们从宏观去看,其实每个应用的一个模块,也就是前面提到的一个基础功能,那么这种基础功能是否可以对外提供呢,就像一个网页一样,我告诉一个路由scheme你就打开我指定页面就好了,我告诉你一个接口,你就调我给你的接口对象就好了。

想做到上面的点就很依赖基础设施平台。由于原本公司就有很完善的基础设施平台这是先天优势(感兴趣的可以看一下这个PPT),那么基于这个平台,再去建设基础组件平台就变得非常容易。下面详细说一下几个重要的点:

  1. 组件的构建很重要,应当是统一由后端构建并直接上传至内部maven仓库。这样避免了开发人员手动打包过程中,测试包通过的包和正式包不一致的情况。如果是后端打包,可以直接精确到某次git commit保证测试的包就是最终发布的包。
  2. 平台具有版本提示作用,每个版本做了什么,当前最新版本是多少,当前应用中接入的版本是多少,当前线上某个版本接入的某个模块是哪个版本的,这些信息都一应俱全。任何一个有权限的人都可以很清楚的了解到这些细节信息,保证出意外时拥有修复的能力(例如模块的热修复)。
  3. 模块的测试与发布。前面我也说了,我们几乎所有模块可以做到动态增删不用修改代码。那么测试人员就可以通过远端直接拉取某个线上版本,将那个应用中指定模块给update或者配合exclude,通过控制变量来测试这个已经release的包是否因为此次模块改动造成问题。
  4. 线上事故记录。难免有测不到的BUG被发布,那么这个问题是哪个版本的应用,哪个版本的模块,问题是否在新版本已经修复过,是否是因为某些特殊接入环境造成的。这些信息也是可以统计到平台内,作为快速自查手册,以避免开发人员多次被同样的事情给打断。

自动版本号

自动版本号的实现网上有很多种办法,这里我讲一下我的实现方法。在gradle执行上传aar的时候,可以去远端maven仓库读maven-metadata.xml这样的一个文件。他里面记录了当前aar的各个版本以及最新的release版本的version。通过xml解析,取到后对这个版本做+1,就得到了新版本的版本号。
具体代码就是一个URLConnection去访问那个xml文件,然后解析,这里就不贴代码了,相信如果你能看到这里,这种代码你也是能写出来的。