微信号:bornmobile

介绍:专注于分享移动开发前沿和一线技术.

浅谈Android组件化

2017-09-27 18:45 张明庆
作者|张明庆
编辑|覃云
得到 APP 的 Android 技术专家张明庆老师曾发表过两篇关于 Android 组件化的文章,并开源了组件化项目,获得了不错的反响。为此,小编特地采访张明庆老师本人,了解其组件化背后的故事。本文是对采访稿的整理。
什么是组件化?
  模块化、插件化和组件化的关系

在技术开发领域,模块化是指分拆代码,即当我们的代码特别臃肿的时候,用模块化将代码分而治之、解耦分层。具体到 android 领域,模块化的具体实施方法分为插件化和组件化。

  插件化和组件化的区别

一套完整的插件化或组件化都必须能够实现单独调试、集成编译、数据传输、UI 跳转、生命周期和代码边界这六大功能。插件化和组件化最重要而且是唯一的区别的就是:插件化可以动态增加和修改线上的模块,组件化的动态能力相对较弱,只能对线上已有模块进行动态的加载和卸载,不能新增和修改。

  如何取舍插件化和组件化?

在插件化和组件化取舍的一个重要原则是:APP 是否有动态增加或修改线上模块的需求,如果这种动态性的需求很弱,就不需要考虑插件化,一般说来,电商类或广告类产品对这个需求比较强烈,而类似“得到 APP”这类的知识服务产品,每个功能的推出都是经过精细打磨的,对这种即时的动态性要求不高,所以不需要采用插件化。

如果你的产品对动态性的要求比较高,那么在选择插件化之前也需要从两个方面权衡一下。一是插件化不可避免的去 hook 一些系统的 api,也就不可避免地有兼容性的问题,因此每个插件化方案需要有专门的团队去负责维护;二是从一个业务逻辑复杂的项目中去拆分插件化需要的时间可能是非常巨大的,需要考虑对开发节奏的影响。

因此,对大多数产品来说,组件化都是一个不错甚至最佳的选择,它没有兼容性,可以更方便地拆分,并且几乎没有技术障碍,可以更顺利地去执行。特别是对急需拆分的产品来说,组件化是一个可退可守的方案,可以更快地执行下去,并且将来要是迁移到插件化,组件化拆分也是必经的一步。

  何为“彻底”组件化

之所以称这个方案是彻底的组件化,主要是为了更好地强调两点:一是组件之间代码边界的问题,直接组件之间直接引用(compile) 是要坚决避免的,一旦这么做了,就难免会导致直接使用其他组件的具体实现类,这样针对接口编程的要求就成了一句空话。更严重的是,一旦决定对组件进行动态地加载或卸载,就会导致严重地崩溃。所以只有做到了代码隔离,这个组件化方案才可以称之为“彻底”的。

在现在的方案中可以做到代码编写期间组件之间是完全不可见的,因此杜绝了直接使用具体的实现类的情况,但是在编译打包的时候,又会自动把依赖的组件打包进去。该方案是一个集合了六大功能的完整方案,覆盖了组件化中需要考虑的全部情况。

既然是“彻底”组件化,那么代码解耦之后,怎样才能让主项目间接引用各个独立的组件呢?方案采用的是一个配置文件,每个组件声明自己所需要的其他组件,配置分为 debug 和 release 两种,可以在日常开发和正式打包之间更灵活的切换。

方案自定义了一个 gradle 插件,它去读取每个组件的配置文件,构建出组件之间的依赖关系。这个插件更“智能”的地方在于,它分析运行的 task 命令,判断是否是打包命令,是的话(例如 assembleRelease)自动根据配置引入,不是(例如正常的 sync /build)等则不引入,也就是在代码编写期间组件之间是完全不可见的,因此杜绝了直接使用具体的实现类的情况。

但是在编译打包的时候,又会自动把依赖的组件打包进去。当然这里面还会涉及到组件之间如何通过“接口 + 实现”的方式进行数据传输,每个组件如果进行加载等问题,这些在 方案 中都有成熟的解决方式。

方案中自定义的 gradle 插件还有一个比较好的功能就是可以自动的识别和修改组件的属性,它可以识别出当前调试的是哪个组件,然后把这个组件修改为 application 项目,而其他组件则默默的修改成 library 项目。因此不论是要单独编译一个组件还是要把这个组件集成到其他组件中调试,都是不需要做任何的手动修改,使用起来相当的方便。

为什么要“彻底组件化”?

在刚开始对“得到 APP”Android 端的代码进行组件化拆分的时候,“得到 APP”已经是一个千万用户级的产品。经过那么长时间的积累,几十万行代码堆积在一起,编译一次大约需要 10 分钟的时间,这严重影响了开发效率。

