彻底的组件化方案-58同城Android端

现有架构的问题

1. 58App 架构

现有架构存在如下几个问题:
  • 编译速度慢
  • 业务线无法独立开发、调试
  • 耦合严重,多平台组件复用难度大
  • 重构、替换实现成本高
  • 测试范围边界难以界定
  • 需求需要改动多模块
  • 厂商包、极速包改造难度大
  • 业务组件间通信只能异步 (walle)

2. SDK 架构

包含公司级 SDK、APP 工厂 SDK、业务中间件 SDK

现有架构存在如下几个问题:
  • 对外 API 与实现耦合,无法快速实现替换,重构难度大
  • 对外 API 范围未约束,测试时无法界定测试范围
  • 增加迭代过程中的向下兼容成本

彻底的组件化设计

可以比较流行的框架 Fresco 的分包设计:

特点:

接口与实现分离,符合面向对象的开闭、依赖倒置原则,高层模块不依赖低层模块,两者都依赖其抽象;抽象不依赖细节,细节依赖抽象

1. 方案设计

(1) 新架构图
(2) 依赖关系图
(3) 组件结构图

一个独立的组件工程包含如下的结构:

  • 依赖关系: 实现库依赖 api 库,demo 依赖 api 库 & 实现库,demo 中具体业务只能从 api 库中调用相关能力

  • api 库: 包含对外的接口、共用的资源、view 以及工具类,工具类中除了包含普通工具外,还需要提供页面跳转与接口实例获取能力

  • 实现库: 依赖于 api 库,实现对外暴露的接口

(4) 解耦方式
  • 页面跳转解耦: 路由框架

  • 组件间通信解耦: 服务暴露组件,依赖注入实现,做到同步通信

  • 业务组件如何管理 Application: 使用 Application 分发管理框架

  • 依赖注入如何实现作用域配置: WBRouter 路由框架的依赖注入每次都是新对象,Hilt 更加灵活,可以通过注解配置作用域:单例、Activity 作用域、Fragment 作用域、View 作用域,但 Hilt 框架过重,可以对 WBRouter 进行扩展

  • 通用组件初始化: 最上层进行统一初始化,下层业务只通过 api 进行调用

(5) 支持快速发布 aar

aar 要求:备份、名称规则

  • 本地发布测试 aar
  • ici 发布正式的 aar
(6) 支持两种调试方式
  • 独立运行,走自己独立的测试
  • 嵌入运行,基于复合构建的方式,快速替换主工程里的 aar 依赖
(7) 测试的标准
  • api 库变动时,所有的依赖库都要测试
  • lib 库变动时,只需要独立功能测试

2. demo

以下组件均为独立工程,拥有 demo,可直接运行,上图为复合构建模式视图

project描述
58AppProject打包工程
58HouseLib房产业务组件
58JobLib招聘业务组件
WubaBasicBussinessLib基础业务组件
WubaCommonsLib基础业务组件
WubaShareSDKApp 工厂组件
PassportSDK集团基础组件
(1) 效果图
(2) demo 架构图
(3) 代码示例

打包工程依赖:

dependencies {
    // 基础组件 - passport sdk
    implementation 'com.wuba.sdk:passport-api:1.0.1'
    implementation 'com.wuba.sdk:passport-lib:1.0.1'

    // APP 工厂 - 分享 sdk
    implementation 'com.wuba.wuxian.sdk:share-api:1.0.0'
    implementation 'com.wuba.wuxian.sdk:share-lib:1.0.0'
    
    // 业务基础组件 - commons lib
    implementation 'com.wuba.wuxian.lib:commons-api:1.0.2'
    implementation 'com.wuba.wuxian.lib:commons-lib:1.0.2'

    // 业务基础组件 - basic business lib
    implementation 'com.wuba.wuxian.lib:basicbussiness-api:1.0.1'
    implementation 'com.wuba.wuxian.lib:basicbussiness-lib:1.0.1'

    // 业务组件 - house lib
    implementation 'com.wuba.wuxian.lib:house-api:1.0.0'
    implementation 'com.wuba.wuxian.lib:house-lib:1.0.0'

    // 业务组件 - job lib
    implementation 'com.wuba.wuxian.lib:job-api:1.0.0'
    implementation 'com.wuba.wuxian.lib:job-lib:1.0.0'
}

