在Gradle中处理大型项目可能很痛苦。随着项目逐渐变大,配置与构建时间通常也会增长。将大型项目拆成多个模块有助于缓解问题,但如果没有严格遵循那些似乎不断变化的项目结构与构建脚本配置建议,就很容易让构建系统做远超必要范围的工作。Gradle做这些额外工作时,你只能坐着等待。
和许多公司一样,Dropbox使用Gradle构建Android应用。我们的单体仓库包含近600个项目,大部分并非工程师日常工作需要。这意味着Android Studio必须加载远多于实际需要的模块,我们把时间浪费在等待工具上。
在大型项目中开发功能时,我们通常主要处理某个具体功能模块。通过在模块内使用合理抽象,我们可以开发代码、编写测试,而不依赖单体仓库中的大多数其他项目。假如能轻松告诉Gradle当前工作位置和需要哪些依赖,让它忽略其余部分,岂不是很好?
为了回答这个问题,我们最近尝试寻找一种简单方法:让工程师指定正在处理的模块,让Gradle和Android Studio忽略其余部分。
手动方式
过去,我们通过手动调整settings.gradle.kts文件中包含的项目,减少Gradle加载的项目数量。最快但粗糙的做法是直接注释掉不需要的项目:
include(":sample:app1")
//include(":sample:app2")
include(":sample:lib1a")
include(":sample:lib1b")
//include(":sample:lib2a")
//include(":sample:lib2b")
//include(":sample:lib2c")
include(":sample:lib-shared")
include(":sample:moved")
project(":sample:moved").projectDir = File("sample/lib-moved")
然而,项目变大、模块间依赖关系越来越复杂时,这种方式很快就会难以管理。
另一种方式是把所有include(“…”)语句提取到单独的文件。这样,只需选择要应用哪些文件,就能更方便地在模块间切换:
//apply(from = File("project-1.settings.gradle.kts")
//apply(from = File("project-2.settings.gradle.kts")
//apply(from = File("project-3.settings.gradle.kts")
apply(from = File("settings-all.gradle.kts"))
但面对所有可能的排列组合,这种方法也很容易变得难以管理。此外,确保这些额外的settings文件保持同步,也可能成为噩梦。
既然Gradle已经知道项目的依赖树,如果有简单的方法让Gradle自动创建这些文件,岂不是很好?
Focus登场
Focus恰好可以做到这一点。我们的Focus Gradle插件会评估项目配置,为你希望聚焦的模块创建专属settings.gradle文件。文件只包含该模块需要的项目依赖,让你轻松忽略其余项目。插件还会创建一个.focus文件,用来标识当前应该聚焦哪个模块。 (Focus Gradle Plugin)
有了这些文件,同步项目时Gradle只会配置需要的模块。删除.focus文件——可以手动删除,也可以使用clearFocus任务——就会恢复为包含所有模块。
例如,我们在Dropbox开发设计系统时,Focus插件让我们轻松聚焦到UI Components Playground项目(单体仓库中用于开发设计系统组件的示例应用)。这让IDE同步时间从1分钟减少到15秒。
开始时,只需运行以下命令:
./gradlew :applications:uicomponents_playground:focus
这会在模块的build目录中创建focus.settings.gradle文件,其中只包含构建uicomponents_playground模块所需的项目;同时在项目根目录创建.focus文件,指向新生成的Gradle settings文件。点击Android Studio中的大象同步图标后,只会加载必需模块,改善IDE和Gradle的性能。
如果想处理另一个模块——例如需要更新的依赖——只需聚焦到那个模块并同步。
./gradlew :dsys:components:focus
通过这种方式,可以轻松减少IDE加载及Gradle配置的模块数量。当准备重新构建整个项目时,只需删除项目根目录的.focus文件,或运行以下Gradle任务:
./gradlew clearFocus
使用Focus后,工程师在一天中可以更快迭代,把更多时间用于提升正在开发的代码质量,减少等待工具同步的时间。
添加Focus插件
Focus是settings插件,因此应在settings.gradle(.kts)文件中应用。由于目前插件发布在Maven Central上,需要在pluginsManagement块中把它加入仓库列表:
// settings.gradle(.kts)
pluginsManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("com.dropbox.focus") version "0.4.0"
}
接着,将全部include语句移入settings-all.gradle文件:
// settings-all.gradle(.kts)
include ':sample:app2'
include ':sample:lib2c'
include ':sample:lib-shared'
// ...
include ':sample:moved'
project(':sample:moved').projectDir = new File("sample/lib-moved")
也可以在settings.gradle.kts文件中配置插件:
// settings.gradle(.kts)
focus {
// The name of the settings file
allSettingsFileName = "settings-all.gradle" // Default
// The name of the pointer file that tells Focus which module to focus on
// This should be added to your .gitignore file.
focusFileName = ".focus" // Default
}
最后,别忘了将Focus文件——默认是.focus——加入.gitignore文件。
完成这些步骤后,就能使用各子项目中的focus Gradle任务,减少坐着等待的时间。
Dropbox的开源实践
已经使用Focus插件的Dropbox员工对效果非常满意。Android工程师看到Android Studio和Gradle性能显著改善;某些情况下,Android Studio与Gradle同步所需时间从2分钟降到20秒。我们为构建性能改善感到兴奋,更为能把这些改善分享给开源社区而兴奋。
说说个人经历:我多年参与开源社区,开发并贡献开源库。在考虑下一份工作时,我很看重能加入一家不仅重视、而且积极参与开源社区的公司。了解到Dropbox支持使用开源库,并鼓励员工向社区回馈,是让我对Dropbox感兴趣的一件事。(如果你也对此感兴趣,我们正在招聘!) (招聘职位)
当我为Focus制作原型,并提出它可以成为通用插件时,Dropbox鼓励我投入时间构建它,并分享给社区。虽然Focus尚未准备好发布1.0版,我们邀请你试用并告诉我们使用效果。如果遇到任何问题,也请告诉我们。 (试用Focus;提交问题)











暂无评论内容