为什么会出现模块化和组件化?
单工程项目的缺陷
随着应用的迭代更新,业务代码持续增加,随之而来的问题逐步呈现出来:
1、各种业务代码混杂在同一个模块里,开发人员在开发、调试过程的效率越来越低,比如定位某个业务问题,需要在多个业务代码混合的模块寻找和跳转。
2、工程师需要了解各个业务的功能,避免代码的改动影响其他业务的功能,导致开发和维护成本不断增加。
3、由于项目工程越来越大,编译完整代码所花费的时间越来越长。
4、多人协作开发时,开发风格不一,又很难将业务完整分开,大家互相影响,导致开发效率低下。
5、代码复用性差,写过的代码很难抽离出来再次利用。
模块化
如何实现模块化
这里先梳理模块化的实现步骤,后面再具体讲解什么是模块化以及模块化与组件化的区别。
如果只是一个依赖库 在gradle配置里面 -> id 'com.android.library' -> 不可运行的库 apk包
id 'com.android.application' ->可运行的程序 arr包
添加模块依赖步骤
image.png image.png
配置了模块依赖之后,“壳”工程的gradle中就会出现下方的依赖代码
implementation project(path ':模块名')
最外层的壳App module配置的是Application插件,业务组件module配置的是Library插件。想要实现业务组件的独立调试,这就需要把配置改为Application插件;而独立开发调试完成后,有需要变回Library插件进行集成调试。
如何让组件在这两种调试模式之间自动转换
1、手动配置更改
2、设置变量
AndroidStudio创建一个Android项目后,会在根目录中生成一个gradle.properties文件。在这个文件定义的常量,可以被任何一个build.gradle读取。所以我们可以在gradle.properties中定义一个常量值isModule,true为独立调试;false为集成调试。然后在业务组件的build.gradle中读取isModule,设置成对应的插件即可。
组件化(Component)
什么是组件化
组件(Component),是对数据和方法的简单封装,功能单一,高内聚,并且是业务能划分的最小粒度。
组件化是基于可重用的目的,将大型的软件系统按照分离关注点的形式,拆分成多个独立的组件,使得整个软件系统也可以做到像电路板一样,是单个或多个组件元件组装起来,哪个组件坏了,整个系统可以继续运行,而不出现崩溃或不正常现象,做到更少的耦合和更高的内聚。
为什么使用组件化
组件化基于可重用的目的,将应用拆分成多个独立组件,以减少耦合:
- 通过关注点分离的形式,将App分离成多个模块,每个模块都是一个组件。解决了各种业务代码耦合在一起导致的问题。
- 开发的过程中,扔这些组件被其他组件依赖,但是在调试时也可以单独成为独立的工程并且运行,这样就解决了因为编译耗时过多导致开发效率的问题。
- 多人开发中,每个组件由担任负责,降低了开发之间沟通成本,减少因代码风格不一而产生的相互影响。
区分模块化与组件化
模块化就是将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块值包含与其功能相关的内容,比如登录功能可以是一个模块,搜索功能可以是一个模块。
组件化是模块化思想的演进,它们两者本质都是一致的。
- 组件化更注重关注点分离,所谓的关注点分离,就是把复杂的问题做合理分解,再分别仔细研究这些问题的不同关注点,最后综合得到整体解决方案。
- 如果从集合角度来看的话,可以说往往一个模块包含了一个或多个组件,或者说模块式一个容器,由组件组装成。简单来说,组件化相比模块化粒度更小。
区分组件化与插件化
插件化也是基于模块化的思想,将应用拆分成多个模块,而这些模块都是一个APK,最终打包时将宿主APK和插件APK。
插件化与组件化存在很多相似之处,但是它们根本的区别在于:
- 组件化的模块虽然在调试的时候可以切换到application独立运行,但是最终在打包时,每个模块始终只是一个library,整个应用只有单独的一个APK。
- 插件化是拆分出了多个APK,并且在运行时通过动态加载的技术方案,来加载这些APK。
组件化结构
不同的人有不同的组件化方式,这里只是常见组件化的一种
组件化架构
主工程(app)
1、只依赖个业务组件,通过配置指定依赖哪些业务组件;
2、除了一些全局的配置和Activity之外,不包含任何业务代码,是应用的入口;
业务组件层
1、各组件之间无直接关联,通过路由进行通信;
2、可直接依赖基础组件层,同时也能依赖公用的一些功能组件;
3、业务组件可以在library和application之间切换,但是最后打包时必须是library;
功能组件层
1、依赖基础组件层
2、对一些公用的功能业务尽心封装与实现;
基础组件层
1、封装公用的基础组件(并且实现了路由);
2、网络访问框架、图片加载框架等主流的第三方库;
3、各种第三方的SDK。
组件化实现步骤
1、创建各个组件层
创建业务组件
moduleCore就是业务组件层
这里进行模拟,创建两个业务组件main和login
image.png创建基础组件层
image.png
创建功能组件
image.png
创建依赖层
image.png
2、如何建立组件之间的业务管理
为什么要建立组件之间的业务管理?
因为各个模块可能会依赖多种第三方的库,以及SDK版本。为了避免因版本不一致的问题而产生依赖冲突。那么实现版本的统一管理就需要抽离公共组件和开源项目。
抽取公共组件、开源项目
在整个项目gradle中添加ext(自己的命名)
isDebug变量的作用:使模块更加灵活地在调试与编译打包之间转换。手动的转换过程已经在上方模块化实现过程中说明。(下方的代码中有涉及到ARouter的依赖代码,将在下一部分讲解)
ext{
isDebug = false //定义变量,当为true的时候模块可以单独运行,否则模块不能单独运行
android = [
compileSdkVersion: 30,
buildToolsVersion:"30.0.3",
targetSdkVersion:30,
minSdkVersion: 19,
versionCode: 1,
versionName: "1.0"
]
applicationId = [
"app" : "com.example.component",
"main": "com.example.module.main",
"login":"com.example.module.login"
]
library = [
"appcompat": 'androidx.appcompat:appcompat:1.2.0',
"materail": 'com.google.android.material:material:1.2.1',
"constraintlayout": 'androidx.constraintlayout:constraintlayout:2.0.4'
]
libARouter = "com.alibaba:arouter-api:1.5.1"
libARouterCompiler = "com.alibaba:arouter-compiler:1.5.1"
libGson = "com.google.code.gson:gson:2.8.6"
}
在libBase中(依赖层),更换一切公共版本组件以及第三方依赖的代码。
api与implementation的区别:api会向上层传递,所以在以来层中需要使用api依赖第三方库传递给其它层的组件。
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
}
def cfg = rootProject.ext
android {
compileSdkVersion cfg.android.compileSdkVersion
buildToolsVersion cfg.android.buildToolsVersion
defaultConfig {
minSdkVersion cfg.android.minSdkVersion
targetSdkVersion cfg.android.targetSdkVersion
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
api cfg.library.appcompat
api cfg.library.materail
api cfg.library.constraintlayout
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
api cfg.libARouter
}
applicationId的值没有必要和文件名一样,只是身份的一个象征,当我们在安装的时候发现已经存在相同的applicationId那么新的应用将对原来的应用进行覆盖。
在modulesCore中(服务组件层)
这里与依赖层有点小区别,首先是apply plugin与plugins:当使用plugins时,其必须置顶,也就是说其头部不能有任何其他的代码。
其次是服务层涉及到调试与编译打包的切换,只有应用程序才有applicationId,所以依赖层不需要切换。
image.png image.png
main组件的gradle
def cfg = rootProject.ext
if (cfg.isDebug){
apply plugin:'com.android.library'
apply plugin:'kotlin-android'
apply plugin: 'kotlin-kapt'
}else {
apply plugin:'com.android.application'
apply plugin:'kotlin-android'
apply plugin: 'kotlin-kapt'
}
android {
compileSdkVersion cfg.android.compileSdkVersion
buildToolsVersion cfg.android.buildToolsVersion
defaultConfig {
if (!cfg.isDebug){
applicationId cfg.applicationId.main
}
minSdkVersion cfg.android.minSdkVersion
targetSdkVersion cfg.android.targetSdkVersion
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main{
if (cfg.isDebug){
mainfest.srcFile 'src/main/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':modulesBase:libBase')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
annotationProcessor cfg.libARouterCompiler
}
login组件的gradle
def cfg = rootProject.ext
if (cfg.isDebug){
apply plugin:'com.android.library'
apply plugin:'kotlin-android'
apply plugin: 'kotlin-kapt'
}else {
apply plugin:'com.android.application'
apply plugin:'kotlin-android'
apply plugin: 'kotlin-kapt'
}
android {
compileSdkVersion cfg.android.compileSdkVersion
buildToolsVersion cfg.android.buildToolsVersion
defaultConfig {
if (!cfg.isDebug){
applicationId cfg.applicationId.login
}
minSdkVersion cfg.android.minSdkVersion
targetSdkVersion cfg.android.targetSdkVersion
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
main{
if (cfg.isDebug){
mainfest.srcFile 'src/main/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':modulesBase:libBase')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
annotationProcessor cfg.libARouterCompiler
}
功能组件层
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
}
def cfg = rootProject.ext
android {
compileSdkVersion cfg.android.compileSdkVersion
buildToolsVersion cfg.android.buildToolsVersion
defaultConfig {
minSdkVersion cfg.android.minSdkVersion
targetSdkVersion cfg.android.targetSdkVersion
versionCode cfg.android.versionCode
versionName cfg.android.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(':modulesBase:libBase')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
app“壳”中:如果不是调试模式,那么就对业务组件进行依赖
image.png
网友评论