58JobLib 中 api 库依赖:

dependencies {
    // 只依赖路由框架,路由跳转与依赖注入使用
    kapt "com.wuba.wuxian.sdk.wbrouter:compiler:${WBRouterVersion}"
    api "com.wuba.wuxian.sdk.wbrouter:core:${WBRouterVersion}"
}

58JobLib 中 lib 库依赖:

dependencies {
    // 依赖 job api 库
    implementation project(':job-api')

    // 依赖组件 api
    implementation 'com.wuba.sdk:passport-api:1.0.1'
    implementation 'com.wuba.wuxian.sdk:share-api:1.0.0'
    implementation 'com.wuba.wuxian.lib:commons-api:1.0.1'
    implementation 'com.wuba.wuxian.lib:basicbussiness-api:1.0.1'
}

招聘业务组件调用登陆功能 (passport-api):

LoginClient.launch(this)

passport-api 相关接口定义:

object LoginClient {
    // 通过 ARouter 获取注入的接口例
    private fun getLoginClientService(context: Context): ILoginClientService? {
        val loginClientService = WBRouter.navigation(context, PassportRouters.LOGIN_CLIENT_ROUTER)
        return if (loginClientService != null) loginClientService as ILoginClientService else null
    }

    @JvmStatic
    fun register(context: Context, callback: LoginCallback?) {
        getLoginClientService(context)?.register(callback)
    }

    @JvmStatic
    fun unregister(context: Context, callback: LoginCallback?) {
        getLoginClientService(context)?.unregister(callback)
    }

    @JvmStatic
    fun onLoginCallback(context: Context) {
        getLoginClientService(context)?.onLoginCallback()
    }

    @JvmStatic
    fun launch(context: Context) {
        getLoginClientService(context)?.launch(context)
    }

    @JvmStatic
    fun logoutAccount(context: Context) {
        getLoginClientService(context)?.logoutAccount(context)
    }

    @JvmStatic
    fun isLogin(context: Context): Boolean {
        return getLoginClientService(context)?.isLogin(context) ?: false
    }

    @JvmStatic
    fun getPPU(context: Context): String {
        return getLoginClientService(context)?.getPPU(context) ?: ""
    }

    @JvmStatic
    fun getUserID(context: Context): String {
        return getLoginClientService(context)?.getUserID(context) ?: ""
    }
}

passport-lib 相关实现类:

// 自动注入
@Route(PassportRouters.LOGIN_CLIENT_ROUTER)
class LoginClientService : ILoginClientService {
    override fun register(callback: LoginCallback?) {
        // ...
    }

    override fun unregister(callback: LoginCallback?) {
         // ...
    }

    override fun onLoginCallback() {
         // ...
    }

    override fun launch(context: Context) {
        // ...
    }

    override fun logoutAccount(context: Context) {
        // ...
    }

    override fun isLogin(context: Context): Boolean {
        // ...
    }

    override fun getPPU(context: Context): String? {
        // ...
    }

    override fun getUserID(context: Context): String? {
        // ...
    }
}

可以看到,组件间交互都是通过 api 层,lib 层通过 ARouter 自动注入,当然需要对 ARouter 进行扩展支持注入对象生命周期管理,使用 ARouter 可以方便的通过路由获取 Activity、Fragment、接口实现等

api 层除了包含对外的 api、公用工具类、页面跳转方法、接口实例获取方法外,还可以包含公用的资源、View 等。lib 层包含具体的实现、个性化的定制等

3. 收益

  • 加快编译速度

每个业务功能都是一个单独的工程,可独立编译运行,拆分后代码量较少,编译自然变快,如果能推动业务线进行改造,业务线编译速度预计可提升 50% 以上

  • 增强组件复用能力

组件类似我们引用的第三方库,只需维护好每个组件,一建引用集成即可。业务组件可上可下,灵活多变;而基础组件,为新业务随时集成提供了基础,减少重复开发和维护工作量。在多 APP 垂直共用业务越来越多,集团新 APP 快速涌出的挑战下,开发、迭代效率预计提升 30% 以上

  • 提高协作效率

