啊 ?用BuildSrc管理Android 依赖版本已经过时了?Catalogs才是版本答案?我不信!gradle统一依赖

Android组件化中使用Catalogs管理版本与对应封装

前言

说起 Android 的版本管理方案其实有很多种实现的,但是目前主流是三种方向,config.gradle 和 buildSrc 和 version Catalogs 这三种方案。

从网上的呼声来看目前大家比较喜欢 Catalogs 这种方案,因为如果现在出文章或出 Demo 还是用老的版本方式,会被读者善意的提醒过时了 😂

我之前的组件化方案【传送门】中我是使用 buildSrc + Kotin DSL 的方案来做的版本管理,然后有读者推荐使用 Catalogs 的方案来管理。

那本篇文章我们就带着几个问题往下看:

1、Catalogs 经过这么长时间的改善真的好用吗?代码提示完善吗?版本升级提示可以吗?

2、对比 buildSrc 这种方案会更好吗?他们之间的优缺点是什么?

3、使用 Catalogs 取代 config.gradle 有什么优势?中大型项目使用组件化开发的时候,Catalogs 如何进行集中管理和封装?

话不多说,直接开始!

一、使用BuildSrc的方式有什么弊端

关于 BuildSrc + Kotlin DSL 如何进行版本管理,我在之前的文章中已经详细的介绍了【传送门】

我介绍了这种方案的一些优势

  1. 可以很方便的跳转查看依赖组件,哪些地方有被依赖到一点就能飞。

  2. 可以使用Kotlin语法,并且可以对依赖组进行编队,可以按需进行依赖组的依赖。

  3. 其次还可以通过 Gradle Task 的方式进行封装,子组件只需要依赖这个 GradleTask 即可实现默认的配置。

这里介绍一些缺点

  1. buildSrc 被 Gradle 视为一个独立的项目,它在主项目构建之前自动编译。这意味着,当你对 buildSrc 中的代码做出更改时,Gradle需要重新编译它。如果 buildSrc 项目变得很大,编译时间可能会对总体构建时间产生显著影响。

  2. 语言成面,kotlin 编写脚本在编译时没有 groovy 的速度快,性能上差点,但是也是在编译时,并不影响运行时速度。

  3. 由于 buildSrc 是独立的项目,所以和主工程的 Gradle 可能有版本冲突,需要准备双份的版本强制指定。

在之前的文章我就说过我搞版本冲突搞了2晚上,就是因为这个问题导致 Hilt 无法使用,最后是强制指定双份版本才解决的冲突的问题,如果是其他的依赖不知道会不会也需要这么做。

对于 Kotlin DSL 和 Groovy DSL 可以看看我的之前的项目,我分别做了不同的分支,一模一样的依赖,分别做出三次清除缓存之后的编译测效果如下。

使用 Groovy 的传统方式:

image.png

image.png

image.png

使用 BuildSrc + Kotlin DSL 的方式:

image.png

image.png

image.png

取决于温度,电脑,CPU,其他环境因素,我不敢说原始的 Groovy 的方式一定比 BuildSrc + Kotlin DSL 的方案快,我只能说在我这边的测试结果是 Groovy 的方式相对于 BuildSrc 的方式稍微要快一些,如果你想要更精确的数据可以自行运行尝试。

当然他们对最终APK大小是没有影响的,最终的打包产物,包括最终性能都没有影响,只是在编译期间有区别。

image.png

二、使用 Catalogs 的集成

我使用的 Gradle 版本是 8.1.3 不算太高,也不算很低,使用 Catalogs 的方式不需要额外的声明。

我们直接创建文件就能使用

image.png

我们常用的四个标签,[versions] [libraries] [bundles] [plugins]

[versions]

作用: 定义项目中使用的各个依赖的版本号。

用途: 通过为每个版本分配一个标识符,您可以在整个版本目录中重用这些版本号。这样做的好处是,当需要升级依赖版本时,您只需在一个地方更新版本号,改动就会被应用到所有使用该版本的依赖上。

[libraries]

作用: 定义项目依赖的库。

用途: 在这个部分,您可以具体列出项目需要的库及其坐标(group,name,[version])。您还可以引用 [versions] 部分定义的版本号,从而避免在每个库定义中重复版本号。

[bundles]

作用: 定义一组相关的依赖,可以一起引入项目中。

用途: 通过创建包(bundle),您可以将相关的依赖分组在一起,然后在项目的不同模块中一次性引入这些依赖。这对于管理那些经常一起使用的库非常有用,如测试库集合、日志库集合等。

