美文网首页从零开始学习SpringBootAndroid架构安卓组件化开发
从零开始Android组件化改造(二) - Gradle的管理

从零开始Android组件化改造(二) - Gradle的管理

作者: BzCoder | 来源:发表于2019-04-30 00:46 被阅读2次

    我的Github:https://github.com/BzCoder
    欢迎各位留言讨论

    Gradle的管理在组件化改造中是一个非常有学问的环节。在我看来Gradle在其中的主要几个职责:

    • 引入包的版本管理
    • 组件化编译与总体编译的切换
    • 各模块间的层级关系维护
    • gradle.properties 配置中转站

    接下来我们就一点一点的讲。

    1.引入包的版本管理

    这其实不是组件化开发的专利。正如其他的项目一样,统一的版本号我们都管理在Config.gradle中。类似下面的文件。在模块中引入模块时,统一通过类似api rootProject.ext.dependencies["mmkv"]的引入方式来保证版本的统一。这个很好理解,因为都是常规操作。

    ext {
    
        android = [
                applicationId    : "${PACKAGE_NAME}",
                compileSdkVersion: 28,
                buildToolsVersion: "28.0.3",
                minSdkVersion    : 18,
                targetSdkVersion : 28,
                versionCode      : VERSION_CODE as int,
                versionName      : "${VERSION_NAME}"
        ]
    
        version = [
                androidSupportSdkVersion: "28.0.0",
                retrofitSdkVersion      : "2.4.0",
                dagger2SdkVersion       : "2.19",
        ]
    
        dependencies = [
                //support
                "appcompat-v7"             : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
                "design"                   : "com.android.support:design:${version["androidSupportSdkVersion"]}",
                "support-v4"               : "com.android.support:support-v4:${version["androidSupportSdkVersion"]}",
                "cardview-v7"              : "com.android.support:cardview-v7:${version["androidSupportSdkVersion"]}",
                "annotations"              : "com.android.support:support-annotations:${version["androidSupportSdkVersion"]}",
                "recyclerview-v7"          : "com.android.support:recyclerview-v7:${version["androidSupportSdkVersion"]}",
                "constraint-layout"        : 'com.android.support.constraint:constraint-layout:1.1.3',
    ]
    
      defaultConfig {
            minSdkVersion rootProject.ext.android["minSdkVersion"]
            targetSdkVersion rootProject.ext.android["targetSdkVersion"]
            versionCode rootProject.ext.android["versionCode"]
            versionName rootProject.ext.android["versionName"]
            testInstrumentationRunner rootProject.ext.dependencies["androidJUnitRunner"]
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                    includeCompileClasspath true
                }
            }
    
            multiDexEnabled true
        }
    

    2.组件化编译与总体编译的切换

    2.1 App与Lib

    我们知道,lib模块需要引入apply plugin: 'com.android.library',应用模块引入apply plugin: 'com.android.application',而在组件化开发中,这两种状态是要不断地切换的,所以我们可以在gradle.properties设定参数isBuildModule来控制。于是gradle的头部就变成了以下

    if (isBuildModule.toBoolean()) {
        apply plugin: 'com.android.application'
    
    } else {
        apply plugin: 'com.android.library'
    }
    

    当然在实际开发中我们设计了多层,相应的我们也给每层建立了基础gradle,参数也随之变成了isBuildModuleOne,isBuildModuleTwo,isBuildModuleThree...每一层的业务只要依赖该层基础gradle即可。

    2.2 两套Manifest

    组件化编译和整体编译,他们的清单文件也是不同的。我们通过在Gradle中android节点中加入以下代码来控制工程使用两套Manifest,但是这种方案美中不足的是,你的每个Activity的注册都必须要写两次

     sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
                if (isBuildModule.toBoolean()) {
                    manifest.srcFile 'src/main/debug/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/release/AndroidManifest.xml'
                }
            }
        }
    

    2.3 不同编译方式时导入不同的包

    说到包的导入,我们先必须明确两个关键字的概念。

    • runtimeOnly:只有在运行时才编译。
    • compileOnly:只有编译的时候引入,实际打包时并不会加入到包当中。

    根据以上原则
    在模块互相导入时,我们使用如下格式:

     if (moduleTv.toBoolean()) {
                runtimeOnly project(":moduleTv")
            }
    
    

    这样写,首先(if语句)可以通过配置文件来确认某个模块是否加入,使用runtimeOnly ,可以在某一个Module组件化编译时,不会影响其他引用它的模块。

    在通用模块导入时,我们使用如下格式:
    butterknife,dagger2等的包引入。

       if (isBuildModule.toBoolean()) {
            //view
            annotationProcessor(rootProject.ext.dependencies["butterknife-compiler"]) {
                exclude module: 'support-annotations'
            }
            //tools
            annotationProcessor rootProject.ext.dependencies["dagger2-compiler"]
            annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
            //test
            debugImplementation rootProject.ext.dependencies["canary-debug"]
            releaseImplementation rootProject.ext.dependencies["canary-release"]
            testImplementation rootProject.ext.dependencies["canary-release"]
        } else {
            compileOnly rootProject.ext.dependencies["butterknife-compiler"]
            compileOnly rootProject.ext.dependencies["dagger2-compiler"]
            compileOnly rootProject.ext.dependencies["arouter-compiler"]
            compileOnly rootProject.ext.dependencies["canary-debug"]
            compileOnly rootProject.ext.dependencies["canary-release"]
        }
    

    这样写只有在组件化编译的时候,模块才会真正把基础包引入,整体打包时,只有一份。

    3.Manifest与gradle.properties的数据桥梁

    因为我们目标想把所有的配置文件都整合到gradle.properties中,但是manifest又不能直接调用gradle.properties中的参数,我们就必须借助gradle作为“中间人”。我们在gradle的defaultconfig节点中加入(以下为推送的写法)

     manifestPlaceholders = [
                    GETUI_APP_ID    : "${PUSH_APPID}",
                    GETUI_APP_KEY   : "${PUSH_APPKEY}",
                    GETUI_APP_SECRET: "${PUSH_APPSECRET}"
    ]
    

    这样一来,在Manifest中就可以取到gradle.properties 的参数了。

    总结

    Gradle的配置总的来说不是太难,以上方案也存在可以优化的地方,就是Manifest,携程曾经有一篇文件是利用manifest的tools:node ="remove"来进行manifest的合并,但是很可惜,在我自己实践的过程中,遇到了不少困难,最终没有采用携程的方案。好的,那么今天的文章就先暂时写到这里。

    相关文章

      网友评论

        本文标题:从零开始Android组件化改造(二) - Gradle的管理

        本文链接:https://www.haomeiwen.com/subject/silpnqtx.html