Android组件化架构 - 9.Gradle优化

作者: 今阳说 | 来源:发表于2020-12-30 08:44 被阅读0次

    gradle本质是一个自动化构建工具, 使用基于groovy的特定领域语言来声明项目设置;

    1. 根目录的gradle文件
    buildscript {//构建脚本引用
        ext {
            kotlin_version = '1.3.72'
        }
        repositories {//插件仓库配置
            google()
            jcenter()
            mavenCentral()
        }
        dependencies {//依赖插件
            //google的Android Gradle插件
            classpath 'com.android.tools.build:gradle:3.5.3'
    
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
    
            //美团walle多渠道打包插件
            classpath 'com.meituan.android.walle:plugin:1.1.6'
            //kotlin插件
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
            //greendao插件
            classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
            //ARouter插件
            classpath "com.alibaba:arouter-register:1.0.2"
        }
    }
    
    
    allprojects {//全部项目的配置
        repositories {//全部项目引用的基础仓库配置
            google()
            jcenter()
            mavenCentral()
            maven { url 'http://developer.seedland.cc/nexus/repository/maven-dev/' }
            maven { url 'https://jitpack.io' }
        }
    }
    
    task clean(type: Delete) {//声明任务
        delete rootProject.buildDir//删除主路径buildDir文件夹
    }
    
    
    1. app module的gradle文件:
    //引入构建需要用到的gradle插件工具库
    //每个build.gradle自身是一个Project对象,project.apply()会加载某个工具库到project对象中
    //apply plugin:'xxx' 会将project对象传递入工具库,然后通过插件中的Groovy文件来操作
    //project对象的属性, 以完善配置初始化信息
    apply plugin: 'com.android.application'
    apply plugin: 'walle'
    apply plugin: 'kotlin-android'
    apply plugin: 'org.greenrobot.greendao'
    apply plugin: 'kotlin-kapt'
    apply plugin: 'com.alibaba.arouter'
    
    //android{} ,dependencies{} 是函数方程式,使用闭包函数的编写方式, 
    //相当于project.android(){} ,project.dependencies(){}
    android {
        compileSdkVersion 30 //编译的sdk
        defaultConfig { //默认配置
            applicationId "com.ljy.publicdemo" //包名
            minSdkVersion 17 //最低支持版本
            targetSdkVersion 30 //支持的目标版本
            versionCode 1 //版本号
            versionName "1.0" //版本名
            multiDexEnabled true //dex分包支持
            //如果应用没有做国际化,可以让应用仅支持 中文的资源配置
            resConfigs "zh"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" //测试脚本
            ndk {
                //选择要添加的对应cpu类型的.so库。
                abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'arm64-v8a', 'x86_64'
                // 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
            }
    
    
            //ARouter配置
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName()]
                }
            }
        }
    
        signingConfigs {//签名配置
            key {
                keyAlias 'key0'
                keyPassword '666666'
                storePassword '666666'
                storeFile file('./ljy_key.jks')
            }
        }
    
        lintOptions {
            // true--关闭lint报告的分析进度
            quiet true
            // true--错误发生后停止gradle构建
            abortOnError false
            // true--只报告error
            ignoreWarnings true
            // true--忽略有错误的文件的全/绝对路径(默认是true)
            //absolutePaths true
            // true--检查所有问题点,包含其他默认关闭项
            checkAllWarnings true
            // true--所有warning当做error
            warningsAsErrors true
            // 关闭指定问题检查
            disable 'TypographyFractions','TypographyQuotes'
            // 打开指定问题检查
            enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
            // 仅检查指定问题
            check 'NewApi', 'InlinedApi'
            // true--error输出文件不包含源码行号
            noLines true
            // true--显示错误的所有发生位置,不截取
            showAll true
            // 回退lint设置(默认规则)
            lintConfig file("default-lint.xml")
            // true--生成txt格式报告(默认false)
            textReport true
            // 重定向输出;可以是文件或'stdout'
            textOutput 'stdout'
            // true--生成XML格式报告
            xmlReport false
            // 指定xml报告文档(默认lint-results.xml)
            xmlOutput file("lint-report.xml")
            // true--生成HTML报告(带问题解释,源码位置,等)
            htmlReport true
            // html报告可选路径(构建器默认是lint-results.html )
            htmlOutput file("lint-report.html")
            //  true--所有正式版构建执行规则生成崩溃的lint检查,如果有崩溃问题将停止构建
            checkReleaseBuilds true
            // 在发布版本编译时检查(即使不包含**重点内容**lint目标),指定问题的规则生成崩溃
            fatal 'NewApi', 'InlineApi'
            // 指定问题的规则生成错误
            error 'Wakelock', 'TextViewEdits'
            // 指定问题的规则生成警告
            warning 'ResourceAsColor'
            // 忽略指定问题的规则(同关闭检查)
            ignore 'TypographyQuotes'
        }
        sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
    
        flavorDimensions "jin"
        productFlavors {
            higher {
                applicationId "com.ljy.publicdemo"
                minSdkVersion 26
                buildConfigField 'boolean', 'isLite', "false"
    //            resValue "string", "app_name_new", "JinDemo"
                manifestPlaceholders = [
                        app_icon    : "@mipmap/ic_launcher_normal",
                        channelName : "higher",
                        verNum      : "2",
                        app_name_new: "JinDemoHigher"
                ]
            }
    
            lower {
                applicationId "com.ljy.publicdemo.lite"
                minSdkVersion 17
                buildConfigField 'boolean', 'isLite', "true"
    //            resValue "string", "app_name_new", "JinDemoLite"
                manifestPlaceholders = [
                        app_icon    : "@mipmap/ic_launcher_lite",
                        channelName : "lower",
                        verNum      : "1",
                        app_name_new: "JinDemoLower"
                ]
            }
        }
    
        buildTypes {
            release {
                minifyEnabled false
                buildConfigField("boolean", "isDebug", "false")
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.key
                android.applicationVariants.all { variant ->
                    variant.outputs.all {
                        outputFileName = "LjyPublicDemo_${variant.buildType.name}_${variant.versionName}.apk"
                    }
                }
            }
            debug {
                minifyEnabled false
                buildConfigField("boolean", "isDebug", "true")
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.key
                android.applicationVariants.all { variant ->
                    variant.outputs.all {
                        outputFileName = "LjyPublicDemo_${variant.buildType.name}_${variant.versionName}.apk"
                    }
                }
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        // 配置数据库相关信息
        greendao {
            //数据库版本号
            schemaVersion 1
            // 设置DaoMaster、DaoSession、Dao 包名
            daoPackage 'com.ljy.publicdemo.greendao'
            //设置DaoMaster、DaoSession、Dao目录
            targetGenDir 'src/main/java'
        }
    
        //gradle的命名提示机制,让各module中资源名必须按一定前缀命名,有助于防止组件化多module时的资源冲突
        resourcePrefix 'app_'
    }
    
    walle {
        // 指定渠道包的输出路径
        apkOutputFolder = new File("${project.buildDir}/outputs/channels");
        // 定制渠道包的APK的文件名称
        apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
        // 渠道配置文件
        channelFile = new File("${project.getProjectDir()}/channel")
    }
    
    tasks.withType(JavaCompile) {
        //在 Gradle 4.10 版本之后便默认使用了增量编译
        //如果在更老的版本需要启动增量编译,可以使用如下配置:
        options.incremental = true
        //解决java控制台输出中文乱码
        options.encoding = "UTF-8"
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
        implementation 'androidx.appcompat:appcompat:1.2.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation project(path: ':mylibrary')
        testImplementation 'junit:junit:4.13'
        androidTestImplementation 'androidx.test.ext:junit:1.1.2'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
        implementation project(':library')   
    }
    
    

    gradle的生命周期分三个阶段:

    1. 初始化: settings.gradle中声明module, 决定哪些工程会加入构建过程,并且创建project对象
    include ':app', ':library',':datepicker',':heigher_setting',':lower_setting',':mylibrary'
    2. 配置: 按引用树去执行所有工程的build.gradle脚本,配置project对象,一个project对象由多个任务组成
    3. 构建: 运行阶段会根据gradle命令传递过来的task名称,执行相关依赖任务
    
    1. 版本参数优化

    每个module的build.gradle文件都拥有一些必要的属性,同一个Android工程中,在不同的模块中要求一些属性一致,
    如compileSdkVersion,如果引用不一致,属性不会被合并引入到工程中,这样一方面会造成资源的重复,包体积增大,另一方面会降低编译效率;
    那么就需要有一个统一的,基础的gradle配置

    • 优化方案 1: 使用共同参数的方式进行配置
    //创建一个 common_config.gradle文件
    project.ext {
        compileSdkVersion = 30
        buildToolsVersion = "30.0.0"
        minSdkVersion = 17
        targetSdkVersion = 30
        applicationId = "com.ljy.publicdemo"
        versionCode = 1
        versionName ="1.0"
    }
    
    //在module的gradle首行引用common_config,并通过类似静态变量的方式引用属性
    apply from: "${rootProject.rootDir}/common_config.gradle"//引用额外配置
    apply plugin: 'com.android.library'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
    
    android {
        compileSdkVersion project.ext.compileSdkVersion
        buildToolsVersion project.ext.buildToolsVersion
    
        defaultConfig {
            minSdkVersion project.ext.minSdkVersion
            targetSdkVersion project.ext.targetSdkVersion
            versionCode project.ext.versionCode
            versionName project.ext.versionName
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            consumerProguardFiles "consumer-rules.pro"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    
    • 优化方案 2: 使用Android对象配置
    //android{}中提供了android这个变量,那么可以进一步简化代码,在common_config.gradle的
    //project.ext中添加一个闭包方法来指定project对象的变量
    project.ext {
        compileSdkVersion = 30
        buildToolsVersion = "30.0.0"
        applicationId = "com.ljy.publicdemo"
        minSdkVersion = 17
        targetSdkVersion = 30
        versionCode = 1
        versionName = "1.0"
        setDefaultConfig = {
            extension -> //闭包参数extension相当于android对象
                extension.compileSdkVersion compileSdkVersion
                extension.buildToolsVersion buildToolsVersion
                extension.defaultConfig {
                    applicationId applicationId
                    minSdkVersion minSdkVersion
                    targetSdkVersion targetSdkVersion
                    versionCode versionCode
                    versionName versionName
    
                    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
                    consumerProguardFiles "consumer-rules.pro"
                }
                extension.dataBinding {
                    enabled true
                }
        }
    } 
    //在bulid.gradle中就可以使用 setDefaultConfig了,类似属性设置,实际上是函数调用
    apply from: "${rootProject.rootDir}/common_config.gradle"//引用额外配置
    apply plugin: 'com.android.library'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
    
    android {
        //使用闭包函数输入android这个对象到函数中
        project.ext.setDefaultConfig android
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    • 优化方案 3: 使用project对象配置(最终版本)
    //common_config.gradle:
    project.ext {
        compileSdkVersion = 30
        buildToolsVersion = "30.0.0"
        applicationId = "com.ljy.publicdemo"
        minSdkVersion = 17
        targetSdkVersion = 30
        versionCode = 1
        versionName = "1.0"
    
        //设置app module 配置
        setAppDefaultConfig = {
            extension ->
                //引用application插件库
                extension.apply plugin: 'com.android.application'
                extension.description "app"
                setAndroidConfig extension.android
                setDependencies extension.dependencies
        }
    
        //设置lib module 配置
        setLibDefaultConfig = {
            extension ->
                //引用Lib插件库
                extension.apply plugin: 'com.android.library'
                extension.description "lib"
                setAndroidConfig extension.android
                setDependencies extension.dependencies
        }
    
        //设置android配置
        setAndroidConfig = {
            extension -> //闭包参数extension相当于android对象
                extension.compileSdkVersion compileSdkVersion
                extension.buildToolsVersion buildToolsVersion
                extension.defaultConfig {
                    minSdkVersion minSdkVersion
                    targetSdkVersion targetSdkVersion
                    versionCode versionCode
                    versionName versionName
    
                    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
                    consumerProguardFiles "consumer-rules.pro"
                    //ARouter配置
                    javaCompileOptions {
                        annotationProcessorOptions {//路由每个模块的名称
                            arguments = [AROUTER_MODULE_NAME: extension.project.getName()]
                        }
                    }
                }
                extension.dataBinding {
                    enabled true
                }
        }
    
        //设置依赖
        setDependencies={
            extension ->
                extension.implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
                //每个module都需要应用路由的apt插件库才能生成相应代码,这里无需重复编写每个module的gradle
                extension.annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
        }
    }
    
    //build.gralde中只需要传入project对象到闭包函数中即可
    
    1. 调试优化

    业务模块调试, 将单一模块作为app启动, 然后用于调试测试, 这样保证了单独模块可以分离调试;

    1. 业务模块是library module, 需要将apply plugin: 'com.android.library' 
      变为 apply plugin: 'com.android.application',
      将模块作为application module才能引入app构建流程;
    2. 每个application都需要配置applicationId
    3. androidMainfest中需要配置默认启动的Activity
    <activity android:name=".datepicker.DatePickerActivity"
        android:theme="@style/Theme.AppCompat">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    4. common_config中声明控制模块是否调试的变量
    project.ext {
        compileSdkVersion = 30
        buildToolsVersion = "30.0.0"
        minSdkVersion = 17
        targetSdkVersion = 30
        versionCode = 1
        versionName = "1.0"
    
        //控制组件模块调试
        isDatePickerDebug=true
    }
    5. 在模块的build.config中将变量作为开关
    apply from: "${rootProject.rootDir}/common_config.gradle"
    
    if (project.isDatePickerDebug) {
        project.ext.setAppDefaultConfig project//设置app配置
    } else {
        project.ext.setLibDefaultConfig project//设置lib配置
    }
    
    android{
        sourceSets {
            main {
                if (project.isDatePickerDebug) {
                    manifest.srcFile 'src/debug/AndroidManifest.xml'
                    res.srcDirs = ['src/debug/res', 'src/main/res']
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                    resources {
                        exclude 'src/debug/*'
                    }
                }
            }
        }
    }
    
    6. 原 app module 下,需要移除已经单独调试的模块的依赖
      if (!project.isDatePickerDebug) {
        implementation project(':datepicker')
      }
    
    1. 资源引用配置
      gradle有多种资源引用的方式

    (1) 使用sourceSets指定文件路径

        sourceSets {
            main {
                manifest.srcFile 'src/main/AndroidManifest.xml' //AndroidManifest路径
                jniLibs.srcDirs = ['libs']//so库路径
                resources.srcDirs = ['src']//全部资源文件路径
                aidl.srcDirs = ['src']//aidl文件路径
                renderscript.srcDirs = ['src']//renderscript文件路径
                res.srcDirs = ['res']//res资源文件路径
                assets.srcDirs = ['assets']//asset资源文件路径
                
            }
        }
    

    (2)可以动态添加res资源,在 buildTypes,productFlavors 中定义 resValue 变量,
    resValue只能动态添加资源,不能替换,如果资源名重复,gradle会提示重复资源

    resValue "string", "app_name_new", "JinDemo"
    

    (3) 可以指定特定尺寸的资源,在 buildTypes, productFlavors 中定义 resConfigs

    android {
        ...
        defaultConfig {
            ...
            resConfigs "zh","en"//剔除不需要的国际化
            resConfigs "hdpi","xhdpi","xxhdpi","xxxhdpi"//剔除自身,三方库,sdk中不需要的dpi资源
            ndk {
                //选择要添加的对应cpu类型的.so库。
                abiFilters 'armeabi', 'armeabi-v7a' //, 'x86', 'arm64-v8a', 'x86_64', 'mips', 'mips64'
            }
        }
    }
    

    (4) productFlavors中,通过buildConfigField可以让代码直接读取到BuildConfig中的值,通过manifestPlaceholders配合AndroidManifest中的meta-data可以间接让代码读取到AndroidManifest的变量

      productFlavors {
            higher {
                buildConfigField 'boolean', 'isLite', "false"
                manifestPlaceholders = [
                        app_icon    : "@mipmap/ic_launcher_normal",
                        channelName : "higher",
                        verNum      : "2",
                        app_name_new: "JinDemoHigher"
                ]
            }
      }
    

    Gradle加载优先级

    在build.gradle中,有四个函数,优先级由高到低为:

    buildTypes > productFlavors > Main > dependencies
    
    我是今阳,如果想要进阶和了解更多的干货,欢迎关注公众号”今阳说“接收我的最新文章

    相关文章

      网友评论

        本文标题:Android组件化架构 - 9.Gradle优化

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