[plugins]

作用: 定义Gradle插件的依赖。

用途: 在这个部分,您可以列出项目中使用的Gradle插件及其版本。同样,您可以利用 [versions] 中定义的版本号来统一管理插件版本。

举个例子:

[versions]
compileSdk = "34"
minSdk = "21"
targetSdk = "33"
versionCode = "100"
versionName = "1.0.0"
applicationId = "com.newki.template"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

androidGradlePlugin = "8.1.3"
kotlinVersion = "1.8.22"
coroutinesVersion = "1.7.1"

appcompat = "1.6.1"
supportV4 = "1.0.0"
coreKtx = "1.9.0"
activityKtx = "1.8.0"
fragment = "1.5.4"
fragmentKtx = "1.5.4"
constraintLayout = "2.1.4"
cardView = "1.0.0"
material = "1.11.0"
recyclerView = "1.2.1"
multidex = "2.0.1"
viewpager = "1.0.0"
viewpager2 = "1.1.0-beta01"

lifecycleVersion = "2.7.0"
hiltVersion = "2.45"
navigationVersion = "2.5.3"
dataStoreVersion = "1.1.0-beta01"
workVersion = "2.8.1"

junit = "4.13.2"
androidJunit = "1.1.5"
espresso = "3.5.1"

#第三方
aRouterVersion = "1.0.3"
retrofitVersion = "2.9.0"
glideVersion = "4.11.0"
permissionVersion = "18.6"
gsonFactoryVersion = "9.5"
gsonVersion = "2.10.1"


[libraries]
#基础与UI
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
supportV4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "supportV4" }
coreKtx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
activityKtx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
fragment = { module = "androidx.fragment:fragment", version.ref = "fragment" }
fragmentKtx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" }
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }

#Widget
constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayout" }
recyclerView = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerView" }
cardView = { module = "androidx.cardview:cardview", version.ref = "cardView" }
material = { module = "com.google.android.material:material", version.ref = "material" }

#lifecycle
lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "lifecycleVersion" }
lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleVersion" }
lifecycle-livedata = { module = "androidx.lifecycle:lifecycle-livedata", version.ref = "lifecycleVersion" }
lifecycle-livedataKtx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleVersion" }
lifecycle-viewModel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycleVersion" }
lifecycle-viewModelKtx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleVersion" }
lifecycle-viewModelSavedState = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycleVersion" }
lifecycle-compiler = { module = "androidx.lifecycle:lifecycle-compiler", version.ref = "lifecycleVersion" }

#Kotlin与协程
stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlinVersion" }
reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinVersion" }
stdlibJdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlinVersion" }
stdlibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlinVersion" }
coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" }
coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutinesVersion" }

#ViewPager
viewpager = { module = "androidx.viewpager:viewpager", version.ref = "viewpager" }
viewpager2 = { module = "androidx.viewpager2:viewpager2", version.ref = "viewpager2" }

#Hilt
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltVersion" }
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltVersion" }

#Work
work-runtime = { module = "androidx.work:work-runtime", version.ref = "workVersion" }
work-runtimeKtx = { module = "androidx.work:work-runtime-ktx", version.ref = "workVersion" }

#Navigation
navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigationVersion" }
navigation-ui = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigationVersion" }
navigation-dynamic = { module = "androidx.navigation:navigation-dynamic-features-fragment", version.ref = "navigationVersion" }
navigation-dynamicRuntime = { module = "androidx.navigation:navigation-dynamic-features-runtime", version.ref = "navigationVersion" }
navigation-testing = { module = "androidx.navigation:navigation-testing", version.ref = "navigationVersion" }

#DataStore
datastore-core = { module = "androidx.datastore:datastore-core", version.ref = "dataStoreVersion" }
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "dataStoreVersion" }

#测试
junit = { module = "junit:junit", version.ref = "junit" }
androidJunit = { module = "androidx.test.ext:junit", version.ref = "androidJunit" }
espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" }

#第三方依赖库
#ARouter
arouter-core = { module = "com.github.jadepeakpoet.ARouter:arouter-api", version.ref = "aRouterVersion" }
arouter-compiler = { module = "com.github.jadepeakpoet.ARouter:arouter-compiler", version.ref = "aRouterVersion" }

#Retrofit
retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofitVersion" }
retrofit-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofitVersion" }
gson = { module = "com.google.code.gson:gson", version.ref = "gsonVersion" }
gsonFactory = { module = "com.github.getActivity:GsonFactory", version.ref = "gsonFactoryVersion" }

