内容简介:当一个项目越来越大,越来越复杂后,代码量就会变得越来大,难以阅读难以维护,业务之间出现耦合的可能性也会越来越大,同时整个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模块化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
QBasic语言程序设计教程(第2版习题解答)
刘瑞新、丁爱萍 / 电子工业出版社 / 1999-6-1 / 13.00
本书是《QBasic语言程序设计教程》(第二版)一书的配套教材、本书第一部分以概要的形式,对全书进行了总结,以便学生复习。在第二部分中,对《QBasic语言程序设计教程》(第二版)中的习题做了详尽的分析与解答。 本书也可作为QBasic语言的习题研单独使用。一起来看看 《QBasic语言程序设计教程(第2版习题解答)》 这本书的介绍吧!