Android Gradle 常用配置

2,656 阅读3分钟

欢迎任何形式的转载,转载请保留原文链接:juejin.cn/post/694834…

本文非教程性质,属于使用查阅与记录,所以看不明白的,请自行百度,这里不解释。当然欢迎指出存在的错误,以及新的用法

本文主要收集与记录 Android 项目常用 Gradle 配置(实际打算包括不常用,请忽略常用两字,手动滑稽)

目录

  • 一些语法
    • 1.1. 加载自定义配置文件
    • 1.2. 引入其他 gradle 文件
  • 阿里云仓库添加
  • 添加单元测试 junit
  • java 8
  • sourceSets
  • 自动签名
  • buildTypes:混淆、资源压缩优化、自定义编译类型等
  • 修改输出 apk 的目录与名称,以及复制 apk 到指定目录
  • 配置自定义常量与资源
  • 多渠道
  • 全局统一管理版本号
  • 多个 module 共用 gradle 配置
  • 参考文献与版本同步

一些语法

不是详细语法,只是记录常用操作的方式

1.1. 加载自定义配置文件

就是读取自定义 xxx.properties 文件里配置的属性了,举例

创建【项目目录/keystore/keystore.properties】文件,并设置以下内容

KEY_APP_STORE_FILE = ../keystore/keystore.jks
KEY_PASSWORD       = release
KEY_ALIAS          = release
KEY_STORE_PASSWORD = release

在 build.gradle 中用以下方式读取

// 获取配置文件路径:
File propertiesFile = file("${rootDir.path}/keystore/keystore.properties") // 方式一
File propertiesFile = project.rootProject.file('keystore/keystore.properties') // 方式二

// 加载配置文件属性
Properties properties = new Properties()
properties.load(propertiesFile.newDataInputStream())

// 使用属性
storeFile file(properties['KEY_APP_STORE_FILE']) // 方式一
storeFile file(properties.getProperty("KEY_APP_STORE_FILE")) // 方式二

注:如果是默认的 gradle.properties 文件,直接使用即可,如该文件存在以下内容

DEV_USER               = "csp"

则 build.gradle 中直接使用

buildConfigField "String", "DEV_USER", DEV_USER

1.2. 引入其他 gradle 文件

两种方式

apply from: "${rootDir.path}/gradle/dependencies.gradle" // 方式一
apply from: rootProject.file('gradle/dependencies.gradle') // 方式二

阿里云仓库添加

添加内容:根目录/build.gradle

buildscript {

    repositories {
        maven { url("https://maven.aliyun.com/repository/public") } // central 仓和 jcenter 仓的聚合仓
        maven { url("https://maven.aliyun.com/repository/google") }
        maven { url("https://maven.aliyun.com/repository/gradle-plugin") }
        google()
        jcenter()
    }
}

allprojects {
    repositories {
        maven { url("https://maven.aliyun.com/repository/public") } // central 仓和 jcenter 仓的聚合仓
        maven { url("https://maven.aliyun.com/repository/google") }
        maven { url("https://maven.aliyun.com/repository/gradle-plugin") }
        google()
        jcenter()
    }
}

添加单元测试 junit

这个默认就有了,这里记录一下

添加文件:项目目录/build.gradle