#图片加载
glide-core = { module = "com.github.bumptech.glide:glide", version.ref = "glideVersion" }
glide-annotation = { module = "com.github.bumptech.glide:annotations", version.ref = "glideVersion" }
glide-integration = { module = "com.github.bumptech.glide:okhttp3-integration", version.ref = "glideVersion" }
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glideVersion" }
gifDrawable = "pl.droidsonroids.gif:android-gif-drawable:1.2.28"


#Gradle插件
androidGradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
hilt-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hiltVersion" }
arouter-plugin = { module = "com.github.jadepeakpoet.ARouter:arouter-register", version.ref = "aRouterVersion" }


[bundles]
appcompatBundles = ["appcompat", "supportV4", "coreKtx", "activityKtx", "fragment", "fragmentKtx", "multidex"]
lifecycleBundles = ["lifecycle-runtime", "lifecycle-runtimektx", "lifecycle-livedata", "lifecycle-livedataKtx", "lifecycle-viewModel", "lifecycle-viewModelKtx", "lifecycle-viewModelSavedState"]
widgetBundles = ["constraintLayout", "recyclerView", "cardView", "material", "viewpager", "viewpager2"]
kotlinBundles = ["stdlib", "reflect", "stdlibJdk7", "stdlibJdk8", "coroutines-core", "coroutines-android"]
navigationBundles = ["navigation-fragment", "navigation-ui", "navigation-dynamic", "navigation-dynamicRuntime"]
dataStoreBundles = ["datastore-core", "datastore-preferences"]
retrofitBundles = ["retrofit-core", "retrofit-gson", "gson", "gsonFactory"]
glideBundles = ["glide-core", "glide-annotation", "glide-integration"]
workBundles = ["work-runtime", "work-runtimeKtx"]


[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
jetbrains-kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }

Catalogs 配合 Gradle.kts 使用:

plugins {
      alias(libs.plugins.android.application) apply false
      alias(libs.plugins.jetbrains.kotlin) apply false
}
plugins {
      alias(libs.plugins.android.application)
      alias(libs.plugins.jetbrains.kotlin)
}

android {
      namespace = "com.hgm.versioncatlogsguide"
      compileSdk = 34

      defaultConfig {
            applicationId = "com.hgm.versioncatlogsguide"
            minSdk = 26
            targetSdk = 33
            versionCode = 1
            versionName = "1.0"

            testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
            vectorDrawables {
                  useSupportLibrary = true
            }
      }

      buildTypes {
            release {
                  isMinifyEnabled = false
                  proguardFiles(
                        getDefaultProguardFile("proguard-android-optimize.txt"),
                        "proguard-rules.pro"
                  )
            }
      }
      compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
      }
      kotlinOptions {
            jvmTarget = "1.8"
      }
      buildFeatures {
            compose = true
      }
      composeOptions {
            kotlinCompilerExtensionVersion = "1.4.3"
      }
      packaging {
            resources {
                  excludes += "/META-INF/{AL2.0,LGPL2.1}"
            }
      }
}

dependencies {

    implementation(libs.bundles.appcompatBundles)
    implementation(libs.bundles.kotlinBundles)
    implementation(libs.bundles.widgetBundles)
    implementation(libs.bundles.lifecycleBundles)
    kapt(libs.lifecycle.compiler)

    ...

}

具体可以参考 Google 的 nowinandroid 项目,内部带有 Catalogs + gradle.kts 的组件化示例。

https://github.com/android/nowinandroid

只不过它没有对 Catalogs 的依赖进行组件化的再封装,因为这个 Demo 也只有几个组件,在我们真实的中大型项目中,都是动辄十几个或几十个组件,如果我们每个组件都需要写一些重复的代码,那么任务量也是很大的,如果要修改某一个配置那也是需要每个组件都修改,岂不是烦死个人,所以我更推荐封装之后使用。

三、使用 Catalog 的进行组件化封装

像我们之前的 config.gradle 一样的方式,我们可以通过自定义 gradle 文件的方式,然后在各组件中引入对应的依赖即可。

从Gradle 7.0开始, Groovy DSL 也可以使用与 Kotlin DSL 类似的依赖语法,这种改进被称为Gradle箭头语法(Gradle Sugar Syntax)。

之前我们的依赖方式:

dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.2'
}

现在我们也能类似 Kotlin DSL 的方式:

dependencies {
    implementation libs.arouter.core  
    implementation(libs.bundles.kotlin)
}

