Android開發(fā)設(shè)計nowinandroid構(gòu)建腳本學(xué)習(xí)
引言
nowinandroid 項目是谷歌開源的示例項目,它遵循 Android 設(shè)計和開發(fā)的最佳實踐,并旨在成為開發(fā)人員的有用參考
這個項目在架構(gòu)演進(jìn),模塊化方案,單元測試,Jetpack Compose,啟動優(yōu)化等多個方面都做了很好的示例,的確是一個值得學(xué)習(xí)的好項目
今天我們來學(xué)習(xí)一下 nowinandroid 項目的構(gòu)建腳本,看一下都有哪些值得學(xué)習(xí)的地方
gradle.properties 中的配置
要看一個項目的構(gòu)建腳本,我們首先看一下 gradle.properties
# Enable configuration caching between builds. org.gradle.unsafe.configuration-cache=true android.useAndroidX=true # Non-transitive R classes is recommended and is faster/smaller android.nonTransitiveRClass=true # Disable build features that are enabled by default, # https://developer.android.com/studio/releases/gradle-plugin#buildFeatures android.defaults.buildfeatures.buildconfig=false android.defaults.buildfeatures.aidl=false android.defaults.buildfeatures.renderscript=false android.defaults.buildfeatures.resvalues=false android.defaults.buildfeatures.shaders=false
可以看出,nowinandroid 項目主要做了以下幾個配置
- 開啟配置階段緩存
- 開啟
androidX
,并且移除了Jetifier
- 關(guān)閉
R
文件傳遞 - 關(guān)閉
build features
前面3個配置之前都介紹過,我們來看一下關(guān)閉 build features
AGP 4.0.0 引入了一種新方法來控制您要啟用和停用哪些構(gòu)建功能,如ViewBinding
,BuildConfig
。
我們可以在 gradle.properties 中全局開啟或關(guān)閉某些功能,也可以在模塊級 build.gradle 文件中為每個模塊設(shè)置相應(yīng)的選項,如下所示:
android { // The default value for each feature is shown below. You can change the value to // override the default behavior. buildFeatures { // Determines whether to generate a BuildConfig class. buildConfig = true // Determines whether to support View Binding. // Note that the viewBinding.enabled property is now deprecated. viewBinding = false // Determines whether to support Data Binding. // Note that the dataBinding.enabled property is now deprecated. } }
通過停用不需要的構(gòu)建可能,可以提升我們的構(gòu)建性能,比如我們最熟悉的BuildConfig
,每個模塊都會生成這樣一個類,但其實我們在絕大多數(shù)情況下是用不到的,因此其實可以將其默認(rèn)關(guān)閉(在 AGP 8.0 中 BuildConfig 生成已經(jīng)變成默認(rèn)關(guān)閉了)
自動安裝 git hook
有時我們會添加一些 git hook,用于在代碼提交或者 push 時做一些檢查
但使用 git hook 的一個問題在于,每次拉取新項目之后,都需要手動安裝一下 git hook,這一點常常容易被忘記
那么有沒有什么辦法可以自動安裝 git hook 呢?nowinandroid 項目提供了一個示例
// settings.gradle.kts val prePushHook = file(".git/hooks/pre-push") val commitMsgHook = file(".git/hooks/commit-msg") val hooksInstalled = commitMsgHook.exists() && prePushHook.exists() && prePushHook.readBytes().contentEquals(file("tools/pre-push").readBytes()) if (!hooksInstalled) { exec { commandLine("tools/setup.sh") workingDir = rootProject.projectDir } }
其實原理很簡單,在settings.gradle.kts
中添加以上代碼,這樣在 Gradle 同步時,就會自動判斷 git hook 有沒有被安裝,如果沒有被安裝則自動安裝
使用 includeBuild 而不是 buildSrc
pluginManagement { includeBuild("build-logic") repositories { google() mavenCentral() gradlePluginPortal() } }
為了支持在不同的模塊間共享構(gòu)建邏輯,此前我們常常會添加一個 buildSrc 模塊
但是 buildSrc 模塊的問題在于每次發(fā)生修改都會導(dǎo)致項目的絕大多數(shù)緩存失效,從而導(dǎo)致構(gòu)建速度變得極慢
因此官方現(xiàn)在更推薦我們使用 includeBuild,比如 nowinandroid 的構(gòu)建邏輯就通過 includeBuild 放在了 build-logic
目錄
如何復(fù)用 build.gradle 代碼?
其實我們項目中的各個模塊的 build.gradle 中的代碼,大部分是重復(fù)的,做的都是一些重復(fù)的配置,當(dāng)要修改時就需要一個一個去修改了
nowinandroid 通過抽取重復(fù)配置的方式大幅度的減少了 build.gradle 中的代碼,如下所示
plugins { id("nowinandroid.android.feature") id("nowinandroid.android.library.compose") id("nowinandroid.android.library.jacoco") } android { namespace = "com.google.samples.apps.nowinandroid.feature.author" } dependencies { implementation(libs.kotlinx.datetime) }
這是 nowinandroid 的一個 feature 模塊,可以看出除了每個模塊不同的namespace
與各個模塊的依賴之外,其他的內(nèi)容都抽取到nowinandroid.android.feature
等插件中去了,而這些插件的代碼都存放在build-logic
目錄中,通過 includeBuild 引入,大家可自行查看
總得來說,通過這種方式可以大幅減少重復(fù)配置代碼,當(dāng)配置需要遷移時也更加方便
使用 Version Catalog 管理依賴
在 build.gradle 中添加依賴有以下幾個痛點
- 項目依賴統(tǒng)一管理,在單獨(dú)文件中配置
- 不同Module中的依賴版本號統(tǒng)一
- 添加依賴時支持代碼提示
針對這幾種需求,Gradle7.0 推出了一個新的特性,使用 Version Catalog 統(tǒng)一依賴版本,它支持以下特性:
- 對所有 module 可見,可統(tǒng)一管理所有module的依賴
- 支持聲明依賴bundles,即總是一起使用的依賴可以組合在一起
- 支持版本號與依賴名分離,可以在多個依賴間共享版本號
- 支持在單獨(dú)的libs.versions.toml文件中配置依賴
- 支持代碼提示(僅 kts)
noinandroid 中目前已經(jīng)全面啟用了 Version Catalog,如上所示,統(tǒng)一依賴版本,支持代碼提示,體驗還是不錯的
關(guān)于 Version Catalog 的具體使用可以查看:【Gradle7.0】依賴統(tǒng)一管理的全新方式,了解一下~
代碼格式檢查
nowinandroid 作為一個開源項目,不可避免地會有第三方貢獻(xiàn)一些代碼,因此也需要在代碼合并前做一些格式檢查,保證代碼風(fēng)格的統(tǒng)一
nowinandroid 通過 spotless 來檢查代碼格式,主要是通過兩種方式觸發(fā)
- 通過上面提到的 git hook,在代碼 push 時觸發(fā)檢查
- 通過 github workflow,在代碼 push 到 main 分支時觸發(fā)檢查
上面兩種方式都會調(diào)用以下命令
./gradlew spotlessCheck --init-script gradle/init.gradle.kts --no-configuration-cache --stacktrace
可以看出,這里主要是執(zhí)行 spotlessCheck 任務(wù),并且指定了 init-script,我們來看一下 init.gradle.kts 里面做了什么
// init.gradle.kts rootProject { subprojects { apply<com.diffplug.gradle.spotless.SpotlessPlugin>() extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> { kotlin { target("**/*.kt") targetExclude("**/build/**/*.kt") ktlint(ktlintVersion).userData(mapOf("android" to "true")) licenseHeaderFile(rootProject.file("spotless/copyright.kt")) } format("kts") { target("**/*.kts") targetExclude("**/build/**/*.kts") // Look for the first line that doesn't have a block comment (assumed to be the license) licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)") } format("xml") { target("**/*.xml") targetExclude("**/build/**/*.xml") // Look for the first XML tag that isn't a comment (<!--) or the xml declaration (<?xml) licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])") } } } }
可以看出,這里指定了對于 kotlin , kts , xml 等文件的格式要求,比如 kotlin 代碼需要遵守 ktlint 規(guī)范,并且文件開頭必須是 license 聲明
自定義 lint 檢查
除了代碼風(fēng)格的統(tǒng)一,nowinandroid 項目還自定義了一些 lint 檢查,跟 spoltess 一樣,也是通過 git hook 與 github workflow 兩種方式觸發(fā),兩種方式都會觸發(fā)以下代碼
./gradlew lintDemoDebug --stacktrace
nowinandroid 中有一個自定義的 lint 模塊,自定義 lint 規(guī)則就定義在這里,如下所示:
class DesignSystemDetector : Detector(), Detector.UastScanner { override fun createUastHandler(context: JavaContext): UElementHandler { return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { val name = node.methodName ?: return val preferredName = METHOD_NAMES[name] ?: return reportIssue(context, node, name, preferredName) } override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) { val name = node.receiver.asRenderString() val preferredName = RECEIVER_NAMES[name] ?: return reportIssue(context, node, name, preferredName) } } } companion object { @JvmField val ISSUE: Issue = Issue.create( id = "DesignSystem", briefDescription = "Design system", explanation = "This check highlights calls in code that use Compose Material " + "composables instead of equivalents from the Now in Android design system " + "module." ) // Unfortunately :lint is a Java module and thus can't depend on the :core-designsystem // Android module, so we can't use composable function references (eg. ::Button.name) // instead of hardcoded names. val METHOD_NAMES = mapOf( "MaterialTheme" to "NiaTheme", "Button" to "NiaFilledButton", "OutlinedButton" to "NiaOutlinedButton", // ... ) val RECEIVER_NAMES = mapOf( "Icons" to "NiaIcons" ) fun reportIssue( context: JavaContext, node: UElement, name: String, preferredName: String ) { context.report( ISSUE, node, context.getLocation(node), "Using $name instead of $preferredName" ) } } }
總得來說,這個自定義規(guī)則是檢查是否使用了 Compose 的默認(rèn) Material 組件而沒有使用 nowinandroid 封裝好的組件,如果檢查不通過則會拋出異常,提醒開發(fā)者修改
總結(jié)
本文主要介紹了 nowinandroid 項目構(gòu)建腳本中的一系列小技巧,具體包括以下內(nèi)容
- gradle.properties 中的配置
- 自動安裝 git hook
- 使用 includeBuild 而不是 buildSrc
- 如何復(fù)用 build.gradle 代碼?
- 使用 Version Catalog 管理依賴
- 代碼格式檢查
- 自定義 lint 檢查
希望對你有所幫助~
項目地址
以上就是Android開發(fā)設(shè)計nowinandroid構(gòu)建腳本學(xué)習(xí)的詳細(xì)內(nèi)容,更多關(guān)于Android nowinandroid 構(gòu)建腳本的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android使用MediaRecorder實現(xiàn)錄音及播放
這篇文章主要為大家詳細(xì)介紹了Android使用MediaRecorder實現(xiàn)錄音及播放,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02Android編程實現(xiàn)ViewPager多頁面滑動切換及動畫效果的方法
這篇文章主要介紹了Android編程實現(xiàn)ViewPager多頁面滑動切換及動畫效果的方法,以完整實例形式分析了ViewPager多頁面滑動切換效果的布局及功能實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11Android開發(fā)應(yīng)用第一步 安裝及配置模擬器Genymotion
這篇文章主要介紹了Android開發(fā)應(yīng)用第一步,即安裝及配置模擬器Genymotion,感興趣的小伙伴們可以參考一下2015-12-12Android讀取手機(jī)通訊錄聯(lián)系人到自己項目
這篇文章主要為大家詳細(xì)介紹了Android讀取手機(jī)通訊錄聯(lián)系人到自己項目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07Android 利用廣播監(jiān)聽usb連接狀態(tài)(變化情況)
這篇文章主要介紹了Android 利用廣播監(jiān)聽usb連接狀態(tài),需要的朋友可以參考下2017-06-0613問13答全面學(xué)習(xí)Android View繪制
這篇文章主要為大家詳細(xì)介紹了Android View繪制,13問13答幫助大家全面學(xué)習(xí)Android View繪制,感興趣的小伙伴們可以參考一下2016-03-03