由于业务复杂,代码交织在一起,可谓牵一发而动全身,因此每个人在写新需求的时候都有严重的代码包袱,瞻前顾后,花费在熟悉之前的代码的时间甚至大于新需求的开发时间。并且每个改动都需要测试人员进行大范围的回归,所以整个开发团队的效率都受到了影响。在这种情况下,实施组件化是迫在眉睫了。

如何实现“彻底组件化”

由于国内对插件化的研究是比较火爆的,而对组件化的研究热情就相对淡了很多。在设计“得到 APP”组件化方案的时候,几乎查阅了全部的组件化文章,都没有找到一个完整的支持上面说的六大功能的方案,所以不得不从头开始设计,“彻底组件化”的方案可跳转阅读 Android 彻底组件化方案实践 和 Android 彻底组件化 demo链接:

http://www.jianshu.com/p/59822a7b2fad

组件化过程中要注意的问题

让方案从纸上运用到实际,是一个比较困难的过程,这期间要注意两个方面的问题:一是技术细节上的不断完善,二是团队的共识建设问题。

技术上的问题主要是如何让方案更灵活,需求总是比预期要复杂,遇到特殊的需求,之前的设计可能就没法实现,或者必须得突破之前确定的拆分原则。这时候就需要回过头再审视整个方案,看看能否在某些方面做一些调整。方案中数据传输和 UI 跳转是分开的两个功能,这是在实际拆分中才做出的选择,因为数据传输更为频繁,且交互形式更多样,使用一个标准化的路由协议难以满足,因此把数据传输改成了接口 + 实现的形式,针对接口编程就可以更加灵活地处理这些情况。

除了技术上的,更重要的是团队的共识问题。要执行一个组件化拆分这样的大工程,需要团队的每个人达成共识,无论是在方案还是在技术的实现细节上,大家都能有一个统一的方向。

为此,在拆分之前多做几次组内的分享讨论,从方案的制定到每一次的实施,都让团队的大部分成员参与进来。正所谓磨刀不误砍柴工,在这种前提下,团队的共识建设会对后期工作效率的提高产生很大的价值。确立了共识,还需要确立统一的规则,虽说条条大路通罗马,但是在一个产品里,还是需要选择统一的道路,否则即便做了拆分,效果也会大打折扣。

iOS 和 Android 的组件化有何区别?

无论是 Android 还是 iOS,要解决的问题都是一样的,因此在组件化方案上要实现的功能(即上面所说的上面六种功能)也都是一样的,所以两者的组件化大体上来说是基本相同的。

有一个微小的区别在于技术实现方式的不同,由于两个平台用到的开发技术是不同的,Android 的组件化可能需要考虑向插件化的迁移,后期一旦有动态变动功能的强需求,可以快速地切换。而目前苹果官方是不允许这种动态性的,所以这方面的考虑就会少一点。但是 iOS 同样可以做到动态地加载和卸载组件的,因此在诸如生命周期、代码边界等问题上也需要格外注意,只是目前一些 iOS 组件化方案在这方面可能考虑的相对少一点。

组件化后的具体成果

组件化后的代码结构非常清晰,分层结构以及之间的交互很明了,团队中的任何一个人都可以很轻松的绘制出代码结构图,这个在之前是没法做到的,并且每个组件的编译时间从 10 分钟降到了几十秒,工作效率有了很大地提升,最关键的还是解耦之后,每次开发需求的时候,面对的代码越来越少,不用背负那么重的代码包袱,可以说达到了“代码越写越少”的理想情况。

其实组件化对外输出也是很可观的,现在一个版本开发完成后,我们可以跟测试说这期就回归“每天听本书”组件,其他的不需要回归。这种自信在之前是绝对没有的,测试的压力也可以小很多。更重要的是我们的组件可以复用,“得到 APP”会上线新的产品,他们可以直接使用已有的组件,省去了很多重复造轮子的工作,这点对整个公司效率的提升也是很有帮助的。

小编推荐:

想知道第一部Mac是如何被开发出来的?想了解更多苹果公司的工程师文化?想学习硅谷人是如何将产品做到极致的?想领略乔布斯的领导魅力?这本书都能告诉你。点击【阅读原文】即可购买,还有精美书签赠送哦~

 
移动开发前线 更多文章 你知道Siri在iOS 11中有哪些新功能吗? iOS 11正式版发布,快去试试这10个ARKit应用 QCon 2017上海站未来移动架构探索与实践 实时语音视频通话SDK如何实现听声辨位 为什么说Flutter是革命性的?
猜您喜欢 使用 Core Graphics 绘制基本形状 第9条 覆盖equals时总要覆盖hashCode 监控工具:Oracle 12c Cluster Health Monitor 详解 Android ListView工作原理完全解析,带你从源码的角度彻底理解(郭林) 架构系列二:秒杀系统浅谈