<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Build on zed的博客</title><link>https://www.dust-zed.site/android/build/</link><description>Recent content in Build on zed的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 24 Oct 2025 03:52:15 +0800</lastBuildDate><atom:link href="https://www.dust-zed.site/android/build/index.xml" rel="self" type="application/rss+xml"/><item><title>Gradle Convention Plugins：多模块构建逻辑组织</title><link>https://www.dust-zed.site/android/gradle-convention-plugins/</link><pubDate>Fri, 24 Oct 2025 03:52:15 +0800</pubDate><guid>https://www.dust-zed.site/android/gradle-convention-plugins/</guid><description>&lt;p>多模块 Android 项目如果每个模块都手写一遍插件、Android 配置、Kotlin 配置、Compose 配置和通用依赖，构建脚本很快会失控。Convention Plugins 的价值就是把这些重复配置收敛到 &lt;code>build-logic&lt;/code> 中，让业务模块只声明“我是什么类型的模块”。&lt;/p>
&lt;h2 id="核心结论">核心结论
&lt;/h2>&lt;ol>
&lt;li>Convention Plugin 是项目内部的 Gradle 插件，用来封装约定配置。&lt;/li>
&lt;li>它适合管理多模块项目的 Android、Kotlin、Compose、Hilt、测试等重复配置。&lt;/li>
&lt;li>业务模块的 &lt;code>build.gradle.kts&lt;/code> 应该尽量薄，只表达模块身份和少量特有依赖。&lt;/li>
&lt;li>&lt;code>build-logic&lt;/code> 本身也要保持边界清晰，避免变成新的复杂中心。&lt;/li>
&lt;/ol>
&lt;h2 id="核心思想">核心思想
&lt;/h2>&lt;p>Convention Plugins是一种通过预配置的Gradle插件来&lt;strong>标准化和复用构建逻辑&lt;/strong>的方法，核心理念是：&lt;/p>
&lt;ul>
&lt;li>约定优于配置 （Convention over Configuration）&lt;/li>
&lt;li>&lt;strong>DRY&lt;/strong>原则（Don&amp;rsquo;t Repeat Yourself）&lt;/li>
&lt;li>构建逻辑集中管理&lt;/li>
&lt;li>类型安全的配置&lt;/li>
&lt;/ul>
&lt;h2 id="典型项目结构">典型项目结构
&lt;/h2>&lt;pre tabindex="0">&lt;code>project/
├── build-logic/ # 构建逻辑模块
│ ├── settings.gradle.kts
│ └── convention/ # 约定插件模块
│ ├── build.gradle.kts
│ └── src/main/kotlin/
│ ├── AndroidApplicationConventionPlugin.kt
│ ├── AndroidLibraryConventionPlugin.kt
│ ├── AndroidFeatureConventionPlugin.kt
│ └── KotlinLibraryConventionPlugin.kt
├── settings.gradle.kts
├── app/
├── feature/
│ ├── feature-foryou/
│ └── feature-bookmarks/
└── core/
├── core-ui/
└── core-data/
&lt;/code>&lt;/pre>&lt;h2 id="解决的问题">解决的问题
&lt;/h2>&lt;p>如果不使用Convention Plugins会怎么样，项目中有10个&lt;code>feature&lt;/code>模块（比如&lt;code>:feature:foryou&lt;/code>,&lt;code>:feature:bookmarks&lt;/code>,&lt;code>feature:settings&lt;/code>等）。在传统的做法中，每个模块的&lt;code>build.gradle.kts&lt;/code>文件都会包含大量相似的配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 在 :feature:foryou/build.gradle.kts 文件中
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.android.library&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kotlin-android&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;dagger.hilt.android.plugin&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...可能还有其他插件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">android&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...大量通用的安卓配置
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:designsystem&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:ui&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.lifecycle:lifecycle-viewmodel-compose:...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ...可能还有10个以上所有feature模块都需要的通用依赖
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现在，如果你需要为所有&lt;code>feature&lt;/code>模块添加一个新的通用依赖库，或者修改一个通用的编译选项，你就必须手动去修改10个不同的&lt;code>build.gradle.kts&lt;/code>文件。这不仅繁琐，而且极易出错，非常难以维护。&lt;/p>
&lt;h2 id="实现方式">实现方式
&lt;/h2>&lt;h3 id="1-设置build-logic模块">1. 设置build-logic模块
&lt;/h3>&lt;p>&lt;strong>build-logic/settings.gradle.kts&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pluginManagement&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repositories&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradlePluginPortal&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">google&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencyResolutionManagement&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repositories&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">google&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">content&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">includeGroupByRegex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com&lt;/span>&lt;span class="se">\\&lt;/span>&lt;span class="s2">.android.*&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">includeGroupByRegex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com&lt;/span>&lt;span class="se">\\&lt;/span>&lt;span class="s2">.google.*&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">includeGroupByRegex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.*&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mavenCentral&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">versionCatalogs&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;libs&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">from&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">files&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;../gradle/libs.versions.toml&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">rootProject&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;build-logic&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:convention&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>build-logic/convention/build.gradle.kts&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">`&lt;/span>&lt;span class="n">kotlin&lt;/span>&lt;span class="p">-&lt;/span>&lt;span class="n">dsl&lt;/span>&lt;span class="err">`&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compileOnly&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">gradlePlugin&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compileOnly&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">kotlin&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">gradlePlugin&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compileOnly&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ksp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">gradlePlugin&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">gradlePlugin&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidApplicationCompose&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">nowinandroid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compose&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">pluginId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementationClass&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;AndroidApplicationComposeConventionPlugin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidApplication&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">nowinandroid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">asProvider&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">pluginId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementationClass&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;AndroidApplicationConventionPlugin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidLibraryCompose&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">nowinandroid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">library&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">compose&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">pluginId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementationClass&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;AndroidLibraryComposeConventionPlugin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidLibrary&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">nowinandroid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">library&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">asProvider&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">pluginId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementationClass&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;AndroidLibraryConventionPlugin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidFeature&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">plugins&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">nowinandroid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">android&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">feature&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">pluginId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementationClass&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;AndroidFeatureConventionPlugin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-创建约定插件">2. 创建约定插件
&lt;/h3>&lt;p>&lt;strong>AndroidApplicationConventionPlugin.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidApplicationConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.android.application&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.android&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.android.lint&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.dropbox.dependency-guard&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ApplicationExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureKotlinAndroid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaultConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">targetSdk&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">35&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Suppress&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;UnstableApiUsage&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">animationsDisabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureGradleManagedDevices&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ApplicationAndroidComponentsExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configurePrintApksTask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureBadgingTasks&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getByType&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ApplicationExtension&lt;/span>&lt;span class="p">&amp;gt;(),&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>AndroidApplicationComposeConventionPlugin.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidApplicationComposeConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.android.application&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.plugin.compose&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">extension&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getByType&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">ApplicationExtension&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureAndroidCompose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extension&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>AndroidLibraryComposeConventionPlugin.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidLibraryComposeConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.android.library&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.plugin.compose&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">val&lt;/span> &lt;span class="py">extension&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getByType&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">LibraryExtension&lt;/span>&lt;span class="p">&amp;gt;()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureAndroidCompose&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">extension&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>AndroidLibraryConventionPlugin.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidLibraryConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.android.library&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.android&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.android.lint&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">LibraryExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureKotlinAndroid&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">targetSdk&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">35&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lint&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">targetSdk&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">35&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaultConfig&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">testInstrumentationRunner&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;androidx.test.runner.AndroidJUnitRunner&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">animationsDisabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureFlavors&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureGradleManagedDevices&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// The resource prefix is derived from the module name,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// so resources inside &amp;#34;:core:module1&amp;#34; must be prefixed with &amp;#34;core_module1_&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">resourcePrefix&lt;/span> &lt;span class="p">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;#34;&amp;#34;\W&amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">toRegex&lt;/span>&lt;span class="p">()).&lt;/span>&lt;span class="n">drop&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="m">1&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">distinct&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">joinToString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">separator&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;_&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="n">lowercase&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="s2">&amp;#34;_&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">LibraryAndroidComponentsExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configurePrintApksTask&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disableUnnecessaryAndroidTests&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;androidTestImplementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kotlin.test&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;testImplementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kotlin.test&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.tracing.ktx&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>AndroidFeatureConvention.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidFeatureConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.android.library&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.hilt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.plugin.serialization&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">LibraryExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">animationsDisabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureGradleManagedDevices&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:ui&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:designsystem&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.hilt.navigation.compose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.lifecycle.runtimeCompose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.lifecycle.viewModelCompose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.navigation.compose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.tracing.ktx&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kotlinx.serialization.json&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;testImplementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.navigation.testing&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;androidTestImplementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.lifecycle.runtimeTesting&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="3-核心方法apply">3. 核心方法：&lt;code>apply&lt;/code>
&lt;/h3>&lt;p>当你将这个插件引用到一个模块时（例如，在&lt;code>:feature:foryou&lt;/code>中应用它），Gradle就会执行这个插件类中的&lt;code>apply&lt;/code>方法。逐行分析这个方法做了什么：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 在 AndroidFeatureConventionPlugin.kt 中
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">AndroidFeatureConventionPlugin&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">Plugin&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">Project&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">override&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">Project&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// &amp;#39;target&amp;#39; 就是应用这个插件的模块, 比如 :feature:foryou
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">with&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">target&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// --- 动作一：应用其他更基础的约定插件 ---
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 这行代码在说：“一个 `feature` 模块，首先它是一个 `library` 模块，
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 并且它也需要 Hilt 依赖注入和序列化功能。”
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 这体现了插件的组合能力。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.android.library&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;nowinandroid.hilt&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">apply&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">plugin&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;org.jetbrains.kotlin.plugin.serialization&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// --- 动作二：配置通用的 Android 属性 ---
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 这里统一为所有 feature 模块配置 &amp;#39;android&amp;#39; 代码块。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 比如，统一关闭测试中的动画，统一配置测试设备。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">extensions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">LibraryExtension&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">animationsDisabled&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureGradleManagedDevices&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// --- 动作三：添加所有 feature 模块都需要的通用依赖 ---
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 这是最强大的部分。它声明了“每一个” feature 模块都会自动获得这些依赖。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// 无需在各自的 build.gradle.kts 中重复添加。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:ui&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:core:designsystem&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.hilt.navigation.compose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;implementation&amp;#34;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">findLibrary&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.lifecycle.viewModelCompose&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="k">get&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... 以及其他所有通用依赖
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-共享配置函数">4. 共享配置函数
&lt;/h3>&lt;p>&lt;strong>KotlinAndroid.kt&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">com.android.build.api.dsl.CommonExtension&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">org.gradle.api.JavaVersion&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">org.gradle.api.Project&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">org.gradle.kotlin.dsl.dependencies&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">org.gradle.kotlin.dsl.withType&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">import&lt;/span> &lt;span class="nn">org.jetbrains.kotlin.gradle.tasks.KotlinCompile&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">internal&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">Project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configureKotlinAndroid&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">commonExtension&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">CommonExtension&lt;/span>&lt;span class="p">&amp;lt;*,&lt;/span> &lt;span class="p">*,&lt;/span> &lt;span class="p">*,&lt;/span> &lt;span class="p">*,&lt;/span> &lt;span class="p">*,&lt;/span> &lt;span class="p">*&amp;gt;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">commonExtension&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">apply&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compileSdk&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">34&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaultConfig&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">minSdk&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">24&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">compileOptions&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sourceCompatibility&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">JavaVersion&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">VERSION_17&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">targetCompatibility&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">JavaVersion&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">VERSION_17&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">configureKotlin&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span> &lt;span class="k">fun&lt;/span> &lt;span class="nf">Project&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">configureKotlin&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tasks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">withType&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">KotlinCompile&lt;/span>&lt;span class="p">&amp;gt;().&lt;/span>&lt;span class="n">configureEach&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kotlinOptions&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">jvmTarget&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nc">JavaVersion&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">VERSION_17&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">toString&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">freeCompilerArgs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">freeCompilerArgs&lt;/span> &lt;span class="p">+&lt;/span> &lt;span class="n">listOf&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;-opt-in=kotlin.RequiresOptIn&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5-在根项目中引入">5. 在根项目中引入
&lt;/h3>&lt;p>&lt;strong>settings.gradle.kts&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">pluginManagement&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">includeBuild&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;build-logic&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">repositories&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">google&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">mavenCentral&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradlePluginPortal&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="6-在模块中引入">6. 在模块中引入
&lt;/h3>&lt;p>&lt;strong>app/build.gradle.kts&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;myapp.android.application&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;myapp.android.application.compose&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">android&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">namespace&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.example.myapp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 只需配置特定于此模块的内容
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">defaultConfig&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">applicationId&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.example.myapp&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:feature:home&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;:feature:profile&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>feature/feature-home/build.gradle.kts&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;myapp.android.feature&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">android&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">namespace&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.example.myapp.feature.home&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Feature 特定的依赖
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="convention-plugin的优势">Convention Plugin的优势
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>DRY (Don&amp;rsquo;t Repeat Yourself)&lt;/strong> 原则：消除了在几十个文件中复制粘贴的构建逻辑。&lt;/li>
&lt;li>单一事实来源 (&lt;strong>Single Source of Truth&lt;/strong>)：如果需要更新一个所有&lt;code> feature&lt;/code> 模块都在用的库版本（比如 Lifecycle），你只需要在 &lt;code>AndroidFeatureConventionPlugin.kt&lt;/code> 这一个文件里修改即可。&lt;/li>
&lt;li>简洁与清晰：每个模块的构建脚本变得非常短，只包含对该模块独一无二的配置，可读性大大提高。&lt;/li>
&lt;li>类型安全与 IDE 支持：因为这些插件是用 Kotlin 编写的，你可以享受到 IDE 的自动补全、编译时类型检查和方便的导航跳转，这比传统的 Groovy 脚本要强大得多。&lt;/li>
&lt;/ol>
&lt;p>这些都是&lt;code>now-in-android&lt;/code>所应用的，更详细的可以通过此项目学习&lt;/p></description></item><item><title>Android Gradle 基础：插件、依赖和版本目录</title><link>https://www.dust-zed.site/android/gradle-basics/</link><pubDate>Sun, 29 Jun 2025 15:26:57 +0800</pubDate><guid>https://www.dust-zed.site/android/gradle-basics/</guid><description>&lt;p>Gradle 配置最容易混乱的地方，是把“构建时用的东西”和“运行时用的东西”混在一起。理解插件、依赖配置和版本目录之后，很多报错会变得容易定位。&lt;/p>
&lt;h2 id="核心结论">核心结论
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>插件改变构建过程&lt;/strong>，例如 Android 插件会添加编译、打包、签名等任务。&lt;/li>
&lt;li>&lt;strong>库参与代码编译或运行&lt;/strong>，例如 Retrofit、Room、JUnit。&lt;/li>
&lt;li>&lt;code>implementation&lt;/code> 默认优先使用，&lt;code>api&lt;/code> 只在需要暴露依赖给下游模块时使用。&lt;/li>
&lt;li>&lt;code>classpath&lt;/code> 是旧式 buildscript 插件依赖，和业务代码依赖不是一回事。&lt;/li>
&lt;li>Version Catalog 负责集中管理版本，适合多模块项目。&lt;/li>
&lt;/ol>
&lt;h2 id="插件和库的区别">插件和库的区别
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>维度&lt;/th>
&lt;th>插件&lt;/th>
&lt;th>库&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>本质&lt;/td>
&lt;td>构建逻辑扩展&lt;/td>
&lt;td>可被代码调用的依赖&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>作用&lt;/td>
&lt;td>添加任务、扩展 DSL、改变构建流程&lt;/td>
&lt;td>提供 API、运行时代码或测试能力&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>声明位置&lt;/td>
&lt;td>&lt;code>plugins {}&lt;/code>&lt;/td>
&lt;td>&lt;code>dependencies {}&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>影响阶段&lt;/td>
&lt;td>配置期和构建期&lt;/td>
&lt;td>编译期、运行期或测试期&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>示例&lt;/td>
&lt;td>&lt;code>com.android.application&lt;/code>、&lt;code>org.jetbrains.kotlin.android&lt;/code>&lt;/td>
&lt;td>Retrofit、Gson、JUnit&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>插件示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">plugins&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.android.application&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">id&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;org.jetbrains.kotlin.android&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>依赖示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;androidx.core:core-ktx:1.12.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">testImplementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;junit:junit:4.13.2&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="implementation">implementation
&lt;/h2>&lt;p>&lt;code>implementation&lt;/code> 是最常用的依赖配置。&lt;/p>
&lt;p>特点：&lt;/p>
&lt;ol>
&lt;li>当前模块可以使用该依赖；&lt;/li>
&lt;li>下游模块不会直接看到该依赖；&lt;/li>
&lt;li>有利于减少不必要的重编译；&lt;/li>
&lt;li>适合绝大多数业务依赖。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.squareup.retrofit2:retrofit:2.9.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果 &lt;code>feature-a&lt;/code> 依赖 &lt;code>core-network&lt;/code>，而 &lt;code>core-network&lt;/code> 使用 &lt;code>implementation&lt;/code> 引入 Retrofit，那么 &lt;code>feature-a&lt;/code> 不应该直接使用 Retrofit 类型。&lt;/p>
&lt;h2 id="api">api
&lt;/h2>&lt;p>&lt;code>api&lt;/code> 会把依赖暴露给下游模块。&lt;/p>
&lt;p>适合场景：&lt;/p>
&lt;ol>
&lt;li>当前模块的公开接口里出现了该依赖的类型；&lt;/li>
&lt;li>SDK 或公共基础库需要让使用方感知这个依赖；&lt;/li>
&lt;li>你明确希望依赖传递。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">api&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.google.dagger:dagger:2.48&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用原则：&lt;/p>
&lt;blockquote>
&lt;p>能用 &lt;code>implementation&lt;/code> 就不要用 &lt;code>api&lt;/code>，只有当公开 API 真的需要暴露依赖类型时再使用 &lt;code>api&lt;/code>。&lt;/p>&lt;/blockquote>
&lt;h2 id="classpath">classpath
&lt;/h2>&lt;p>&lt;code>classpath&lt;/code> 常见于旧式 Gradle 配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">buildscript&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">classpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;com.android.tools.build:gradle:8.1.0&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它的作用是让 Gradle 构建脚本能加载插件。它不参与 App 业务代码编译，也不会进入 APK。&lt;/p>
&lt;p>现代项目更推荐使用 &lt;code>plugins {}&lt;/code> 和 &lt;code>pluginManagement&lt;/code> 管理插件版本。&lt;/p>
&lt;h2 id="version-catalog">Version Catalog
&lt;/h2>&lt;p>Version Catalog 通常位于：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">gradle/libs.versions.toml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>它解决的问题是：多模块项目里依赖版本分散、重复、难统一。&lt;/p>
&lt;p>示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">versions&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">androidxCore&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;1.12.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">libraries&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">androidx-core-ktx&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">group&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;androidx.core&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;core-ktx&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">version&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ref&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;androidxCore&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>&lt;span class="nx">plugins&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">android-application&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;com.android.application&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">version&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="s2">&amp;#34;8.1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>使用方式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-kotlin" data-lang="kotlin">&lt;span class="line">&lt;span class="cl">&lt;span class="n">dependencies&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">implementation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">libs&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">androidx&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">core&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ktx&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="排错思路">排错思路
&lt;/h2>&lt;p>遇到 Gradle 依赖问题时，先问几个问题：&lt;/p>
&lt;ol>
&lt;li>报错发生在配置期、编译期、运行期还是测试期？&lt;/li>
&lt;li>这个东西是插件还是库？&lt;/li>
&lt;li>当前模块是否真的需要直接访问该依赖？&lt;/li>
&lt;li>是否因为用了 &lt;code>implementation&lt;/code>，下游模块访问不到类型？&lt;/li>
&lt;li>是否因为用了过宽的 &lt;code>api&lt;/code>，导致构建变慢和依赖污染？&lt;/li>
&lt;/ol>
&lt;h2 id="回看清单">回看清单
&lt;/h2>&lt;ol>
&lt;li>插件扩展构建流程，库提供代码能力。&lt;/li>
&lt;li>&lt;code>implementation&lt;/code> 隐藏依赖，&lt;code>api&lt;/code> 暴露依赖。&lt;/li>
&lt;li>&lt;code>classpath&lt;/code> 是给 Gradle 构建脚本加载插件用的。&lt;/li>
&lt;li>Version Catalog 解决多模块依赖版本集中管理。&lt;/li>
&lt;li>构建问题先判断发生阶段，再看依赖边界。&lt;/li>
&lt;/ol></description></item><item><title>Android APK 打包流程：从源码到可安装文件</title><link>https://www.dust-zed.site/android/apk-build-process/</link><pubDate>Sat, 28 Jun 2025 12:15:42 +0800</pubDate><guid>https://www.dust-zed.site/android/apk-build-process/</guid><description>&lt;p>Android 打包流程的本质，是把源码、资源、Manifest、依赖和原生库转换成系统可以安装和加载的产物。日常点击 “Run” 很简单，但理解构建链路能帮助定位资源冲突、依赖问题、签名问题和包体积问题。&lt;/p>
&lt;h2 id="核心结论">核心结论
&lt;/h2>&lt;ol>
&lt;li>资源先经过 AAPT2 编译和链接，生成 &lt;code>R&lt;/code>、&lt;code>resources.arsc&lt;/code> 和二进制资源。&lt;/li>
&lt;li>Kotlin/Java 源码先编译成 JVM 字节码，再由 D8/R8 转成 Dex。&lt;/li>
&lt;li>Manifest 会合并主工程、依赖库和变体配置。&lt;/li>
&lt;li>Release 包需要签名和 zipalign，签名保证来源可信，zipalign 优化运行时读取。&lt;/li>
&lt;li>AAB 是上传给应用商店的发布格式，最终安装到设备上的仍是按设备拆分后的 APK。&lt;/li>
&lt;/ol>
&lt;h2 id="总体流程">总体流程
&lt;/h2>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Kotlin / Java 源码
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; kotlinc / javac
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; .class
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; D8 / R8
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; classes.dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">res / AndroidManifest.xml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; AAPT2 compile
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; .flat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; AAPT2 link
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; resources.arsc / R / 合并后 Manifest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">assets / jniLibs / resources / dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; 打包
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; zipalign
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; apksigner
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; APK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="资源编译aapt2">资源编译：AAPT2
&lt;/h2>&lt;p>AAPT2 分成两个阶段：&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>compile&lt;/strong>&lt;/p>
&lt;p>把 &lt;code>res/&lt;/code> 下的资源单独编译成 &lt;code>.flat&lt;/code> 中间文件。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>link&lt;/strong>&lt;/p>
&lt;p>合并所有资源，生成 &lt;code>resources.arsc&lt;/code>、&lt;code>R&lt;/code> 类、最终资源表和处理后的 Manifest。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>这种分阶段设计的好处是支持增量编译。修改一个资源文件时，不一定要重新处理整个资源目录。&lt;/p>
&lt;h2 id="代码编译kotlinjava-到-dex">代码编译：Kotlin/Java 到 Dex
&lt;/h2>&lt;p>代码链路可以分成两段：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">Kotlin / Java
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; .class
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> -&amp;gt; .dex
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中：&lt;/p>
&lt;ol>
&lt;li>&lt;code>kotlinc&lt;/code> / &lt;code>javac&lt;/code> 负责生成 JVM 字节码；&lt;/li>
&lt;li>D8 负责把 &lt;code>.class&lt;/code> 转成 Dex；&lt;/li>
&lt;li>R8 在 Release 构建中负责裁剪、优化、混淆，并最终产出 Dex。&lt;/li>
&lt;/ol>
&lt;p>可以粗略理解为：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">D8 = 字节码到 Dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">R8 = D8 + shrink + optimize + obfuscate
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="manifest-合并">Manifest 合并
&lt;/h2>&lt;p>最终 APK 中只有一个 &lt;code>AndroidManifest.xml&lt;/code>，但它来自多个来源：&lt;/p>
&lt;ol>
&lt;li>app 主 Manifest；&lt;/li>
&lt;li>product flavor / build type 配置；&lt;/li>
&lt;li>library module 的 Manifest；&lt;/li>
&lt;li>第三方 SDK 的 Manifest。&lt;/li>
&lt;/ol>
&lt;p>Manifest 合并常见问题：&lt;/p>
&lt;ol>
&lt;li>权限重复或冲突；&lt;/li>
&lt;li>&lt;code>provider authorities&lt;/code> 冲突；&lt;/li>
&lt;li>&lt;code>minSdk&lt;/code> / &lt;code>targetSdk&lt;/code> 约束冲突；&lt;/li>
&lt;li>组件属性被依赖库注入。&lt;/li>
&lt;/ol>
&lt;p>排查入口：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">build/intermediates/merged_manifest/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="打包内容">打包内容
&lt;/h2>&lt;p>一个 APK 通常包含：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">AndroidManifest.xml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">classes.dex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">resources.arsc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">res/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">assets/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">lib/&amp;lt;abi&amp;gt;/*.so
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">META-INF/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中：&lt;/p>
&lt;ol>
&lt;li>&lt;code>classes.dex&lt;/code> 是运行在 ART 上的字节码；&lt;/li>
&lt;li>&lt;code>resources.arsc&lt;/code> 是资源索引表；&lt;/li>
&lt;li>&lt;code>lib/&amp;lt;abi&amp;gt;&lt;/code> 存放 Native 库；&lt;/li>
&lt;li>&lt;code>assets&lt;/code> 原样打包；&lt;/li>
&lt;li>&lt;code>META-INF&lt;/code> 和签名相关。&lt;/li>
&lt;/ol>
&lt;h2 id="签名与-zipalign">签名与 zipalign
&lt;/h2>&lt;h3 id="zipalign">zipalign
&lt;/h3>&lt;p>&lt;code>zipalign&lt;/code> 会调整 APK 中未压缩资源的对齐方式，让系统可以更高效地通过内存映射读取资源。&lt;/p>
&lt;p>核心价值：&lt;/p>
&lt;ol>
&lt;li>减少运行时额外拷贝；&lt;/li>
&lt;li>优化资源读取；&lt;/li>
&lt;li>是 Release APK 的标准步骤。&lt;/li>
&lt;/ol>
&lt;h3 id="签名">签名
&lt;/h3>&lt;p>签名用于证明 APK 来源，并保护包内容不被篡改。&lt;/p>
&lt;p>常见签名方案：&lt;/p>
&lt;ol>
&lt;li>V1：基于 JAR 签名，兼容老系统；&lt;/li>
&lt;li>V2：对整个 APK 签名；&lt;/li>
&lt;li>V3：支持密钥轮换；&lt;/li>
&lt;li>V4：服务于增量安装。&lt;/li>
&lt;/ol>
&lt;h2 id="apk-和-aab">APK 和 AAB
&lt;/h2>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>格式&lt;/th>
&lt;th>用途&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>APK&lt;/td>
&lt;td>设备可直接安装&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AAB&lt;/td>
&lt;td>上传到 Google Play，由商店按设备生成拆分 APK&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>AAB 的优势是可以按设备拆分：&lt;/p>
&lt;ol>
&lt;li>ABI；&lt;/li>
&lt;li>屏幕密度；&lt;/li>
&lt;li>语言；&lt;/li>
&lt;li>动态功能模块。&lt;/li>
&lt;/ol>
&lt;p>因此 AAB 能降低用户实际下载体积，但本地调试和部分渠道发布仍会直接使用 APK。&lt;/p>
&lt;h2 id="回看清单">回看清单
&lt;/h2>&lt;ol>
&lt;li>AAPT2 处理资源，D8/R8 处理 Dex。&lt;/li>
&lt;li>Manifest 是多来源合并的结果，冲突要看合并产物。&lt;/li>
&lt;li>Release 包要关注 R8、签名和 zipalign。&lt;/li>
&lt;li>APK 是安装格式，AAB 是应用商店分发格式。&lt;/li>
&lt;li>构建问题要先判断出在资源、代码、Manifest、签名还是打包阶段。&lt;/li>
&lt;/ol></description></item></channel></rss>