Android模块化开发配置

栏目: IOS · Android · 发布时间: 4年前

内容简介:当一个项目越来越大,越来越复杂后,代码量就会变得越来大,难以阅读难以维护,业务之间出现耦合的可能性也会越来越大,同时整个APP编译调试的时间也会越来越长。而使用模块化开发则可以解决以上问题:但是…这篇文章不是教你如何进行模块化开发,而是介绍如何进行模块化开发的工程配置,以满足模块化开发过程中的多团队协作问题。
  1. 项目由多个模块组成
  2. 每个模块都是一个独立的Feature或组件
  3. 业务模块间解耦不相互直接依赖
  4. 业务模块与公共组件模块通过aar依赖
  5. 每个模块独立开发,独立运行调试

模块化的好处

当一个项目越来越大,越来越复杂后,代码量就会变得越来大,难以阅读难以维护,业务之间出现耦合的可能性也会越来越大,同时整个APP编译调试的时间也会越来越长。

而使用模块化开发则可以解决以上问题:

  1. 项目代码结构清晰,每个Feature和公共组件都是一个独立的Library模块
  2. 避免每个Library模块间的直接耦合
  3. 提升模块的复用性
  4. 单个模块独立编译调试速度更快,节省开发时间
  5. 只关注自己所在的模块,从而避免其他Feature的异常block自己的Feature开发

但是…这篇文章不是教你如何进行模块化开发,而是介绍如何进行模块化开发的工程配置,以满足模块化开发过程中的多团队协作问题。

模块化开发配置

我们先创建一个Android工程,这个工程除了有一个app的主module之外,还有两个library类型的module,工程结构如下图:

Android模块化开发配置

上图中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模块化开发配置

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

游戏编程算法与技巧

游戏编程算法与技巧

【美】Sanjay Madhav / 刘瀚阳 / 电子工业出版社 / 2016-10 / 89

《游戏编程算法与技巧》介绍了大量今天在游戏行业中用到的算法与技术。《游戏编程算法与技巧》是为广大熟悉面向对象编程以及基础数据结构的游戏开发者所设计的。作者采用了一种独立于平台框架的方法来展示开发,包括2D 和3D 图形学、物理、人工智能、摄像机等多个方面的技术。《游戏编程算法与技巧》中内容几乎兼容所有游戏,无论这些游戏采用何种风格、开发语言和框架。 《游戏编程算法与技巧》的每个概念都是用C#......一起来看看 《游戏编程算法与技巧》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码