前言
随着Android平台的飞速发展,许多老牌App,都发展成了所谓的“超级应用”,不但功能模块众多、代码量巨大,团队规模更是扩大到了几十人甚至上百人的规模。一线大厂的旗舰应用,甚至可能涉及到多个部门的协同开发。 在这个过程里,必然的产生了一些“代码复用”和“协同效率”方面的问题。所以,组件化开发逐渐被各个大厂提上了日程。如今,移动端开发除了要解决“代码”方面的问题,也需要关注“工程”方面的问题。 2022年,百度网盘App迎来了10周年,在这10年的发展过程中,我们也遇到过各种各样的的“工程”问题,为了解决这些问题,我们引入了组件化开发,而Rubik就是在这个过程中诞生的。关于Rubik
Rubik由百度网盘团队出品,从2019年开始建设,至今已经迭代了两个大版本,并在多款线上产品中得到应用。 Rubik是一套解决Android平台组件化的综合框架,也就是说,Rubik在帮助我们实现模块解耦的同时,还能能够提供一些组件管理能力。Rubik所提供的模块解耦能力是一种基于“函数路由”的组件间低耦合通讯方案。组件管理能力能够帮助我们实现组件的版本控制、maven发布、aar/jar与源码之间的切换等能力,Rubik还可以通过配置文件,更简便的把现有的组件,组合成不同的APK。
关于组件化
关于组件化开发,有很多不同的定义。在这里,定义并不重要,我们更应该关心,对于客户端开发而言,组件化开发所能解决的最核心的问题是什么。我们认为,组件化开发的核心,在于两点:- 隔离:让业务组件之间保持相对的独立性
- 复用:复用业务组件形成可运行软件
怎样才算彻底的组件隔离?
让业务组件之间保持独立性,通常可以与代码层面上的“解耦”划上等号,如果两个业务的代码混杂在一起,缺乏对边界的定义,肯定是代码隔离不彻底的表现。

所以,在组件化开发中,组件之间应该保持没有代码耦合,在需要互相通信时,使用一种通过接口,间接的依赖的通信方式。

在实际的组件化开发过程中,隔离往往是最难以实际操作的,无论是面对难以维护的历史代码,还是在前期规划不足的新业务。但是,一旦清晰的划分出两个组件间的边界,把彼此的代码隔离开,就会带来一系列的工程方面的好处:
- 明确开发人员的职责:由于对一个模块的修改不会直接影响另一个模块,所以负责开发不同模块的开发人员,只需要对约定好的接口负责,不需要关心其他组件的具体实现。
- 降低测试成本:同样的,对于测试人员而言,如果能够保证对一个组件的修改,只影响组件本身和组件的接口,那么对于没有被修改过的组件,就没有回归测试的必要了。
- 提升编译速度:在实际开发中,可以让大部分组件提前编译成二进制,只让少数经常变更的组件保持源码状态。
- 故障隔离:当一个组件出现故障,能够做到不影响其他组件的正常运行。
组件级别的复用应该做到什么程度?
组件级别的复用,指的是在不同应用程序之间,复用已有的业务组件。这往往都是为了快速搭建新项目,并且最大限度的保证新老项目的代码的一致性,从而降低代码的维护成本。所以,组件化开发框架,应该能够做到对现有的组件进行储备,并且在新项目启动时,通过简单的配置,对已有的组件进行筛选,快速的生成新的应用程序。

Rubik在组件化中的作用?
Rubik由两部分组成,一部分是解决组件间低耦合通信的Rubik Router模块,另一部分是基于Gradle Plugin实现的Rubik工具链,负责解决组件管理和依赖管理方面的问题。Rubik Router:基于Kotlin DSL的“函数”路由
Rubik在依赖倒置与依赖注入的基础上,实现了一套基于Uri的路由通信方式,与一般的页面路由或四大组件路由不同,Rubik Router允许把Uri及参数,导航到组件内部任意的一个公开的Java或Kotlin函数的执行上。Rubik Router的选择以函数而非Android中的四大组件为路由的终点,主要基于三方面考虑:- 灵活性:在实际开发中,组件的边界通常不是简单的页面跳转,有可能是Api的调用或数据、实例的获取,相比于传统的页面路由,“函数”路由可以更加轻量级的满足这些需求。
- 可扩展性:“函数”路由有更低的层次,使用者可以在函数的基础上延伸更多的用法。
- 一致性:对于路由调用者而言,路由的终点无论是函数、页面还是数据,Rubik Router都提供一致的调用方式。

@RRoute(path = "user")
fun getUser(id : Int, name : String) : User? {
…
}
用元注解把函数注册到路由
navigate {
uri = "app://com.account/user"
query {
"id" with 400
"name" with "zhangsan01"
}
result<User?> { user ->// 通过泛型指定接收数据类型
…
}
}
通过Kotlin DSL调用其他组件提供的接口
Rubik Plugins:基于Gradle Plugin的组件管理和依赖管理工具
Rubik gradle plugins 提供了组件定义、版本控制、maven发布、二进制依赖与源码依赖切换等能力,包括4个gradle plugin:-
rubik:
-
提供全局定义组件的能力,并根据全局定义自动启用rubik-context、rubik-root等插件
rubik插件工程结构
rubik { component { uri "app://com.cloud-file" // uri 是组件的唯一 id,和路由根路径 dependencies {// 组件所依赖的其他组件uri uri ("app://com.local-file" ) uri ("app://com.upload" ) } source {// 定义的多种来源 project (":lib-cloud-file") maven { // 其他组件依赖自己的默认版本 version "0.2.0" variant "english-debug" } } } component { … }// 继续定义下一个组件 }
组件的定义方式
-
提供全局定义组件的能力,并根据全局定义自动启用rubik-context、rubik-root等插件
-
rubik-root:
- 给App工程提供筛选组件能力,根据flavor、版本号筛选要打包进apk的业务组件
- 提供组件的源码工程和aar切换的能力

rubik {
packing {
uri ("app://com.cloud-*") { // 筛选范围,uri表示用uri筛选,支持*匹配任意字符
projcetMode () // 筛选方式, projcetMode通过工程筛选一些组件
}
uri ("app://com.preview-file") {
mavenMode {// 筛选方式, mavenMode通过maven依赖aar筛选一些组件
version "0.2.0"
variant "netdisk-english-debug"
}
}
……
}
}
筛选组件的方式
-
rubik-context:
- 提供把业务代码按flavor、版本号编译成aar 并发布到maven的能力
- 提供辅助函数路由,把中间代码打包成context.jar ,并按版本号发布到maven的能力,并根据全局定义,为组件自动添加其他组件的中间代码依赖

-
rubik-test:
- 给工程提供单元测试环境
最后
希望Rubik能够帮助大家更便捷的实现组件化开发,也欢迎大家进行代码和技术层面的交流,如果你觉得我们做的还不错,请小伙伴们不要吝惜star、fork和watching: https://github.com/baidu/Rubik