Android - Gradle 小课堂第2课 - SourceSet

Gradle 小课堂系列
Gradle 小课堂第1课 - 入门篇
Gradle 小课堂第2课 - SourceSet

什么是 SourceSet?

SourceSet 就是源码的集合。用来告诉 gradle 我的 java 代码在哪里,我的 resource 目录在哪里等等。默认情况下,只有一个 SourceSet : main,也不用做任何设置就能正常编译项目,实际上是因为 SourceSet 规定了一些默认值,如果按照默认值配置项目(Android Studio 新建项目就按照默认值创建)就不用额外去写配置代码了。

SourceSet 规定的那些默认值是什么?

这个可以通过运行一个 task 来查看,这个 task 就叫 sourceSets,可以在 Android Studio 的 gradle 面板上双击执行,也可以用命令行执行。见下图:

gradle 面板

执行结果片段:

main
----
Compile configuration: compile
build.gradle name: android.sourceSets.main
Java sources: [app\src\main\java]
Manifest file: app\src\main\AndroidManifest.xml
Android resources: [app\src\main\res]
Assets: [app\src\main\assets]
AIDL sources: [app\src\main\aidl]
RenderScript sources: [app\src\main\rs]
JNI sources: [app\src\main\jni]
JNI libraries: [app\src\main\jniLibs]
Java-style resources: [app\src\main\resources]

上面列出的是 main 的默认值,默认情况下还应该打印出这几个的默认值: debug、release、test、testDebug、testRelease、androidTest、androidTestDebug。带 test 字符串的是与测试相关的。debug 和 release 也是默认就创建好的两个 SourceSet。

多个 SourceSet 之间的关系是什么?

先说 main 这个 SourceSet,它是一个共用的 SourceSet,可以认为是所有 SourceSet 的“基类”,任何一个其他的 SourceSet 编译时都会将 main 中的代码包括进去,也就是说 main 中的代码肯定是会参与编译的。

再说 debug 和 release 这两个 SourceSet,他俩是默认的两个 buildType 生成的 SourceSet。buildType 会影响 SourceSet,而且 buildType 与 SourceSet 是一一对应的,只要有一个 buildType 就会有一个 SourceSet 默默地创建出来。可以将这种 SourceSet 记为 buildType 维度的 SourceSet。

如果定义了 productFlavor,如下面代码所示:

android {
    productFlavors {
        huoguo {
        }
        malatang {
        }
    }
}

就会生成两个名为 huoguo 和 malatang 的 SourceSet。productFlavor 也是与 SourceSet 一一对应的。可以将这种 SourceSet 记为 productFlavor 维度的 SourceSet。

那么最终编译的时候到底应该使用哪个 SourceSet?这就要提到 Build Variant,在 Android Studio 中可以在 Build Variants 界面选择,如下图所示:


Build Variants

可以看到两个不同维度的 SourceSet 自由组合了起来,生成了 2 x 2 = 4 个 Build Variant。每次编译只能选择其中一种,也就是说,最终编译使用哪些 SourceSet 是由 buildType 和 productFlavor 共同决定的,而 SourceSet 就起到了区分这些变量的作用,通过 SourceSet 的配置,使得不同的 buildType 和不同的 productFlavor 有机会使用不同的代码来编译。

回到 SourceSet 之间的关系的问题,同一维度下的几个 SourceSet 之间可见是互斥的关系,而不同维度之间的 SourceSet 就是(可能产生的)组合的关系。因此同一维度下的不同 SourceSet 可以使用路径完全相同的类文件,而不用担心类重复的冲突。而不同维度下的 SourceSet 必须考虑到冲突的问题,不能使用路径相同的类文件。

SourceSet 自定义路径

上文提到的 sourceSets task 会打印出所有的目录默认配置,列出的就是所有 SourceSet 可配置的目录,对应的代码如下摘自 DSL 文档的图:

SourceSet 可配置属性

下面举几个常用的例子:

例1:添加一个 java 源码目录的设置

android {
    sourceSets {
        main { // main source set
            java {
                def anotherDir = "......"
                srcDirs anotherDir        // 1 添加
                srcDir anotherDir         // 2 添加
                setSrcDirs([anotherDir])  // 3 重置
                srcDirs = [anotherDir]    // 4 重置
                srcDirs += [anotherDir]   // 5 添加
            }
        }
    }
}

注释 1 与 2 位置的语句是调用了 srcDirs(dir)srcDir(dir) 这两个方法,虽然方法名有单数和复数的区别,实际上这两个方法效果是完全一样的,都是添加一个源码目录。而注释 3 和 4 的语句是等价的,是设置属性 srcDirs,会覆盖之前的值,也就是说设置完成后,只剩下新添加的 anotherDir 了。注释 5 的语句相当于调用 setSrcDirs(getSrcDirs() + [anotherDir]),使用了运算符重载(operator overload),重载了 + 运算,与 1 和 2 是效果相同的。

例2:将 SourceSet 的路径修改为另一套

android {
    sourceSets {
        main {
            def dependSrc = 'juanbing'
            java.srcDirs = ["${dependSrc}/java"]
            assets.srcDirs = ["${dependSrc}/assets"]
            res.srcDirs = ["${dependSrc}/res"]
            manifest.srcFile "${dependSrc}/AndroidManifest.xml"
        }
    }
}

你可能感兴趣的:(Android - Gradle 小课堂第2课 - SourceSet)