Android Gradle | 二:gradle常用字段解析、以Kotlin构建Gradle

626 阅读4分钟

一、模拟一下gradle的代码风格

首先展示效果

class APP: Application() {
    override fun onCreate() {
        super.onCreate()
        android {
            compileSdk = 32
            buildToolsVersion = "33.0.0"
            ndkVersion = "21.4.7075529"
            defaultConfig {
                applicationId = "com.android"
                minSdk = 23
                targetSdk = 33
                versionCode = 1
                versionName = "1.0.0"
                multiDexEnabled = true
            }
        }
    }
}

具体的实现:

fun android(init: Android.() -> Unit) {
    init.invoke(Android.create())
}

class Android private constructor() {
    companion object {
        private val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Android() }

        @Synchronized
        fun create() = instance
    }
    var compileSdk = 0
    var buildToolsVersion = ""
    var ndkVersion = ""

    fun defaultConfig(init: Config.() -> Unit) {
        val defaultConfig = Config.create()
        init.invoke(defaultConfig)
    }
}
class Config private constructor(){
    companion object {
        private val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Config() }

        @Synchronized
        fun create() = instance
    }
  var applicationId = ""
  var minSdk = 0
  var targetSdk = 0
  var versionCode = 0
  var versionName = ""
  var multiDexEnabled = false
}

很明显,就是通过高级函数,并在高级函数返回当前类的对象就可以实现这种风格的配置文件,当然在Gradle中可能会有些许差异,但本质上就是使用的高级函数。截取了一些源码:

fun productFlavors(action: NamedDomainObjectContainer<ProductFlavorT>.() -> Unit)

fun defaultConfig(action: DefaultConfigT.() -> Unit)

@Incubating
fun signingConfigs(action: NamedDomainObjectContainer<out ApkSigningConfig>.() -> Unit)

二、基础配置在KTS中的表现形式

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    compileSdk = 30
    buildToolsVersion = "33.0.0"
    ndkVersion = "21.4.7075529"

    defaultConfig {
        applicationId = "com.daimajia.gold"
        minSdk = 23
        targetSdk = 30
        versionCode = 64200
        versionName = "6.4.2"
        multiDexEnabled = true
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    
    buildTypes {
        getByName("release"){
            //混淆
            isMinifyEnabled = true
            // 移除无用的resource文件
            isShrinkResources = true
            //自动图片压缩
            isCrunchPngs = true
            signingConfig = signingConfigs.getByName("release")
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro")
        }
    }
}

dependencies {
    //引用别的模块
    implementation(project(":core"))
    //引用lib文件夹中的jar和arr
    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
}

三、依赖管理中三种引用的区别

implementation:不会传递依赖

compile/api:会传递依赖

api是代替的compile,子项目(被主项目引用的项目)使用该字段引入的库,在主项目中也可以访问

四、项目多元化、构建项目变体

buildTypes与productFlavors都可以实现变体,productFlavors是在每个buildTypes再次变体,我的理解是buildTypes的变体是用于不同环境的变体比如调试版、内测版、发行版,而productFlavors才是用于区分免费版、付费版;国际版、国内版等区分的。

下面会举例buildTypes的使用方法,productFlavors与buildTypes使用方式类似但不完全相同,多了维度等,在此就不列举了,感兴趣的可以去Android开发者官网去看文档。

buildTypes {
    //调试版 只能直接运行的版本 用于开发者调试错误
    getByName("debug"){
    //指定该版本使用debug_beta作为文件目录
        sourceSets.getByName("debug") {
            setRoot("src/debug_beta")
        }
        //指定打包进APK的so文件
        ndk {
            abiFilters.clear()
            abiFilters.addAll(mutableSetOf("armeabi" ,"x86", "x86_64","armeabi-v7a","arm64-v8a"))
        }
    }
    //发布版 使用正式环境的接口
    getByName("release") {
        //混淆
        isMinifyEnabled = true
        // 移除无用的resource文件
        isShrinkResources = true
        //自动图片压缩
        isCrunchPngs = true
        signingConfig = signingConfigs.getByName("release")
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro")
        ndk {
            abiFilters.clear()
            abiFilters.addAll(mutableSetOf("armeabi-v7a","arm64-v8a"))
        }
    }
    //测试版 使用测试环境的接口
    register("beta"){
        sourceSets.getByName("beta") {
            setRoot("src/debug_beta")
        }
        //使用Release的配置
        initWith(getByName("release"))
        //对于自定义的变重需要配置指定替代匹配项,就是你这个主模块有自定义变体了,但是其他模块没有,这个就是当你以beta构建运行时或打包时,其他库是什么变体,比如现在就就是如果我以beta打包,那子模块就是debug,如果没有debug变体,就是release。
        matchingFallbacks += listOf("debug", "release")
    }
    //32位的发布版
    register("release_32"){
        sourceSets.getByName("release_32") {
            setRoot("src/release")
        }
        initWith(getByName("release"))
        ndk {
            abiFilters.clear()
           abiFilters.addAll(mutableSetOf("armeabi-v7a"))
        }
        matchingFallbacks += listOf("release","debug")
    }
    //64位的发布版
    register("release_64"){
        sourceSets.getByName("release_64") {
            setRoot("src/release")
        }
        initWith(getByName("release"))
        ndk {
            abiFilters.clear()
            abiFilters.addAll(mutableSetOf("arm64-v8a"))
        }
        matchingFallbacks += listOf("release","debug")
    }
}

五、为打包的项目重命名

这个没什么好讲的

android.applicationVariants.all {
    val buildType = preBuildProvider.name
    outputs.all {
        if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
            this.outputFileName = "${Config.Build.getPackedName(buildType)}.apk"
        }
    }
}

现在我们的项目已经可以根据不同的变体来打出不同的包了,但是我们突然发现,批量打包时,他们包的位置都是在不同的文件夹下,变体少还好点,如果是中国移动或者是某种不可言说类型的APP变体特别多怎么办,难道要一个一个拖,这时候我们的第一个想法就是改路径啊,很可惜,如果你在重命名的地方该路径的话,IDEA会提示你不能使用绝对路径,下方有源码,可能低版本是可以的,当然也有解决方案,文章中是使用相对路径/../../../../../(上一级目录)来改变输出路径的,这种方法依赖于目录层级不说,也不能打包至指定的文件夹中,所以我们不妨退而求其次,换一种思路,打包完成后运行一个Task将他们移动到指定的文件夹,具体流程会在下一篇文章讲解Task时作为一个例子。

public void setOutputFileName(String outputFileName) {
    if (new File(outputFileName).isAbsolute()) {
        throw new GradleException("Absolute path are not supported when setting " +
                "an output file name");
    }
    variantOutput.getOutputFileName().set(outputFileName);
}

文章参考: Android开发者 Gradle官方文档

上一篇:Android Gradle | 一:gradle基础 下一篇: