Android模块化设计之组件开发规范

3,233

最近一直在做基础建设方面的工作,面对这三十多个完全没有规范可言的组件,气的我直接打了一套闪电五连鞭,但打工还得继续,于是想对这些组件建立一套规范,来降低够用、使用、维护以及扯皮成本,本想在网上白嫖一套,可找到的都是一些基础的代码规范,用处不大,于是乎根据自己的工作经验,总结出一套规范/约定出来,希望能抛砖引玉,各位大佬多多指点和补充~

规范本身就是开发人员之间的约定,没有最权威的,只有最适合的。

版本规范

为保证组件在各个项目中的兼容性问题,约定组件开发版本如下:

AndroidSdk版本

minSdkVersion:21

targetSdkVersion:28

语言环境

开发语言:Kotlin

Kotlin版本:1.4.x

JDK版本:1.8

其他建议版本

AndroidStuido:4.2

gradle tools:4.1.x

gradle:6.7.x

组件命名规范

根据组件的功能不同,约定组件分为三个类型:

基础组件

为项目提供与业务无关基础支持的组件库,如提供MVVM架构的lib_basic,提供依赖注入的lib_basic_koin,这类组件统一命名方式为lib_basic_xxx。这些基础组件也可以被工具组件和业务组件依赖,而不仅仅是只被项目依赖。

工具组件

对项目中常用的业务无关功能封装的组件库,如Dialog弹窗,相册选择等,这类组件统一命名方式为lib_util_xxx

业务组件

在某个项目的需求中出现的在其他项目中可能也会用到的功能的封装,比如A业务员版中扫描拍照在A商业版中也有用到,于是单独封装为一个业务组件进行管理,这类组件统一命名方式为lib_tool_xxx

GroupId

所有组件统一GroupId为com.company.android,方便在Maven仓库中进行索引和管理。

版本号

对于迭代的版本号,不做强制性的要求,只需合理进行升级即可,但以下情况需特殊注意:

1、对于上传的开发版本,需要在版本中进行体现,如1.1.0-dev02,与正式版本做区分,在项目中验证无风险后,合并到主迭代版本中。

2、对于紧急修复A项目中的问题而临时发版的,需要在版本中进行体现,如1.1.0-hotfix-A,此次修改在其他使用的项目中验证无风险后,合并到主迭代版本中。

开发原则

开闭原则

在组件升级时,应对新增开放,对修改关闭,即做加法不做减法,目的是为了保证对项目中调用老版本API的兼容问题。对于不再建议使用的API,应使用 @Deprecated注解进行标注,并新增建议使用的API进行代替,而不是直接删除旧API。在完成多个稳定版本的迭代之后,可以所有组件使用者讨论删除旧版API的事宜。

向下兼容原则

所有组件的版本迭代必须向下兼容,即1.2.0版本须兼容1.1.0,在使用者升级版本之后,无需修改业务层代码。

如果因组件前期设计不合理导致升级必须修改业务代码时,组件开发者需要与所有组件使用者商讨技术实现方案,看能否以最小的改动完成组件的升级,并在组件功能开发完毕后,以书面形式告知使用者并提供完整的升级文档

三方隔离原则

我们在封装组件的时候,难免会使用到第三方依赖,为了避免更换三方依赖或依赖升级造成的影响,需要对三方依赖库进行二次封装,避免直接使用。比如我们要使用glide框架加载图片,可以创建一个代理类对glide的功能进行代理,在业务代码中使用代理类进行操作而不是直接调用glide,这样我们将来替换glide框架时,能最小的改动业务代码。

当然,并不是所有的三方库都通过代理模式进行隔离,比如retrofit、RxJava等,毕竟我们的隔离原则是为了以后更简单的迭代而不是自寻烦恼。

最少依赖原则

为了减少项目中依赖的类库,在组件封装中遇到如下情况,应对组件进行拆分工作:

