组件化分析

559 阅读3分钟

一. 架构演进综述

graph LR
单模块+分包-->模块化
模块化-->组件化
组件化-->插件化

项目初期启动,业务形态单一,简单的单一模块+分包就能满足业务开发需求,

随着用户的增多,部分业务变得复杂,比如登录,网络通信,统计上报,社会分享等,简单分包会导致依赖不清晰,从而进化到模块化开发,将不同的业务组织成模块,模块与模块之前存在依赖关系,模块之间可以互相调用

随着业务的变更,在追求研发效能的今天,新的项目启动会要求能够快速迭代,因此希望能够复用已有的能力,模块的设计由于存在相互的依赖,导致不容易抽离出来作为aar被依赖,组件化的需求诞生,组件化梳理清楚组件之间的依赖,并且通过反射的形式进行组件通信,达到快速复用

随着业务的继续壮大,很多的业务包含多个组件,并且业务形态可能在多个产品线上被复用,如何能够让单一团队来维护业务,插件化的需求诞生,插件化能够将业务打包成独立的apk,被不同的产品依赖。

一. 组件化的目标

组件化是随着业务不断壮大之后的必然结果,组件化不能舍本逐末,它的本质在于:

  • 1.项目解耦合,提升组件的原子性
  • 2.组件的高可用,提升研发效率

二. 组件化的分层设计

  • 1.分层设计思路

按照是否包含UI和是否包含业务来区分为四个象限,象限依赖关系为 1 -> 4 -> 2 -> 3 四象限设计思路

  • 2.分层设计概览

根据分层设计思路,与依赖关系,得到分层设计结果

分层设计概览

三. 组件化的操作流程

  • 1.抽象公共的config.gradle

config.gradle被所有的组件依赖,它根据组件的设计和调试模式来确定组件的类型

if (isModule() || isApp()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
  • 2.对于不同的组件,为了防止资源冲突,我们定义资源的命名方式,强制以模块的名作为资源的前缀
if (!isApp()) {
    resourcePrefix "${project.name}_"
}
  • 3.当组件处于调试模式时,需要单独的applicationId
if (isApp()) {
    applicationId c.applicationID
} else if (isModule()) {
    applicationId "${c.applicationID}.${this.name}"
} else {
    println("found lib ==  com.huya.${this.name}")
}
  • 4.对于组件,在调试模式下和组件模式下,需要对源码和manifest文件做区分
sourceSets {
    main {
        if (isApp() || isLib()) {
            manifest.srcFile 'src/main/AndroidManifest.xml'
        } else {
            if (isModule()) {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                java {
                    //release 时 runonly 目录下文件不需要合并到主工程
                    exclude '*runonly/**'
                }
            }
        }

        jniLibs.srcDirs = ['libs']
    }
}
  • 5.为了方便管理依赖版本,将所有的依赖版本抽象到version.gradle中
ext {
c = [
        compileSdkVersion: 29,
        buildToolsVersion: "29.0.3",
        minSdkVersion    : 21,
        targetSdkVersion : 29,
        versionCode      : vCode,
        versionName      : version,

        VSupportSdk      : '28.0.0',
        VRetrofitSdk     : "2.4.0",
        VOkhttp          : "3.11.0",
        VRxlifecycle     : "3.1.0",
        VRxJava          : "2.1.0",
        VGlide           : "4.12.0",
        vConstraint      : "1.1.3",
        VButterknife     : "10.2.2",
        VExoplayer       : "2.9.2",
        VOkDownload      : "1.0.7",
        applicationID    : packageName
]

commons_io = 'commons-io:commons-io:2.4'

butterknife = "com.jakewharton:butterknife:${c["VButterknife"]}"
b
}

四. 组件化的通信与数据依赖

由于组件之间存在通信,通信的数据内容组织方式包含两种

  • 1.通用格式 如pb , json , jce等
  • 2.格式化结构 xxBean

由于业务请求的数据常常被封装成bean,因此在传输过程中需要频繁的拆装箱,为了解决这个问题,将数据模块抽象出来,让所有的组件都依赖与数据模块,这样所有的数据结构可以在不同的组件之间流通,这种方式对组件本身不友好,但是可以极大的减少数据传输的序列化与反序列化的消耗

五. 组件化的调试与发布

通过控制开关来设置组件模式,当设定为组件模式时,可以单独运行,否则可以被依赖

# is_Debug=true,设置其他组件true,则为组件调试模式
# is_Debug=false ,app模式
is_Debug=false
is_umeng=false
is_user=false
is_oss=false
is_video=false
is_laboratory=false
is_huyavideo=false
is_push=false
is_videoedit=false
is_dlna=false

六. 组件见通信

采用ARouter解耦各个模块的依赖

七. 组件生命周期

AppLifecycle 用于将壳工程Application的生命周期分发到各个业务组件