2024年最新组件化开发必备:Gradle 依赖切换源码的实践_gradle换源(1),2024年最新2024最新鸿蒙大厂面试题来袭

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • group:依赖的 group id
  • open:表示是否启用源码依赖
  • children.name:表示子工程的 module 名称,对应于依赖中的 artifact id
  • children.path:表示子工程对应的相对目录

也就是说,

  • 一个工程下的多个子工程的 group id 必须相同
  • children.name 必须和依赖的 artifact id 相同

上述配置文件的工作流程是,

def sourceSwitches = new HashMap<String, SourceSwitch>()

// Load sources configurations.
parseSourcesConfiguration(sourceSwitches)

// Checkout remote sources.
checkoutRemoteSources(sourceSwitches)

// Replace dependencies with sources.
replaceDependenciesWithSources(sourceSwitches)

  • 首先,Gradle 在 setting 阶段解析上述配置文件
  • 然后,根据解析的结果,将打开源码的工程通过 project 的形式引用到项目中
  • 最后,根据上述配置文件,将项目中的依赖替换为工程引用
2.2 为项目动态添加子工程

如上所述,这里我们忽略掉 json 配置文件解析的环节,直接看拉取最新分支并将其作为子项目添加到项目中的逻辑。该部分代码实现如下,

/** Checkout remote sources if necessary. */
def checkoutRemoteSources(sourceSwitches) {
def settings = getSettings()
def rootAbsolutePath = settings.rootDir.absolutePath
def sourcesRootPath = new File(rootAbsolutePath).parent
def sourcesDirectory = new File(sourcesRootPath, “open_sources”)
if (!sourcesDirectory.exists()) sourcesDirectory.mkdirs()
sourceSwitches.forEach { name, sourceSwitch ->
if (sourceSwitch.open) {
def sourceDirectory = new File(sourcesDirectory, name)
if (!sourceDirectory.exists()) {
logd(“clone start [ n a m e ] b r a n c h [ name] branch [ name]branch[{sourceSwitch.branch}]”)
“git clone -b ${sourceSwitch.branch} s o u r c e S w i t c h . u r l " . e x e c u t e ( n u l l , s o u r c e s D i r e c t o r y ) . w a i t F o r ( ) l o g d ( " c l o n e c o m p l e t e d [ {sourceSwitch.url} ".execute(null, sourcesDirectory).waitFor() logd("clone completed [ sourceSwitch.url".execute(null,sourcesDirectory).waitFor()logd("clonecompleted[name] branch [KaTeX parse error: Expected 'EOF', got '}' at position 26: …tch.branch}]") }̲ else { def sb …{currentBranch}], checkout branch [${sourceSwitch.branch}]”)
def out = new StringBuffer()
“git pull”.execute(null, sourceDirectory).waitFor()
“git checkout -b s o u r c e S w i t c h . b r a n c h o r i g i n / {sourceSwitch.branch} origin/ sourceSwitch.branchorigin/{sourceSwitch.branch}”
.execute(null, sourceDirectory).waitForProcessOutput(out, System.err)
logd(“checkout completed: KaTeX parse error: Expected 'EOF', got '}' at position 27: …ng().trim()}") }̲ } // After che…{child.name}”)
settings.project(“😒{child.name}”).projectDir = new File(sourcesDirectory, child.path)
}
}
}
}

这里,我将子项目的源码克隆到 settings.gradle 文件的父目录下的 open_sources 目录下面。这里当该目录不存在的时候,我会先创建该目录。这里需要注意的是,我在组织项目目录的时候比较喜欢将项目的子工程放到和主工程一样的位置。所以,上述克隆方式可以保证克隆到的 open_sources 仍然在当前项目的工作目录下。

然后,我对 sourceSwitches,也就是解析的 json 文件数据,进行遍历。这里会先判断指定的源码是否已经拉下来,如果存在的话就执行 checkout 操作,否则执行 clone 操作。这里在判断当前分支是否为目标分支的时候使用了 git rev-parse --abbrev-ref HEAD 这个 Git 指令。该指令用来获取当前仓库所处的分支。

最后,将源码拉下来之后通过 Settingsinclude() 方法加载指定的子工程,并使用 Settingsproject() 方法指定该子工程的目录。这和我们在 settings.gradle 文件中添加子工程的方式是相同的,

include ‘:utils-core’, ‘:utils-ktx’
project(‘:utils-core’).projectDir = new File(‘…/AndroidUtils/utils’)
project(‘:utils-ktx’).projectDir = new File(‘…/AndroidUtils/utils-ktx’)

2.3 使用子工程替换依赖

动态替换工程依赖使用的是 Gradle 的 ResolutionStrategy 这个功能。也许你对诸如

configurations.all {
resolutionStrategy.force ‘io.reactivex.rxjava2:rxjava:2.1.6’
}

这种写法并不陌生。这里的 forcedependencySubstitution 一样,都属于 ResolutionStrategy 提供的功能的一部分。只不过这里的区别是,我们需要对所有的子项目进行动态更改,因此需要等项目 loaded 完成之后才能执行。

下面是依赖替换的实现逻辑,

/** Replace dependencies with sources. */
def replaceDependenciesWithSources(sourceSwitches) {
def gradle = settings.gradle
gradle.projectsLoaded {
gradle.rootProject.subprojects {
configurations.all {
resolutionStrategy.dependencySubstitution {
sourceSwitches.forEach { name, sourceSwitch ->
sourceSwitch.children.each { child ->
substitute module(“ s o u r c e S w i t c h . a r t i f a c t : {sourceSwitch.artifact}: sourceSwitch.artifact:{child.name}”) with project(“😒{child.name}”)
}
}
}
}
}
}
}

这里使用 Gradle 的 projectsLoaded 这个点进行 hook,将依赖替换为子工程。

此外,也可以将子工程替换为依赖,比如,

dependencySubstitution {
substitute module(‘org.gradle:api’) using project(‘:api’)
substitute project(‘:util’) using module(‘org.gradle:util:3.0’)
}

2.4 注意事项

上述实现方式要求多个子工程的脚本尽可能一致。比如,在 AndroidUtils 的独立工程中,我通过 kotlin_version 这个变量指定 kotlin 的版本,但是在 android-mvvm-and-architecture 这个工程中使用的是 kotlinVersion. 所以,当切换了子工程的源码之后就会发现 kotlin_version 这个变量找不到了。因此,为了实现可以动态切换源码,是需要对 Gradle 脚本做一些调整的。

在我的实现方式中,我并没有将子工程的源码放到主工程的根目录下面,也就是将 open_sources 这个目录放到 appshell 这个目录下面。而是放到和 appshell 同一级别。

这样做的原因是,实际开发过程中,通常我们会克隆很多仓库到 open_sources 这个目录下面(或者之前开发遗留下来的克隆仓库)。有些仓库虽然我们关闭了源码依赖,但是因为在 appshell 目录下面,依然会出现在 Android Studio 的工程目录里。而按照上述方式组织目录,我切换了哪个项目等源码,哪个项目的目录会被 Android Studio 加载。其他的因为不在 appshell 目录下面,所以会被 Android Studio 忽略。这种组织方式可以尽可能减少 Android Studio 加载的文本,提升 Android Studio 响应的速率。

总结

上述是开发过程中替换依赖为源码的“无痕”修改方式。不论在组件化还是非组件化需要开发中都是一种非常实用的开发技巧。按照上述开发开发方式,我们可以既能开发 android-mvvm-and-architecture 的时候随时随地打开 AndroidUtils 进行修改,亦可对 AndroidUtil 这个工程独立编译和开发。

源代码参考 android-mvvm-and-architecture 项目(当前是 feature-3.0 分支)的 AppShell 下面的 sources.gradle 文件。

作者:开发者如是说
链接:https://juejin.cn/post/7174753036143689787

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值