音视频【免费下载链接】SimpMusicA cross-platform music app using YouTube Music for backend项目地址https://gitcode.com/gh_mirrors/si/SimpMusic点击查看免费下载导读本文围绕 SimpMusic 仓库中的构建配置实践系统讲解如何用Gradle Convention Plugins约定插件与Version Catalog版本目录搭建一套可扩展、可维护的 Android 多模块构建体系。你将掌握build-logic目录结构、settings.gradle.kts复合构建接入、libs.versions.toml的版本统一管理以及自定义插件的创建与注册并结合 SimpMusic 仓库现状理解从逐模块复制粘贴演进到插件复用的完整路径。一、为什么要收敛构建逻辑告别 copy-pasteSimpMusic 是一个典型的多模块项目仅在本仓库根目录下就同时管理着androidApp、vivoAndroidApp、composeApp、desktopApp以及cast、cast-empty、crashlytics、crashlytics-empty、lastfm、lastfm-empty等一批功能模块。这种结构下如果每个模块都独立写一份build.gradle.kts会出现大量重复配置Compose 编译选项compose true、Compose Compiler 插件Kotlin 编译选项jvmTarget、freeCompilerArgs各模块都要重复声明的 SDK 版本、minSdk、compileSdkHilt/Koin 等依赖注入配置。从源码结构看SimpMusic 中androidApp/build.gradle.kts与vivoAndroidApp/build.gradle.kts就存在明显的配置同构——后者在文件头注释中甚至直接写明Everything except the package and the ABI mirrors androidApp/build.gradle.kts: keep the two in step除包名与 ABI 外完全镜像 androidApp两者需保持同步。这正是文档所述痛点Stop copy-pasting code betweenbuild.gradle.ktsfiles在真实项目中的直接体现。解决方案的核心思路把 Compose 设置、Kotlin 选项、依赖注入等公共构建逻辑从各个模块的脚本中抽离出来集中到可复用的插件Convention Plugins中配合 Version Catalog 统一管理版本号。二、目标项目结构build-logic 复合构建按文档方案先确保项目具备一个build-logic目录并在settings.gradle.kts中将其作为复合构建composite build引入root/ ├── build-logic/ │ ├── convention/ │ │ ├── src/main/kotlin/ │ │ │ └── AndroidApplicationConventionPlugin.kt │ │ └── build.gradle.kts │ ├── build.gradle.kts │ └── settings.gradle.kts ├── gradle/ │ └── libs.versions.toml ├── app/ │ └── build.gradle.kts └── settings.gradle.kts其中build-logic/settings.gradle.kts是复合构建自身的设置文件负责声明其内部子项目如convention与所用插件仓库build-logic/convention是一个独立的 Gradle 子项目src/main/kotlin/下存放各约定插件的 Kotlin 源码build.gradle.kts负责注册这些插件根目录的gradle/libs.versions.toml是 Version Catalog供根构建与 build-logic 共享各功能模块如app通过alias(...)引用约定插件脚本体积大幅缩减。三、Step 1在 settings.gradle.kts 中接入复合构建includeBuild(build-logic)会把build-logic作为一个独立的复合构建引入其提供的插件对所有子项目可见。同时配置插件仓库与依赖解析仓库// settings.gradle.kts pluginManagement { includeBuild(build-logic) repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }几点关键语义pluginManagement决定插件的解析来源与顺序includeBuild(build-logic)让 Gradle 优先从复合构建中寻找插件 ID未命中的再回落至下方仓库RepositoriesMode.FAIL_ON_PROJECT_REPOS强制所有模块统一走根级仓库声明禁止各模块自行声明仓库避免多模块之间因仓库不一致导致的解析问题实际项目中可按需追加仓库。例如 SimpMusic 的根 settings.gradle.kts 就额外加入了 JitPackmaven { setUrl(https://jitpack.io) }与 JogAmp 仓库并在注释中记录了移除已关停的 oss.sonatype.org 的经验——仓库列表应当根据实际依赖随时收敛。与 SimpMusic 现状的对照SimpMusic 根 settings.gradle.kts 已经完整应用了本文档的插件仓库 依赖仓库管理骨架pluginManagement中声明了google()、mavenCentral()、gradlePluginPortal()及自定义仓库dependencyResolutionManagement同样设置了FAIL_ON_PROJECT_REPOS。它尚未引入的是includeBuild(build-logic)一行——也就是说SimpMusic 目前只完成了 Version Catalog 化build-logic复合构建属于可选的下一步演进。此外SimpMusic 还开启了enableFeaturePreview(TYPESAFE_PROJECT_ACCESSORS)让模块间依赖可以写成projects.composeApp、projects.data这种类型安全访问器见 androidApp/build.gradle.kts 中implementation(projects.composeApp)的用法。四、Step 2在 libs.versions.toml 中统一定义依赖与插件Version Catalog 的核心价值是让库与插件的版本各有一个唯一出处。典型写法如下库部分以注释省略[versions] androidGradlePlugin 8.2.0 kotlin 1.9.20 [libraries] # ... [plugins] android-application { id com.android.application, version.ref androidGradlePlugin } android-library { id com.android.library, version.ref androidGradlePlugin } kotlin-android { id org.jetbrains.kotlin.android, version.ref kotlin } # Define your own plugins here nowinandroid-android-application { id nowinandroid.android.application, version unspecified }关键点[versions]集中存放版本号libraries与plugins通过version.ref引用一处升级全局生效自己写的约定插件在 Version Catalog 中声明为version unspecified——因为它来自复合构建而非外部仓库没有独立版本号声明后Gradle 会为每个条目生成类型安全的访问器例如libs.plugins.android.application、libs.versions.kotlin。SimpMusic 的实际 Version CatalogSimpMusic 的 gradle/libs.versions.toml 是一个规模可观的真实样例体现了文档所述模式在生产项目中的完整形态三段式组织[versions]超过 90 个版本号如android 9.4.1、kotlin 2.4.20、compose-bom 2026.09.00[libraries]约 130 条依赖声明[plugins]约 20 个插件条目版本号可被构建脚本反向读取androidApp/build.gradle.kts中versionCode libs.versions.version.code.get().toInt()、versionName libs.versions.version.name.get()让 APK 版本号与 toml 中的version-name/version-code单一来源保持一致desktopApp/build.gradle.kts 也用它来给桌面端设version插件与库共同管理[plugins]中除了android-application、android-library还包含compose-compiler、kotlin-serialization、ksp、room、sentry-gradle等全部通过version.ref与版本段联动带注释的版本选择记录toml 中大量注释记录了版本间兼容性约束如 material3 与 material3-multiplatform 必须同步升级这本身就是 Version Catalog 化给团队带来的可维护性红利。五、Step 3创建并注册 Convention Plugin约定插件的本质是一段把公共配置应用到目标项目的逻辑。在build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt中定义import com.android.build.api.dsl.ApplicationExtension import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure class AndroidApplicationConventionPlugin : PluginProject { override fun apply(target: Project) { with(target) { with(pluginManager) { apply(com.android.application) apply(org.jetbrains.kotlin.android) } extensions.configureApplicationExtension { defaultConfig.targetSdk 34 // Configure common options here } } } }要点说明PluginProject的apply(target: Project)是插件入口with(target)让后续pluginManager、extensions都在目标项目上生效通过pluginManager.apply(...)把官方插件内聚进约定插件目标模块就不必再重复声明这些官方插件extensions.configureApplicationExtension { ... }是 AGP 推荐的类型安全配置方式可在此统一compileSdk、minSdk、targetSdk、buildFeatures等公共项。随后必须在build-logic/convention/build.gradle.kts中注册该插件才能让插件 ID 对外可见gradlePlugin { plugins { register(androidApplication) { id nowinandroid.android.application implementationClass AndroidApplicationConventionPlugin } } }gradlePlugin是 Gradle 的 Java/Kotlin DSL 插件机制id是暴露给其他模块的插件标识implementationClass指向插件的具体实现类。约定插件的公共入口应进一步扩展到公共配置例如android-application、android-library、kotlin-android、Compose 与依赖注入相关插件从而覆盖项目里所有模块的公共配置。六、Usage在模块中应用约定插件在功能模块如app/build.gradle.kts中只需要一行即可替代原本几十行的公共配置plugins { alias(libs.plugins.nowinandroid.android.application) }alias(libs.plugins.xxx)会解析 Version Catalog 中声明的插件条目由于约定插件内部已经完成了官方插件的 apply 与公共 DSL 配置模块级脚本得以大幅瘦身只需保留模块特有内容如namespace、专属依赖。这正是文档This drastically cleans up module-level build files所述的效果。SimpMusic 当前采用的声明模式SimpMusic 尚未落地约定插件但已经充分使用了根项目统一声明 模块 alias 引用的过渡形态根 build.gradle.kts 中以alias(libs.plugins.android.application) apply false等方式统一声明所有插件apply false表示只将插件加入构建的类路径、不立即应用避免插件版本在多处重复各模块只做一次 alias例如 androidApp/build.gradle.kts 的plugins { alias(libs.plugins.android.application); alias(libs.plugins.sentry.gradle); alias(libs.plugins.compose.compiler) }cast/build.gradle.kts 的alias(libs.plugins.android.library)与alias(libs.plugins.compose.compiler)根脚本同时用subprojects { ... }做全局 KMP 编译参数与依赖解析策略的收敛见 build.gradle.kts例如通过resolutionStrategy.force(...)强制统一 nanojson 版本以规避NoSuchMethodError。从源码结构看SimpMusic 的androidApp与vivoAndroidApp之间明显的配置同构同样一组插件、同样的compileSdk 37、同样的minSdk 26、同样格式的packaging排除规则正是文档方案所要消除的重复——这也是将上述公共配置抽取为AndroidApplicationConventionPlugin的最佳候选场景。七、实战迁移路径从 SimpMusic 现状到 Convention Plugins结合仓库实际情况可以从Version Catalog 化继续推进到约定插件化建议按以下步骤落地建立build-logic目录创建build-logic/settings.gradle.kts、build-logic/build.gradle.kts并引入kotlin-dsl插件以支持用 Kotlin 编写插件源码接入根构建在根 settings.gradle.kts 的pluginManagement中加入includeBuild(build-logic)编写第一个约定插件把androidApp与vivoAndroidApp共有的配置compileSdk、minSdk、targetSdk、versionCode/versionName读取、buildFeatures、packaging排除规则抽入AndroidApplicationConventionPlugin在 Version Catalog 中登记参照version unspecified的写法在 gradle/libs.versions.toml 的[plugins]段为每个约定插件声明条目逐模块替换将模块脚本中的公共插件列表替换为一行alias(libs.plugins.自定义插件)仅保留模块特有配置并用./gradlew :androidApp:assembleDebug逐模块验证构建按需拆分当模块类型分化明显时如 Android 库、KMP 模块、桌面 JVM 模块按类型拆分为多个约定插件AndroidLibraryConventionPlugin、KmpLibraryConventionPlugin等保持每个插件职责单一。需要注意的前提与限制约定插件修改后会作用于所有应用它的模块属于一次配置、全局生效引入前应先在 CI 上充分验证build-logic本身是独立的构建它的仓库解析需要在其自己的settings.gradle.kts中声明不能依赖根构建的仓库SimpMusic 已在根 gradle.properties 中开启org.gradle.unsafe.configuration-cachetrue编写约定插件时应注意避免在doLast中捕获Project/layout等对象引用以免破坏配置缓存兼容性这一点与 desktopApp/build.gradle.kts 中对个别任务标注notCompatibleWithConfigurationCache的处理思路一致。八、最佳实践小结版本单一来源所有库与插件的版本只存在于libs.versions.toml脚本中通过类型安全的libs.*访问器引用仓库收敛坚持FAIL_ON_PROJECT_REPOS根级统一管理仓库列表随依赖变化增删参考 SimpMusic 移除已停服 OSSRH 的做法公共配置进插件凡是两个以上模块重复的配置优先抽取为约定插件而不是继续复制粘贴渐进式演进即使暂时不引入build-logic先完成根项目apply false声明 模块alias引用 Version Catalog这套组合也能消除绝大部分版本漂移问题SimpMusic 当前正是这一中间形态可作为后续向 Convention Plugins 迁移的现成样板。参考文件导航关联文档本文骨架来源.claude/skills/android-gradle-logic/SKILL.md根构建配置settings.gradle.kts、build.gradle.kts、gradle.propertiesVersion Catalog 实例gradle/libs.versions.toml模块脚本对照androidApp/build.gradle.kts、vivoAndroidApp/build.gradle.kts、cast/build.gradle.kts、lastfm/build.gradle.kts、desktopApp/build.gradle.kts打包脚本build_and_sign_apk.sh赞分享音视频【免费下载链接】SimpMusicA cross-platform music app using YouTube Music for backend项目地址https://gitcode.com/gh_mirrors/si/SimpMusic点击查看免费下载相关推荐Thunderbird for Android 构建插件体系基于 Gradle Convention Plugins 的统一构建逻辑管理实践Thunderbird for Android 构建插件体系基于 Gradle Convention Plugins 的统一构建逻辑管理实践 导读 Thund移动开发企业应用Robolectric 构建逻辑build-logic解析Convention Plugins 与 Error Prone 聚合报告机制Robolectric 构建逻辑build logic解析Convention Plugins 与 Error Prone 聚合报告机制 build lo测试移动开发AnkiDroid 构建体系剖析buildSrc 约定插件与 Gradle 构建逻辑复用实战AnkiDroid 构建体系剖析buildSrc 约定插件与 Gradle 构建逻辑复用实战 导读 本文以 buildSrc/README.md https:移动开发教育上一篇Arklex framework完全指南构建智能AI Agent的终极工具包下一篇Win11Debloat完整指南给Windows 11做一次系统深度优化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考