Android模块化平台设计-讲稿

这篇文章是我在 2018【携程技术沙龙移动技术专场】技术分享时所讲内容的文字版本,修改删减了演讲时的冗余言语。

Android 模块化

Android 模块化

大家好,今天跟大家分享的主题是《Android工程模块化平台的设计》

Android 模块化

Android 模块化

首先自我介绍一下:我叫张涛,目前就职于饿了么移动技术部。可能有些朋友认识我,我之前也会在我博客【开源实验室】写一些Android相关的技术点,如果对今天讲的模块化设计,你觉得有什么问题或者可以深入探讨的,也欢迎加我微信kymjs123详聊。

Android 模块化

Android 模块化

今天我们讲的主题是基于项目模块化来说的,模块化是什么大家肯定都是知道了的,这里问一下大家,有多少人在此之前有做过模块化的,举个手我看一下;了解过听说过模块化的呢?这次比较多。
我们说,做模块化其实跟项目重构很像,都是从这几个点来做的,只是侧重点不同。分别是:删除、组织、降级、解耦。那么这四点是什么意思呢,那么接下来跟大家分享一下我是如何理解这四大块的:

Android 模块化

Android 模块化

删除:删除不必要的文件,尽可能减小工程体积。这里有一组数据,是我统计我们饿了么的一款 APP 在模块化前后一些文件的数量。
可以看到,.java文件从1677个减少到了1543个。其实这不是重点,重点是下面的drawable,这里drawable只包含图片、和xml布局,当经过模块化重构后文件数从 693 减少到 538 个。图片资源减少接近 200 个,apk 的大小也会随之降低。

Android 模块化

Android 模块化

而组织呢,指的是:按照有意义的标准将代码分组。这其实也是java的包所存在的目的之一。
但是随着项目的不断迭代,需求很紧的情况下是很难有时间去真正规范的将类分组的。看到图中,我们之前的结构很乱,就是因为项目快速迭代和人员更替的过程中,不免会有这样的现象。所以这也是模块化重构时所作的一件大事。

Android 模块化

Android 模块化

接下来就是我们经常说的内聚和耦合了,降级。我们之前有一个类叫:Navigator,它是负责几乎所有Activity直接跳转的。就是我们会把所有的startActivity()的跳转放到这个类里面去写。之前少的时候还好,结果等我看到这个类的时候,这个类已经有 200 多个方法了,全是Activity跳转的方法,其中还有重复的,就是很早之前有人写了一个跳到某个界面,结果之后来了个人,他不知道又写一个。

而我们在做模块化重构时的做法就是,首先观察自己的项目,这是重构很重要的一步,就是要结合自身。把这个类拆分成了三大部分,我们有两块业务是会频繁跳转的但这两个业务跳转的页面又都是在自身的模块内,分别是用户模块和商户模块。因此我们将这两个模块中分别建立两个用于模块自己内部的跳转叫UserNavigatorShopNavigator,而模块间的跳转或一些小模块内部的则使用Router去做,我们自己定义了一个路由库,其实实现跟现在开源的区别不大。

Android 模块化

Android 模块化

最后解耦,也是今天的重点,如何优雅移除模块间的耦合。
到目前为止,我们已经能够做到让所有不包含业务状态接口的模块的增删,不需要改动任何一行代码。
具体到一个示例就是这样:

Android 模块化

Android 模块化

或者,也可以是这样:

Android 模块化

Android 模块化

这两个段代码的区别就是一个是手动管理Debug的状态,另一个是交给Gradle的编译任务去控制,原理上是一样的。
而这么做是如何实现的呢,其本质就是:一个模块就是一个功能,你想要让你的 apk 具备这个功能,就添加这个模块一起编译就可以了。这才是我们说的真正的组件化,模块之间零耦合,增减模块零改动。
例如图中:debug这个模块,肯定不会用在正式的生产环境;而相反的tinker这个模块,热补丁肯定也不会用于调试阶段。所以我在开发时就可以不使用这个模块相关的代码。
另外再举个使用的例子:我有一个订单模块,订单模块需要播放铃声,比如大家在饭店经常听到“您有新的饿了么订单,请及时处理”。但我在开发订单模块的时候,如果我已经确定铃声播放是没有问题的,那我可以选择开发阶段不打铃声的包,直到发布到线上了再去加上铃声的包。那我没有添加这个铃声模块的时候,我就默认不具备播放铃声的功能,但完全不影响其他的订单模块的业务功能,而这个铃声模块的增删,是不需要修改任何代码的。
听到这里相信大家都很好奇这是怎么实现的。接下来就跟大家讲讲内部的原理。

Android 模块化

Android 模块化

所有的核心功能都来自我们自己写的一个库:IronBank。取《自冰与火之歌》中的【铁金库】,叫铁金库不容拖欠。
铁金库的内部实现,其实是使用了 APT 注解处理器,去在编译时解析注解生成一个类,让这个类去生成跨模块的对象。铁金库使用了与后端 SOA 设计思路类似的方式:将模块之间的主动依赖倒置,变为功能的提供与使用。
那什么是 SOA 的设计思路呢,我们看到一张我画的漫画图:SOA 它是一种面向服务的架构模型。

Android 模块化

Android 模块化

例如图上左边有一个对外提供媒体功能的服务提供者,他告知IronBank我提供媒体服务:“嘿,老铁,我这有个媒体服务,你那边有谁要用的时候可以用我的。”
到了另一边,如果此刻有模块说是,我需要媒体服务:“老铁,你那有没有媒体服务,我这边需要播一个铃声啊!”。
“有的,给你。”
IronBank就会将之前服务提供者提供给他的媒体对象交给服务使用者。

Android 模块化

Android 模块化

接下来我们来看具体到代码上是如何使用的:首先是作为服务使用方,也就是上一张图右半部分。我们看到传统的做法是首先声明一个接口类型,然后new出接口的实现类给他赋值。
而使用了IronBank的时候,你是不需要关心接口的实现类到底是谁的。这就是IronBank唯一的用处,隐藏实现类,做到彻底的面相接口编程。

Android 模块化

Android 模块化

top Created with Sketch.