并且部分的 Groovy API 支持 DSL 的方法也支持跳转,并且对于 Catalogs 的支持也是很好可以直接跳转过去,但是支持有限,有些可以有些不性感,这是由于 Groovy 是一种动态语言,而 Kotlin 是一种静态语言,它们在IDE集成支持方面存在差异。即使在Gradle 8.0+ 中, Groovy DSL的导航和重构支持仍然有限的原因 。Kotlin DSL作为一种静态类型的DSL,天生得到了更好的 IDE 集成支持。

3.1 使用 BuildSrc + Groovy DSL 的方式

我们就配合 Groovy DSL 的方式来封装一个基类的公共配置:

configBasic.gradle

/**
 * 最基类的,公共的配置模块,(一般不直接使用),只是给别的配置文件继承使用
 */
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'dagger.hilt.android.plugin'

android {

    compileSdk = Integer.parseInt(libs.versions.compileSdk.get())
    defaultConfig {
        minSdk = Integer.parseInt(libs.versions.minSdk.get())
        targetSdk = Integer.parseInt(libs.versions.targetSdk.get())
        versionCode = Integer.parseInt(libs.versions.versionCode.get())
        versionName = libs.versions.versionName.get()

        testInstrumentationRunner = libs.versions.testInstrumentationRunner.get()
        vectorDrawables {
            useSupportLibrary = true
        }

        multiDexEnabled = true
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = "17"
    }

    buildFeatures {
        buildConfig = true
        viewBinding = true
    }

    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}

dependencies {
    //基础
    implementation(libs.bundles.appcompatBundles)
    implementation(libs.bundles.kotlinBundles)
    implementation(libs.bundles.widgetBundles)
    implementation(libs.bundles.lifecycleBundles)
    kapt(libs.lifecycle.compiler)

    //Hilt
    implementation(libs.hilt.android)
    kapt(libs.hilt.compiler)

    //ARouter
    implementation(libs.arouter.core)
    kapt(libs.arouter.compiler)

    //junit
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidJunit)
    androidTestImplementation(libs.espresso)
}

子模块的gradle基类,configModule.gradle:

/**
 * 默认的模块初始化gradle配置,可以依赖这个配置 再配置别的依赖
 * 上层的配置和running_config同级别-供组件化开发中的子组件依赖,如auth-component组件
 */
apply plugin: 'com.android.library'

apply from: rootProject.file('configBasic.gradle')  //重复的配置统一由基类提供

dependencies {
    //Module模块默认添加Service模块的
    implementation(project(":cs-service"))
}

主要添加组件的服务类。

app宿主或独立运行模块需要的依赖,configRunning.gradle:

/**
 * 运行模块初始化gradle配置,可以依赖这个配置 再配置别的依赖
 * 上层的配置和module_config同级别-供真正能运行的模块配置,如app组件
 */
apply plugin: 'com.android.application'
apply plugin: 'com.alibaba.arouter'

apply from: rootProject.file('configBasic.gradle')  //重复的配置统一由基类提供

android {
    defaultConfig {
        applicationId = "com.newki.running"
    }

    signingConfigs {
        release {
            storeFile file("${project.rootDir}/${project.store_file}")
            storePassword project.store_password
            keyAlias project.key_alias
            keyPassword project.key_password
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }

    buildTypes {
        release {
            //默认系统混淆
            minifyEnabled true
            // 不显示Log
            buildConfigField "boolean", "LOG_DEBUG", "false"
            //是否可调试
            debuggable false
            //Zipalign优化
            zipAlignEnabled true
            //移除无用的resource文件
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            debuggable true
        }
    }
}

dependencies {
    //Module模块默认添加Service模块的
    implementation(project(":cs-service"))
}

主要是配置一些签名,编译信息。

使用也比较简单:

app的build.gradle:

apply from: rootProject.file('configRunning.gradle')

android {
    namespace = "com.newki.template"
    defaultConfig {
        applicationId = libs.versions.applicationId.get()
    }
}

dependencies {
    implementation(project(":cpt-auth"))
    implementation(project(":cpt-profile"))

    //依赖到对应组件的Api模块
    implementation(project(":app-api"))
}

profile组件的build.gradle:

apply from: rootProject.file('configModule.gradle')

android {
    namespace = "com.newki.profile"
}

dependencies {
    //依赖到对应组件的Api模块
    implementation(project(":cpt-profile-api"))
}
3.2 使用 BuildSrc + Kotin DSL 的方式

理论上 Catalog + Kotlin DSL 的方式会更加优雅一些。

例如创建 common.gradle.kts :

// 示例共通配置
tasks.register("commonTask") {
    doLast {
        //做一些公共的配置
    }
}

这里的公共配置和我们之前的BuildSrc中的 DefaultGradlePlugin 配置类似,然后我们在各模块引入这个基类。

apply(from = "../common.gradle.kts")

或者类似 NowInAndroid 中的单独定义模块定义 Plugin 然后注册之后各模块引入:

比如自定义模块 build-logic 中定义 Plugin :

class AndroidTestConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) {
        with(target) {
            with(pluginManager) {
                apply("com.android.test")
                apply("org.jetbrains.kotlin.android")
            }

            extensions.configure<TestExtension> {
                configureKotlinAndroid(this)
                defaultConfig.targetSdk = 34
                configureGradleManagedDevices(this)
            }
        }
    }

}