解耦使得组件之间彼此互不打扰,组件内部代码相关性极高。 团队中每个人有自己的责任组件,不会影响其他组件;降低团队成员熟悉项目的成本,只需熟悉责任组件即可

  • 提高QA测试效率,降低风险

对测试来说,只需重点测试改动的组件,而不是全盘回归测试,测试效率预计可提升 20% - 30%

  • 灵活多变、快速重构

不同 APP 拥有个性化实现,厂商包、极速包严控包大小,面向接口编程,快速灵活响应,替换底层第三方库无需修改上层业务

为了更直观地阐述收益点,我们列出下具体的业务场景:

#场景收益
1编译速度对于业务线、业务相关组件编译速度将大幅提升,预计提升 50% 以上
2提升已有垂直共用业务迭代效率垂直业务库维护成本降低,开发、调试、迭代效率提升 30% 以上,如部落、房产、本地版垂直库
3快速响应新的垂直业务接入如去年本地版接入房产业务,后续此类场景可做到快速响应
4新 App 快速集成58到家等新 App 快速集成
5提升 App 工厂迭代效率aar 发布模式优化可快速定位问题,API 库对于向下兼容、旧有代码清理、单元测试等都有收益,而 API 与实现分离可以做到代码重构、底层库替换成本最低
6提升公司 SDK 迭代效率参考App 工厂
7提升 QA 测试效率API 层与实现层,快速界定是否需要上层业务回归测试,缩小测试范围,减少隐藏 bug 风险
8厂商包、极速包对于包大小控制严格的厂商包、极速包可快速删减功能、替换实现
9个性化业务不同 APP 可根据 API 层快速定制个性化业务
10精简包大小降低删除无用代码、资源风险,减少重复功能代码
11API 变动监控API 分离后可通过脚本快速监控变化

4. 缺点

  • 改造好后,如何持续按规范维护
  • 改造成独立的组件后,通用库版本号如何在各组件项目、平台项目迭代过程中保持统一
  • 接口与实现隔离,排查问题时无法快速关联实现类定位,如断点
  • 底层组件升级时,按照什么标准决定上层使用业务需要同步发布 aar 或者测试
  • 增加编码工作量与类数量,对外功能、跳转功能、接口实例都需要编写对应接口与工具类

5. 推进难点

(1) App工厂 SDK
  1. 多达十几个,开发工作量大
  2. App工厂 SDK 大多属于基础能力,对外 API、视图、工具众多,API 梳理难度大,如 Hybrid、RN 这种重型的框架
  3. 测试工作量大:App工厂 SDK 在同城、本地版、安居客、58到家、驾校 App 等都有使用,进行大规模重构势必涉及到多平台共同测试,否则后期使用方平台无法升级迭代

优点是:内部维护,拥有绝对的主动权

(2) 公司级 SDK
  1. 具备 App 工厂 SDK 的所有难点
  2. 由 TEG 负责,有跨部门成本,需要很高的收益点和数据进行推动
(3) 基础业务组件
  1. 对外 api 多,梳理成本高
  2. 使用业务较多,多 app 间存在个性差异
  3. 重业务,解耦不彻底

优点为目前部分库还未进行 SDK 化改造,属于计划中的任务,同时属于内部维护

(4) 垂直库
  1. 依赖于底层库的组件化改造
  2. 重业务,解耦不彻底
  3. 功能复杂,依赖的组件多,改造工作量大
  4. 涉及的平台较多,需要同时测试

优点是:业务功能独立,对外接口较少,内部维护

(5) 业务线
  1. 依赖于底层库的组件化改造
  2. 有跨部门成本,需要很高的收益点和数据进行推动
  3. 功能复杂,依赖的组件多,改造工作量大

建议:逐个突破,新老框架并存过度。可以从基础业务组件作为入口 (原本在改造计划中,只是按照新模式进行改造),第二改造优先级为部落垂直库,第三改造优先级为 App 工厂 SDK,最后再以收益数据向业务线、公司级 SDK 进行推广

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值