内容简介:当一个项目越来越大,越来越复杂后,代码量就会变得越来大,难以阅读难以维护,业务之间出现耦合的可能性也会越来越大,同时整个APP编译调试的时间也会越来越长。而使用模块化开发则可以解决以上问题:但是…这篇文章不是教你如何进行模块化开发,而是介绍如何进行模块化开发的工程配置,以满足模块化开发过程中的多团队协作问题。
- 项目由多个模块组成
- 每个模块都是一个独立的Feature或组件
- 业务模块间解耦不相互直接依赖
- 业务模块与公共组件模块通过aar依赖
- 每个模块独立开发,独立运行调试
模块化的好处
当一个项目越来越大,越来越复杂后,代码量就会变得越来大,难以阅读难以维护,业务之间出现耦合的可能性也会越来越大,同时整个APP编译调试的时间也会越来越长。
而使用模块化开发则可以解决以上问题:
- 项目代码结构清晰,每个Feature和公共组件都是一个独立的Library模块
- 避免每个Library模块间的直接耦合
- 提升模块的复用性
- 单个模块独立编译调试速度更快,节省开发时间
- 只关注自己所在的模块,从而避免其他Feature的异常block自己的Feature开发
但是…这篇文章不是教你如何进行模块化开发,而是介绍如何进行模块化开发的工程配置,以满足模块化开发过程中的多团队协作问题。
模块化开发配置
我们先创建一个Android工程,这个工程除了有一个app的主module之外,还有两个library类型的module,工程结构如下图:
上图中module1和module2就代表了两个不同的业务module
模块化开发配置需要解决哪些问题呢?
module配置参数化
大家都知道一个项目的主module存在一个build.gradle文件,里面有如下内容:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "xxxxxxxxx"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
....
}
而工程中的library类型的moudle也有一个build.gradle文件,它的内容如下:
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
...
}
通过上面的配置内容可以发现主module和library类型的module除了plugin不一样之外,主module会比library类型module只多一个applicationId。
所以如果我们想让某个library类型的module能独立运行调试,我们可以通过参数控制,动态的为该module添加相关配置,具体脚本如下:
//common-build.gradle
project.ext {
mainModuleType = "mainModule"//主module
debugLibraryModuleType = "debugLibraryModule"//可单独运行的library module
libraryModuleType = "libraryModule"
//根据module类型动态添加对应的配置
configModuleGradleScript = { moduleType ->
applyPlugin(moduleType)
applyAndroidDefaultConfig()
applyApplicationId(moduleType)
}
//配置module的编译版本相关配置
applyAndroidDefaultConfig = {
project.android.compileSdkVersion compileSdkVersion
project.android.defaultConfig.targetSdkVersion targetSdkVersion
project.android.defaultConfig.minSdkVersion minSdkVersion
}
//根据module类型动态添加对应plugin
applyPlugin = { moduleType ->
if(moduleType == libraryModuleType) {
project.apply plugin: 'com.android.library'
project.description "library"
} else {
project.apply plugin: 'com.android.application'
project.description "app"
}
project.apply plugin: 'kotlin-android'
project.apply plugin: 'kotlin-android-extensions'
}
//根据module类型动态添加对应的applicationId
applyApplicationId = { moduleType ->
if(moduleType == mainModuleType) {
project.android.defaultConfig.applicationId applicationId
} else if(moduleType == debugLibraryModuleType) {
project.android.defaultConfig.applicationId applicationId + "." + project.name
}
}
}
可以看到所有的动态配置脚本我都是通过在 project.ext 中添加闭包实现的,这样做的好处是在其他脚本文件中也可以引用project.ext中定义的闭包和变量
另外上面的配置中,在配置可单独调试library module时,我对其 applicationId 添加一个工程名称作为后缀,这样可以对主app的 applicationId 进行区分。
上面脚本里的注释提到了 可单独运行的library module ,那这是什么意思呢?
我们每个工程打包为apk时只能有唯一一个plugin为 com.android.application 的主module,而其他需要集成的moudle的plugin均为 com.android.library ,当开发某个业务module(library类型)时,我们需要该module能单独运行以方便我们调试同时节省编译时间,这时我们就需要通过gradle参数控制,将其plugin暂时变为 com.android.application 以便使其能独立运行,所以这个时候该module也是一个主module,但为了与apk的主module进行区分,所以我把它叫做可单独运行的library module
那具体如何通过参数控制将某个library module暂时变为可单独编译运行的module,而在集成的时候又设置为library类型的module呢?我们可以在工程根目录的 gradle.properties 文件中添加参数进行控制
//gradle.properties文件 debugLibraryModules=[module1]
上面的 debugLibraryModules 参数将module名称为 module1 的module设置为可单独运行的library module,这个参数是一个数组,可以配置多个module。当这个数组为空的时候就代表不设置任何library module。
通过这个参数我们就可以在每个module工程加载脚本时判断当前module是否为可单独编译运行的module,判断方法如下:
project.ext {
//通过module的名称进行判断
isDebugLibraryModule = { projectName ->
def debugLibraryModuleList = debugLibraryModules
return project.hasProperty('debugLibraryModules') && debugLibraryModuleList.indexOf(projectName) != -1
}
}
到这里,我们的参数化动态配置脚本的基础已经完成了,接着要针对主module和library module进行具体的工程配置。
配置app的主module
//main-module-build.gradle
configModuleGradleScript(mainModuleType)
getRootProject().getSubprojects().each {item ->
if(item.name != project.name && !isDebugLibraryModule(item.name)) {
project.dependencies.add("implementation", project(":$item.name"))
}
}
因为app的主module是不可能变的,所以该module的moduleType肯定是 mainModuleType
另外app在集成其他library module时必须要将其他library module添加为主module的工程依赖,所以上述脚本中使用 getRootProject().getSubprojects() 先找到工程的所有module,然后将library module都动态添加为主module的工程依赖
主module的 build.gradle 内容如下:
apply from: "${rootProject.rootDir}/buildScript/common-build.gradle"
apply from: "${rootProject.rootDir}/buildScript/main-module-build.gradle"
android {
defaultConfig {
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
在该工程的主module动态添加工程源码依赖时,也可以通过参数来控制某个module到底是采用工程源码依赖,还是采用远程仓库的aar依赖,以此来满足不同的业务需要。这篇文章就不介绍了,相信看懂这篇文章的同学应该能自己实现这个需求。
配置library module
//library-module-build.gradle
def getModuleType() {
if(isDebugLibraryModule(project.name)) {
return debugLibraryModuleType
} else {
return libraryModuleType
}
}
configModuleGradleScript(getModuleType())
在配置library module时,要先判断当前library moduel是否已经通过参数设置为可单独运行的module,如果是的话则该module调用闭包 configModuleGradleScript 的参数为 debugLibraryModuleType ,否则则为 libraryModuleType
library module的 build.gradle 内容如下
apply from: "${rootProject.rootDir}/buildScript/common-build.gradle"
apply from: "${rootProject.rootDir}/buildScript/library-module-build.gradle"
android {
defaultConfig {
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
if(isDebugLibraryModule(project.name)) {
manifest.srcFile 'src/debug/AndroidManifest.xml'
java.srcDirs = ['src/debug/java', 'src/main/java']
res.srcDirs = ['src/debug/res','src/main/res']
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
resources {
//排除java/debug文件夹下的所有文件
exclude 'src/debug/*'
}
}
}
}
}
上述脚本在配置可单独运行的library module时,虽然已经动态将其plugin设置为 com.android.application 也添加了 applicationId ,但是该module依旧还不能单独运行,因为我们一开始创建的library module中的 AndroidManifest.xml 文件没有配置也不能配置启动 Activity ,所以需要在library module的 buld.gradle 脚本中添加如下脚本:
sourceSets {
main {
if(isDebugLibraryModule(project.name)) {
manifest.srcFile 'src/debug/AndroidManifest.xml'
java.srcDirs = ['src/debug/java', 'src/main/java']
res.srcDirs = ['src/debug/res','src/main/res']
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
resources {
//排除java/debug文件夹下的所有文件
exclude 'src/debug/*'
}
}
}
}
上面的脚本先检查当前module是否为可单独运行的library module,如果是则采用 src/debug/AndroidManifest.xml 文件。要注意的是该文件必须要包含’src/main/AndroidManifest.xml’文件的所有内容,同时还要设置启动 Activity 。
到这里,模块化开发的工程配置介绍完了,有兴趣的同学可以在这 https://github.com/huyongli/AndroidModuleDesign 查看完整代码
原创文章,严禁随意转载。欢迎大家添加个人微信讨论交流,添加时请备注:博客。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Android模块化改造以及模块化通信框架
- Laravel 模块化开发模块 – Caffienate
- 前端模块化架构设计与实现(二|模块接口设计)
- 模块化编程的实践者 disconver 更新了用户模块
- ASP.NET Core模块化前后端分离快速开发框架介绍之4、模块化实现思路
- JavaScript模块化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
TED Talks Storytelling: 23 Storytelling Techniques from the Best
Akash Karia / CreateSpace Independent Publishing Platform / 2015-1-11 / USD 6.99
"Every speaker can put these ideas into practice immediately -- and they should!" ~ Dr. Richard C. Harris, Certified World Class Speaking Coach "An insightful read" ~Dennis Waller, Top 500 Revie......一起来看看 《TED Talks Storytelling: 23 Storytelling Techniques from the Best》 这本书的介绍吧!