作者:张志付
博客:https://juejin.im/post/5cd179e3f265da039b08850a
组件化背景
一个项目,随着业务的发展,模块会变的越来越多,代码量也会变的异常庞大,进而可能开发的人会越来越多,这种情况下如果还是基于单一工程架构,那就需要每一个开发者都熟悉所有的代码,而且代码之间耦合严重,一个模块穿插着大量其他业务模块的逻辑,严重的话可能使项目处于牵一发而动全身,不想轻易修改的局面;而且庞大的单一工程项目会导致编译速度极慢,开发者长时间等待编译结果,非常不利于开发工作。所以,就需要一个灵活的架构来解决这些问题,组件化架构思想应运而生。
整体结构
common:基础组件部分,与业务无关,需要所有组件共同依赖的部分,如:网络请求封装、图片加载封装、ui相关基类、工具集合等(当然这些内容可以依据分层原则放在不同的基础module中)
router-comp:路由驱动组件,承载整个项目的路由工作
comp1:业务组件1,如视频组件,可独立运行
comp2:业务组件2,如新闻组件,可独立运行
comp3:业务组件3,如视频组件,可独立运行
app:壳工程,用于将各个组件组装成一个完成app
组件化所面临的问题:
集成模式与组件模式转换(热插拔)
组件之间页面跳转(路由)
组件之间通信、调用彼此服务
打包混淆
组件化的实现
针对上面所说的几个问题,下面我们逐个说明它们的解决方案,当解决完这些问题,你会发现,你已经搭建了一个基于组件化的项目。下图是一个完整的组件化项目结构:common是基础组件module,作为library存在,需要所有组件依赖;comp1、comp2作为组件存在,可配置成library或可独立运行的module;app是个壳,通过组装组件实现其价值。
集成模式与组件模式转换(热插拔)
Android工程通过gradle构建,通过配置每个module的gradle,来实现module的不同表现。Android Studio的module有两种属性,分别是:
application属性:可独立运行,也就是我们的app
library属性:不可独立运行,被app依赖的库
module属性通过其目录下的gradle文件配置,当module属性为application时,该module作为完整的app存在,可以独自运行,方便编译和调试;当module属性为library时,该module作为一个依赖库,被壳工程依赖并组装成一个app。那么如何让这两种模式可以自动转换呢?如果每次切换模式的时候,都手动去修改每个组件的配置,组件少的情况下还可以接受,组件多了会非常不方便,下面就让我们来聊聊如何实现两种模式的自动转换。
1、首先,声明全局配置变量,来标识module的属性(app or library),如在工程目录下的build.gradle文件中声明布尔变量ext.isModule,true代表组件作为可独立运行的app,false代表组件作为被依赖的library,如下所示
buildscript {
ext.kotlin_version = '1.3.21'
ext.isModule = true //true-每个组件都是单独的module,可独立运行 false-组件作为library存在
repositories {
google
jcenter
}
}
2、配置组件的build.gradle文件
//1
if (rootProject.ext.isModule) {
//可独立运行的app
apply plugin: 'com.android.application'
} else{
//被依赖的library
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
//applicationId "com.study.comp1" //2 如果没有,默认包名为applicationId
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
//3
sourceSets {
main {
if(rootProject.ext.isModule){
manifest.srcFile 'src/main/java/module/AndroidManifest.xml'
} else{
manifest.srcFile 'src/main/java/library/AndroidManifest.xml'
java {//移除module包下的代码
exclude 'module'
}
}
}
}
}
上面是截取的组件gradle的部分代码,包含了组件化需要配置的所有内容,每一点都进行了注释
注释1:如上所述,根据isModule的值,来设置module的属性,作为app or library
注释2:当module属性为library时,不能设置applicationId;当为app时,如果未设置applicationId,默认包名为applicationId,所以为了方便,此处不设置applicationId
注释3:Android Studio会为每个module生成对应的AndroidManifest.xml文件,声明自身需要的权限、四大组件、数据等内容;当module属性为app时,其对应的AndroidManifest.xml需要具备完整app所需要的所有配置,尤其是声明Application和launch的Activity;当module属性为library时,如果每个组件都声明自己的Application和launch的Activity,那在合并的时候就会发生冲突,编译也不会通过,所以,就需要为当前module重新定义一个AndroidManifest.xml文件,不声明Application和launch的Activity,然后根据isModule的值指定AndroidManifest.xml的路径,因此就有了注释3处的写法。为了避免集成模式下的命名冲突,每个文件都以自身module名为前缀来命名会是一个很好的方法。下图是该module的目录
在需要切换module属性的时候改变步骤1处声明的变量值,然后重新编译即可
组件之间页面跳转(路由)
在组件化架构中,不同的组件之间是平衡的,不存在相互依赖的关系(可参考文章开头的架构图)。因此,假设在组件A中,想要跳转到组件B中的页面,如果使用Intent显式跳转就行不通了,而且大家都知道,Intent隐式跳转管理起来非常不方便,所以Arouter出现了,并且有强大的技术团队支持,可以放心使用了。那么如何在组件化架构中应用Arouter呢?下面详细来聊一聊
1、依赖处理
在common组件中将Arouter依赖进来,并配置编译参数;在业务组件中引入arouter编译器插件,同时配置编译器参数,下面是Common组件gradle文件的部分片段
//配置arouter编译器参数,每个组件都需配置
kapt {
arguments {
arg("AROUTER_MODULE_NAME