build-logic 的 build.gradle 中 register。

gradlePlugin {
    plugins {
        register("androidTest") {
            id = "nowinandroid.android.test"
            implementationClass = "AndroidTestConventionPlugin"
        }
        ... 
    }
}

使用:

plugins {
    id("nowinandroid.android.test")
    // 其他插件...
}

那么对比 BuildSrc + Kotlin DSL 的管理+封装方式,这种方案有什么缺点?

只是之前 BuildSrc + Kotlin DSL 的方式,我们把版本管理和封装都搞在一起了,现在是 Catalogs 做版本管理,使用另一个单独的模块做封装了。

好处是全程支持 Kotlin 代码提示和导航比 BuildSrc 还要好,其次是扩展性要比 BuildSrc 更好通过注册插件的方式,可以更容易地扩展和自定义构建逻辑,而不需要修改主项目的构建脚本,再次是分离了版本管理和插件注册的逻辑。

缺点是与buildSrc相比编译速度并没有提升,甚至还更慢,因为 buildSrc 是一个特殊的模块, Gradle加载过程略有优化。其次是复杂程度更高,插件太过碎片化导致管理难度提高。

总结

本文介绍了三种比较推荐的做法,BuildSrc + Kotlin DSL ,Catalogs + Groovy DSL,Catalogs + Kotlin DSL并且这三种方式各有利弊。

BuildSrc + Kotlin DSL 的方案的主要特点是编译会稍慢,可能需要处理依赖版本冲突问题,好处是熟悉的Kotlin语法,与良好的代码导航支持。

Catalogs + Groovy DSL 的方案特点是虽然支持了代码导航,但是支持程度没有 Kotlin DSL 好,好处是编译速度更快,都在一个 Project 中版本冲突问题会缓解。

Catalogs + Kotlin DSL 的方案特点中和个上面两种方案的优点,无需新项目编译,完美的代码导航,友好的语言环境,但是无法像上面两种方案进行封装使用。

如果你的项目是小项目,或者组件比较少,或者不是组件化项目,我推荐你使用 Catalogs + Kotlin DSL 的方式,相对更完美,只需要在每一个模块都写一份配置文件而已。

如果你的项目是大型项目,并且有很多的子组件,那么我推荐你使用 Catalogs + Groovy DSL 的方式,封装一份 gradle 基类方便统一集中的管理。

当然如果你想以Catalogs + Kotlin DSL ,封装的方式选择使用单独模块的方式实现Plugin 再 register 之后在其他模块使用也是可以的,方法有很多,各有利弊。

回归到标题上,Android版本管理BuildSrc过时了?Catalogs才是版本答案?我不信!好吧现在我信了,就单纯版本管理来说确实 Catalogs 已经很方便了,尤其是如今 Catalogs 更新迭代到现在,不管是代码导航还是版本升级提示都表现相对更完美,如果你有新项目开发或者老项目改造我都还是更推荐 Catalogs 的方案。

(PS:防杠)当然了,这并不是唯一答案,反正都能用,各位高工大佬爱用哪个用哪个,说到底只是一个编译工具,对最终的打包产物与性能并没有影响,我们应该把更多的关注点放在业务和性能上面。

好了闲话少说,如果你有其他的更多的更好的实现方式,也希望大家能评论区交流一起学习进步。如果我的文章有错别字,不通顺的,或者代码、注释、有错漏的地方,同学们都可以指出修正。

最后本文源码奉上,恳请各位大佬高工指点 【传送门】

内部的两个分支分别对应了这里的两种方案,大家可以按需进行参考或测试。

作者:Newki
链接:https://juejin.cn/post/7355738271693373479
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值