android {
    defaultConfig {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    // 单元测试
    testImplementation 'junit:junit:4.13.1'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

java 8

android {
    // java 1.8
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

sourceSets

android {

    // sourceSets
    sourceSets {
        main {
            jniLibs.srcDirs 'libs'

            println "\nsourceSets 配置情况:"
            sourceSets.all { set ->
                println "[${set.name}]的文件是 ${set.java.srcDirs}"
            }
        }
    }
}

自动签名

生成签名文件

1. Build -> Generate Signed Bundle / APK
2. Next -> Create new...
3. 填写路径,密码,别名,然后 OK,就生成好了

添加内容:项目目录/build.gradle

android {
    // 自动签名
    signingConfigs {
        println "\n[${project.name}]项目签名配置开始"

        // 加载配置文件
        String propertiesPath = "${rootDir.path}/keystore/keystore.properties"
        File propertiesFile = file(propertiesPath)
        // File propertiesFile = project.rootProject.file('keystore/keystore.properties')
        Properties properties = new Properties()
        properties.load(propertiesFile.newDataInputStream())

        // 配置自动签名
        release {
            storeFile file(properties['KEY_APP_STORE_FILE'])
            // storeFile file(properties.getProperty("KEY_APP_STORE_FILE"))
            storePassword properties['KEY_STORE_PASSWORD']
            keyAlias properties['KEY_ALIAS']
            keyPassword properties['KEY_PASSWORD']
        }

        println "签名配置文件路径:" + propertiesPath
        println "[${project.name}]项目签名配置结束"
    }

    buildTypes {

        debug {
            signingConfig signingConfigs.release // 自动签名
        }

        release {
            signingConfig signingConfigs.release
        }
    }
}

添加文件:配置文件,项目目录/keystore/keystore.properties

KEY_APP_STORE_FILE = ../keystore/keystore.jks
KEY_PASSWORD       = release
KEY_ALIAS          = release
KEY_STORE_PASSWORD = release

buildTypes:混淆、资源压缩优化、自定义编译类型等

android {

    buildTypes {

        release {
            signingConfig signingConfigs.release
            debuggable false // 不可调试
            zipAlignEnabled true // zipalign 优化,minifyEnabled = true 时生效
            shrinkResources true // 删除没有用到的资源,minifyEnabled = true 时生效
            minifyEnabled true // 开启混淆

            // 混淆规则
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        // 自定义编译类型,注意一旦添加自定义编译类型,则所有被引用的 module 都要添加,否则导致除 debug 外其他编译类型无法打包
        pre.initWith(release) // 以 release 为模板
        pre {
            debuggable true
        }
    }
}

关于自定义编译类型

  • 注意一旦添加自定义编译类型,则所有被引用的 module 都要添加,否则导致除 debug 外其他编译类型无法打包

关于混淆,有个自定义混淆

  • 目的:作为 SDK 时,有些内容即便是 app 打包也不能混淆 SDK 里面的内容。为了防止 app 集成时没有添加混淆规则导致的某些问题(如类找不到等),可以采用以下方式解决
  • 优点:使用该方式自定义混淆规则,可以使 app 打包时自动应用 aar 中自带的 proguard 文件,这样就不用让 app 额外添加混淆规则

添加文件:项目目录/build.gradle

android {
    defaultConfig {
        // 自定义混淆规则(SDK 用)
        consumerProguardFiles 'consumer-rules.pro'
    }
}

修改输出 apk 的目录与名称,以及复制 apk 到指定目录

添加内容:项目目录/build.gradle

android {
    // 修改输出 apk 的目录与名称,以及复制 apk 到指定目录
    applicationVariants.all { variant ->

        // 修改输出目录:一般不修改,防止 Studio 直接运行报到不到文件错误
//        File outDir = new File(project.buildDir, "outputs/apk")
//        def artifact = variant.getPackageApplicationProvider().get()
//        artifact.outputDirectory = outDir
//
//        println "\n输出 apk 目录:" + outDir.getAbsolutePath()

        // 修改输出 apk 文件名
        variant.outputs.all {
            String now = new Date().format("yyyy-MM-dd", TimeZone.getDefault())
            outputFileName = "${project.name}-v${versionName}-${variant.flavorName}-${variant.buildType.name}-${now}.apk"

            println "输出 apk 名称:" + outputFileName
        }

        // 复制输出 apk 到指定文件夹
        variant.assemble.doLast {
            File outDir = new File(project.buildDir, "outputs/apk")

            variant.outputs.forEach { file ->
                copy {
                    from file.outputFile
                    into outDir
                    // rename {
                    //     String fileName -> "${project.name}-v${versionName}-${variant.flavorName}-${variant.buildType.name}-${now}.apk"
                    // }
                }

                println "\n复制 apk:" + file.outputFile
                println "到指定目录:" + outDir.getAbsolutePath()
            }
        }
    }
}

配置自定义常量与资源

添加内容:项目目录/build.gradle

添加位置不止以下,也可以在 build.gradle 的其他地方

android {

    defaultConfig {
        println "\n[${project.name}]配置自定义常量与资源中..."

        // 读取自定义配置文件
        Properties properties = new Properties()
        properties.load(file("${rootDir.path}/keystore/keystore.properties").newDataInputStream())

        // 自定义常量
        def assembleTime = new Date().format("yyyy-MM-dd HH:mm", TimeZone.getDefault())

        buildConfigField "String", "ASSEMBLE_TIME", "\"${assembleTime}\""
        buildConfigField "String", "DEV_USER", DEV_USER
        buildConfigField "String", "KEY_ALIAS", "\"${properties.getProperty("KEY_ALIAS")}\""

        // 自定义资源
        resValue "string", "app_name", "Sample"

        println "[${project.name}]配置自定义常量与资源结束"
    }

添加内容:根目录/gradle.properties

DEV_USER               = "user"

添加文件:配置文件,项目目录/keystore/keystore.properties

KEY_APP_STORE_FILE = ../keystore/keystore.jks
KEY_PASSWORD       = release
KEY_ALIAS          = release
KEY_STORE_PASSWORD = release

修改内容:项目目录/.../strings.xml

<resources>
    <!-- 已通过 build.gradle 自定义,故以下内容注释 -->
    <!-- <string name="app_name">Sample</string> -->
</resources>

多渠道

添加内容:项目目录/build.gradle

android {

    defaultConfig {
        // 多渠道用
        flavorDimensions "applicationId"
    }

    // 多渠道
    productFlavors {
        // 正式版本
        publish {
        }

        // 开发渠道
        dev {
            applicationIdSuffix ".dev" // 修改包名
        }

        // 测试渠道
        dtest {
        }
    }

    // 多渠道:根据渠道,自定义常量与资源
    applicationVariants.all { variant ->

        def buildType = variant.buildType // 编译类型
        def flavorName = variant.flavorName // 渠道名称

        // 开发意图、测试意图、生产意图
        def isDev = flavorName.startsWith("dev")
        def isTest = flavorName.startsWith("dtest")
        def isRelease = !isTest && !isDev

        // 调试总开关
        def isDebuggable = isDev || isTest || buildType.debuggable || buildType.name == "debug"

        // 应用名称
        def appName = "Sample"
        if (isDebuggable)
            appName = appName + "(${variant.flavorName})"

        // 自定义常量与资源
        resValue "string", "app_name", "${appName}"

        buildConfigField "boolean", "IS_DEBUGGABLE", "${isDebuggable}" // 请不要把 IS_DEBUGGABLE 改成 DEBUGGABLE,避免混淆
        buildConfigField "boolean", "IS_DEV", "${isDev}"
        buildConfigField "boolean", "IS_TEST", "${isTest}"
        buildConfigField "boolean", "IS_RELEASE", "${isRelease}"

        println "\n[${project.name}]多渠道:根据渠道,配置自定义常量与资源情况:"
        println "编译渠道:${variant.flavorName}"
        println "编译类型:" + variant.buildType.name
        println "应用名称:" + appName
        println "调试总开关:" + isDebuggable
        println "开发意图:" + isDev
        println "测试意图:" + isTest
        println "生产意图:" + isRelease
    }
}

全局统一管理版本号

添加文件:依赖配置文件,项目目录/gradle/dependencies.gradle

ext {
    // sdk version
    compileSdkVersion = 30
    minSdkVersion = 19
    targetSdkVersion = 30

    // java version
    sourceCompatibilityVersion = JavaVersion.VERSION_1_8
    targetCompatibilityVersion = JavaVersion.VERSION_1_8

    dep = [
            // 基本
            appcompat       : 'androidx.appcompat:appcompat:1.2.0',
            recyclerview    : 'androidx.recyclerview:recyclerview:1.1.0',
            constraintlayout: 'androidx.constraintlayout:constraintlayout:2.0.4',

            // 测试单元
            junit           : 'junit:junit:4.13.1',
            junit_test      : 'androidx.test.ext:junit:1.1.2',
            espresso_core   : 'androidx.test.espresso:espresso-core:3.3.0',
    ]
}

添加内容:根目录/build.gradle

apply from: "${rootDir.path}/gradle/dependencies.gradle"

修改内容:项目目录/build.gradle

android {

    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
    }

    // java 1.8
    compileOptions {
        sourceCompatibility rootProject.ext.sourceCompatibilityVersion
        targetCompatibility rootProject.ext.targetCompatibilityVersion
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    implementation dep.appcompat
    implementation dep.recyclerview
    implementation dep.constraintlayout

    // 单元测试
    testImplementation dep.junit
    androidTestImplementation dep.junit_test
    androidTestImplementation dep.espresso_core
}

多个 module 共用 gradle 配置

添加文件:通用配置文件,根目录/gradle/config.gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    implementation dep.appcompat

    testImplementation dep.junit
    androidTestImplementation dep.junit_test
    androidTestImplementation dep.espresso_core
}

修改内容:项目目录/build.gradle

apply from: "${rootDir.path}/gradle/config.gradle"

android {

    defaultConfig {
        versionCode 1
        versionName "1.0.0"
    }
}

dependencies {

    // 其他 Module
    implementation project(':lib-utils')
}

可使用场景

  • 解决 annotationProcessor 不能共用的问题(该编译指令不能像 api 一样能对外可见),不过一般也不是这么用的,这里只是做个记录

参考文献与版本同步

参考文献:

  • 没有专门的

博客同步版本:

  • 2021-04-07:首发博客