# Android组件化工程模板 **Repository Path**: shamohaidao/MyComponentization ## Basic Information - **Project Name**: Android组件化工程模板 - **Description**: Android组件化工程模板 - **Primary Language**: Android - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2019-09-27 - **Last Updated**: 2022-07-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 我的组件化之路 本项目是一个工程组件化的模板,旨在将业务逻辑拆分为组件单独完整运行。 ## 组件间因依赖关系而引发的独立运行的问题 本工程依赖关系如下 ![project dep](./doc/project_dep.png "依赖关系") 实际项目中经常会遇到组件Component1依赖组件Component2的情况,本项目旨在模拟并解决此类关系的组件独立运行问题。 ## 项目结构 如上图建立相应项目结构 * app 完整的项目入口 * build.gradle 独立完整项目打包配置文件 * base 所有组件都依赖的公共库目录 * common_library 所有组件都依赖的公共库(gradle方式引入示例、包含基础类封装) * other_library1 所有组件都依赖的公共库(源码方式引入示例) * other_library2 同1 * business 所有业务组件目录 * component1 业务组件1工程目录 * src 业务组件1核心代码 * ***as_app 业务组件1独立运行所需的配置目录(包含启动逻辑、测试资源和入口)*** * main 业务组件1具体业务逻辑 * ***build.gradle 业务组件1配置文件*** * component2 业务组件2工程目录 * src 业务组件2核心代码 * ***as_app 业务组件2独立运行所需的配置目录*** * main 业务组件2具体业务逻辑 * ***build.gradle 业务组件2配置文件*** * build.gradle 工程全局配置文件 * ***config.gradle 组件配置文件*** * settings.gradle 项目路径配置文件 ## 目录划分 编辑`settings.gradle`配置文件,进行模块目录整理,将公共依赖库放入`base`目录,将业务组件放入`business`目录 ## 组件化配置 新建`config.gradle`文件,在`ext`下定义: * `isAllComponentsMixedIn` 所有组件是否汇总到整体app运行的开关 控制项目是否整体运行, * true产出一个包含所有功能的app,所有组件不能单独运行 * false下面定义的组件可单独运行,(当所有组件asApp配置为false时,此开关将无视false的取值,自动默认为true) * `Component1 2 ... ` 自定义的组件描述信息,必须先声明组件(对象名自定义) 后面的`AsAppDependencies`才找的到 定义组件信息包含3个方面: * `asApp`是否作为app单独运行开关 * `name` 当前组件的工程名 * `path` 当前组件的被依赖时的路径 * `AsAppDependencies`组件间依赖关系 某些组件必须依赖其他组件才能运行,比如支付组件依赖登录组件,在这种情况下,必须定义这样的依赖关系,才能确保让组件能够完整独立运行。定义格式形如: * `host` 单独运行的组件信息 * `need` 单独运行`host`所指组件依赖的其他组件列表 * 举例来说: 1. 想运行整体项目,则配置 ``` isAllComponentsMixedIn = true ``` 即可,此时无视其后的任何组件相关配置,`sync now`之后如图: ![run all](./doc/runAll.png "整体运行") 2. 只想运行Component2,则配置 ``` isAllComponentsMixedIn = false Component2 = [ asApp: true, name : "component2", path : ":business:component2" ] ``` 同时依赖Component2的其他组件全置`asApp:false`,`sync now`之后如图: ![run2](./doc/run2.png "只运行组件2") 3. 只想运行Component1,我们假定Component1依赖Component2才能独立运行,则配置 ``` isAllComponentsMixedIn = false Component1 = [ asApp: true, name : "component1", path : ":business:component1" ] Component2 = [//必须先定义好,后面的AsAppDependencies才能找到组件2的信息 asApp: true, //此时组件2的asApp无论为何值都会自动改为false name : "component2", path : ":business:component2" ] AsAppDependencies = [ [ host: Component1, need: [Component2]//如果需要更多组件,此处类似数组,逗号分隔加上其他组件的定义即可 ] //如果需要更多组件间依赖关系,此处类似数组,逗号分隔加上其他组件依赖的定义即可 ] ``` 配置完成后,`sync now`之后如图: ![run1](./doc/run1.png "只运行组件1") ## 组件介绍 上面说了那么多,是不是还是云里雾里,不知道为啥要那么写?好的,我们先看看简单的组件2 #### 组件2的目录结构 ![c2](./doc/c2.png "组件2目录结构") #### 从Android Studio新建的Module是怎样变为Component2 的 1. 新建空的Android项目 2. 在项目根目录下新建business文件夹 3. 新建名为Component2的Module 4. 修改项目根目录下的`settings.gradle`文件,删除 `':component2'`,然后`sync now` 5. 此时Component2变成了普通文件夹,拖拽Component2文件夹到business里 6. 再修改`settings.gradle`文件,增加`':business:component2'`,然后`sync now` 7. 再在Component2的src里增加as_app目录,内容见源码 #### 组件2的秘密 好了,一切秘密都在组件2的`build.gradle`里,看看它是怎么配置的吧。实际上修改的地方只有4处: 1. 根据`config.gradle`的配置,决定组件2采用什么插件: ``` if (!rootProject.ext.isAllComponentsMixedIn && rootProject.ext.Component2.asApp) { apply plugin: 'com.android.application' } else { apply plugin: 'com.android.library' } ``` 2. 当组件2独立运行时包名是什么: ``` if (!rootProject.ext.isAllComponentsMixedIn && rootProject.ext.Component2.asApp) { applicationId "com.cjz.component2" } ``` 3. 当组件2独立运行时需要的代码和资源路径有哪些: ``` sourceSets { main { if (!rootProject.ext.isAllComponentsMixedIn && rootProject.ext.Component2.asApp) { manifest.srcFile 'src/as_app/AndroidManifest.xml' java.srcDirs = ['src/as_app/java','src/main/java'] res.srcDirs = ['src/as_app/res','src/main/res'] } else { manifest.srcFile 'src/main/AndroidManifest.xml' res.srcDirs = ['src/main/res'] java.srcDirs = ['src/main/java'] } } } ``` 4. 组件2的依赖配置 因为组件2的依赖关系比较简单,只依赖公共库 ``` implementation project(":base:common_library") ``` 请先忽略ARouter的相关配置,至此,组件2结构诞生! #### 组件1的目录结构 有了组件2的操作,组件1的结构是一毛一样,此处不计,参考源码 #### 组件1的秘密 组件1的独立运行需要依赖组件2,否则组件1独立运行时在与其依赖的组件2的相关界面上交互操作必然出bug。 比如:支付组件(组件1)依赖登录组件(组件2),支付组件单独运行时必然需要先登录,而支付组件完全"看不见"登录组件的代码,怎么拉 起登录呢(即便配置ARouter,它要么path找不到,要么@Autowired空指针)谈何独立运行呢?为了让组件1独立运行,必须要 ``` implementation project ("组件2") ``` 那么问题来了,组件1依赖辣么多都要手动写吗?我们看看组件1的`build.gradle`是怎么配置的呢?详细见源码,这里说一下, 其他步骤与组件2一样,关键在根据 `config.gradle` 的`AsAppDependencies`依赖配置,自动引入对应的依赖组件: ``` if (!rootProject.ext.isAllComponentsMixedIn) { for (int i = 0; i < rootProject.ext.AsAppDependencies.size(); i++) { //遍历所有组件间依赖配置 def x = rootProject.ext.AsAppDependencies[i] if (x.host.asApp) { if (x.host.name == project.getName() && x.need != null) { //找到当前组件的配置 for (int j = 0; j < x.need.size(); j++) { def y = x.need[j] y.asApp = false //将被依赖的组件禁止独立运行 implementation project(y.path) //引入到当前组件中来 } } } } } ``` # 组件间通信 请参考ARouter