现封装一图片加载类库lib_util_imageloader,对GlidePicasso进行了二次封装,由于两个类库提供了类似的功能,在项目中只需要使用一套就能满足业务需求,没有必要把两个图片加载框架都进行依赖,所以应该对lib_util_imageloader进行拆分为如下结构:

lib_util_imageloader_core:图片加载核心库,把加载图片的方法抽象为接口,面向项目,不做具体的实现。

lib_util_imageloader_glide:对glide进行二次封装,实现core中的接口。

lib_util_imageloader_picasso:对picasso进行二次封装,实现core中的接口。

在项目使用图片加载库时,除了必须要依赖的core外,只需要从glide和picasso中挑选一个即可,这样就不会把用不到的类库也打包到项目中去了。

这样的做法还有一个好处,就是容易拓展。如果我想要使用Fresco,只需要再新增一个lib_util_imageloader_fresco,并实现core的接口即可,在切换组件时,只需要改变gradle文件中的依赖,无需变更业务代码,因为业务代码都是基于core组件的。

最少可见原则

组件应该尽可能少的对外暴露类、接口、方法等。可以通过外观模式对使用者统一提供API,降低使用者的理解难度。

支持开发模式

组件需要预留开发者模式,可以让使用者自行选择开启关闭。

打开开发者模式:组件运行的关键节点需进行日志输出,方便使用者进行调试,可以在运行时抛出异常。

关闭开发者模式:组件不再对外输出Error级别以下的日志,禁止在运行时抛出异常、ANR,如发生异常需要在组件内捕获并通过错误回调或打印Error级别日志等方式告知使用者。

可拓展性

部分组件(视具体情况而定,多数为对三方类库的封装组件)应该有一定的拓展性,应支持使用者自定义实现覆盖默认实现。

比如Dialog类库有默认弹窗样式,需要支持使用者自定义弹窗样式而不是只能在默认样式中进行选择,图片加载框架也是相同的逻辑。

开发规范

包名规范

为了避免无意中导致的包名冲突问题,约定组件的包名为4级,除去前两级的com.company为固定写法以外,后两级可根据具体的组件功能进行命名,并要求有良好的可读性。

资源规范

组件中定义资源文件时,要以组件名称为前缀,避免资源冲突导致的打包问题,需要在gradle文件的android节点下新增如下代码强制进行资源名称前缀检查:

android{
  resourcePrefix = "${your_component_name}_"
}

代码规范

应当遵循Java开发代码规范,这里不再赘述。

可见域规范

Kotlin中新增关键字internal,可用于修饰类名、方法名和成员变量名,限制所修饰的对象模块内可见,对于无需对外暴露的类、方法和变量,应当降低其可见域。

内联函数

Kotlin中新增关键字inline修饰方法,可以减少方法栈的进栈方法数。在封装组件时善用inline以提高组件的运行效率。

由于JVM本身对方法调用做了优化,对于普通方法使用inline对月效率的提升有待验证,是否使用需要进行深入调研。

内存泄漏

所有组件在发布之间,必须进行内存泄漏检测,禁止存在内存泄漏的组件上线,应在开发阶段修复所有泄漏问题。

混淆

组件开发者需要确认自己的组件在打包混淆后是否可以正常工作,如有用到运行时注解、反射、Json转换等功能,需要在接入文档中声明避免混淆的规则。

如果组件可以打包成aar,建议把混淆规则打入aar中,防止使用者忘记添加混淆规则导致打包异常问题。

android{
  defaultConfig{
    //通过此配置把混淆规则打入aar中
    consumerProguardFiles "proguard-rules.pro"
  }
}

文档

组件的接入、使用、升级和注意事项等需要在开发文档中有明确的体现,没有接入文档或者文档不完善一律不许通过验收。

Demo

组件最好有配套的演示工程,用来最直观的体现出组件所提供的功能,也方便使用者进行参考。

最后

目前只能想起这么多,以后有新增的会继续补充。

规范好定制,落实起来却难,路漫漫其修远兮,加油吧,打工人!