一、模拟一下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基础 下一篇: