Gradle项目管理依赖的船新版本


/   今日科技快讯   /

昨日晚,微软公司正式推出新的Windows 11系统。据悉,这是微软近6年来首次推出新的Windows系统。此前,Windows 10是世界上使用最广泛的PC操作系统,有超过13亿部设备在使用。

据报道,微软首席产品官帕诺斯·潘奈将Windows 11系统描述为“让你更接近自己所爱之物的Windows”。新一代Windows在图标、菜单栏、声音、性能上均有变化,并且还可以实现自定义电脑的新方法。

/   作者简介   /

明天就是周六啦,祝大家周末愉快!

本篇来自leobert-lan的投稿,和大家分享了Gradle项目管理依赖另一种思路,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章!

leobert-lan的博客地址:

https://juejin.cn/user/2066737589654327/posts

/   写在最前   /

在前段时间,我发布过一篇拙见:组件化场景下module依赖实践方案。该文在组件化背景下,探索了一种方案,可以同时满足 减少编译数量以减少编译时间、便捷的修改依赖树以灵活改动任意层级的Module内容。具体内容可以阅读前文,不再赘述

除却Module依赖,还有库包依赖 ,本文着重于探索 库包依赖项 的管理方式,而且是狭义上的仓库下的库包。并斗胆称之为最佳实践

/   背景与知识   /

首先确定一件事情:

implementation fileTree(dir: 'libs', include: ['*.jar'])

此类方式,引入的库包不属于仓库范畴,仅讨论基于Maven仓库的范畴,赘述一句,仓库按照习惯又可以分为两种类型:

  • Local:特指Maven的MavenLocal仓库,或者Gradle的Cache,MavenLocal和Gradle的Cache本质是一致

  • Remote:通过Uri指定的特定位置的仓库,最为常见的是MavenCentral和JCenter仓库。当然,可以将本机的目录指定为 "远程仓库" 位置。

当然,这并不影响本文的讨论

众所周知,使用Gradle确定仓库的库包需要三个因素:

  • GroupId

  • ArtifactId

  • version ,'+'号通配符表达最新的含义

for example:

androidx.core:core-ktx:1.3.2

  • GroupId 为 "androidx.core"

  • ArtifactId 为 "core-ktx"

  • version 为 "1.3.2"

问题背景

以Android为例,商业项目中,一个Project仅存在一个Module 的情况应该非常少见了, 往往一个Project下会存在多个Module,而且存在一定的依赖关系。

如果没有合适的管理手段,那么每个Module均声明自身的依赖项,当发生版本变更时:

  • 修改过于零碎

  • 同一个依赖项在不同Module下可能出现版本差异,这也是上一点所带来的后果

举个更典型的例子,以后端项目为例,微服务 的概念大家一定不陌生.

即使未曾深入了解,也知道后端将整个服务体系进行了拆分,用多个子系统项目(微服务)共同 支撑完整的服务体系。以此达到 降低复杂度、 根据业务特性使用不同框架、 根据业务权重定制运维策略 等目的

而微服务之间通过RPC进行通信,而此处势必牵涉一个最大的 痛点 :Service方法签名和DTO数据保持一致,否则会带来 方法不存在 或者 数据遗失、解析错误 等问题

/   传统做法优劣   /

比较早期的做法,是在Gradle构建时的运行环境中,创建或者利用Project级别的集合对象,将依赖项信息全部写入其中,各个Module使用时,达成了统一。

大家对这种做法很熟悉,不再用代码举例。往往需要用到Extension扩展,为了方便描述,我们将:存储依赖项信息的Project级别集合 称为 Ext.deps

优点:

  • 统一管理入口。一次修改,全Project生效

缺点:

  • 无法进行代码提示

  • 一般无法兼容于构建工具的 新版本提示

  • 仅针对单Project,无法应对多Project,后端的微服务往往是多Project

改良版

利用Gradle 可以apply 远程构建脚本 (xxx.gradle) 的特性,进行方案改进。

将 "构建 Ext.deps 信息" 的 脚本,存储于网络特定位置,以解决多Project难以管理的问题。

一般需要对脚本文件按照版本命名,并保有所有版本的脚本。

这样可以避免:项目回溯版本功能时,出现额外问题。

/   利用Gradle后门   /

Gradle编译项目是很有意思的事情,我们知道:在成功加载完Gradle项目后,会 编译Gradle脚本 并生成各类Gradle任务,实际情况会更加复杂,为了方便,我们将之称为Task编译

既然存在编译过程,Gradle团队索性留了一个后门:

如果根项目下存在"buildSrc", gradle 认为这是在Task编译过程中需要编译的内容,这些内容可能包含了:

  • Gradle插件内容

  • 插件设置内容

  • 等等

并且其编译结果对于该项目下的Gradle内容透明

这并不是一个新的特性,它至少已经有五年的历史了。官方文档(https://docs.gradle.org/current/userguide/organizing_gradle_projects.html)对其使用方式做了概要的描述。

勘误

因为buildSrc机制已经不是一个新特性了,故而利用这个机制去 管理Gradle依赖信息 已经是一个老话题了。

可能是巧合,该做法出现在开发者视野中时,刚好是 gradle开始对 kotlin-dsl 进行支持,同样不是新特性,大约是三年前的Gradle-4.10。

而开始流行的做法又恰好对新特性进行了尝鲜,并且在讲解视频中留下了一些坑,于是这一做法的着重点,便被吸引到了 如何正确使用kotlin管理Gradle项目的依赖项这一话题上。

这一做法和kotlin、kts脚本并无实质关联

做法

在buildSrc目录下,按照标准sourceSet结构建立目录,并新增类文件例如:

buildSrc/src/main/java/Deps.java

public class Deps {
    public static String junit = "junit:junit:4.13.0";
}

sync后,类会被编译,我们可以在项目下的Gradle脚本中,只用使用,例如:

dependencies {
    //...

    testImplementation Deps.junit
//    'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

并且可以享有 代码提示 、 跳转 、 javaDoc弹窗 功能

而可查询到的常见做法,往往是使用kotlin类,那么就需要让buildSrc 在编译时支持kotlin ,那么自然需要 添加插件 :

在buildSrc下新建 build.gradle 并添加插件:

apply plugin: "kotlin"

buildscript {
    ext.kotlin_version = "1.4.21"
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
repositories {
    jcenter()
}

即可,此时添加的kotlin类即可被编译。

buildSrc/src/main/java/KDeps.kt

object KDeps {
    @JvmStatic
    val ext_junit = "androidx.test.ext:junit:1.1.2"
}

使用示例:

dependencies {
    testImplementation Deps.junit
//    'junit:junit:4.+'
    androidTestImplementation KDeps.ext_junit
    //'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

同样可以享有 代码提示 、 跳转 、 javaDoc弹窗 功能

而网传的 kts脚本 以及添加 kotlin-dsl支持,其实在这个需求中,并无真正的有效用途,只不过是应用了kts脚本后, 本身就需要编译kotlin内容,所以 默认使用了kotlin编译插件

言归正传,使用这种管理方式后,我们解决了无代码提示的弊端,再次 利用机器解放生产力。

但是,我们没有解决服务端例子中的问题

将依赖信息打包发布

我想你已经深刻意识到了buildSrc机制的本质是啥:

利用Gradle 编译 buildSrc内容,产物供 后续的 该项目的 Gradle编译过程 使用

那么你一定可以想到,buildSrc可以申明自身的依赖!

于是,我们对常用库包进行分析后,选取对象并确定版本后,即可编写一个Library

  • 将库包信息写成常量

  • 对Library建立版本机制

  • 发布Library并在buildSrc中使用

这是最简单的做法,即可在多个Project下,以最小的人力成本管理依赖并满足 一致性需求

进阶

Library依赖 Gradle后,可以编写 Gradle-Task内容配置 的过程代码,封装 依赖添加 和 依赖检查 等内容。

举个简单的例子:

object KDeps {
    //    @JvmStatic
    const val ext_junit = "androidx.test.ext:junit:1.1.2"
}

public class Deps {
    public static String junit = "junit:junit:4.13.0";

    public static void applyAll(Project project) {
        project.getDependencies().add(
                "testImplementation", junit
        );
        project.getDependencies().add(
                "androidTestImplementation",KDeps.ext_junit
        );
    }
}

buildSrc/build.gradle

apply plugin: "kotlin"

buildscript {
    ext.kotlin_version = "1.4.21"
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//        implementation 'com.android.tools.build:gradle:4.1.1'
        //gradle sdk
        gradleApi()
    }
}
repositories {
    jcenter()
}

在app 的build.gradle中,可以这样使用:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    //略
}

dependencies {
    //略

    //修改为直接在 afterEvaluate 后调用函数设置
//    testImplementation Deps.junit
//    androidTestImplementation KDeps.ext_junit
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

afterEvaluate {
    Deps.applyAll(project)
}

当然,我们在这个过程中还可以使用各类编程技巧。

此时,我们已经拥有了无限可能,根据项目的实际需求 ,自行拓展吧。

推荐阅读:

我的故事登上了Android开发者的官网

Android 12上全新的应用启动API,适配一下?

为什么如此安全的https协议却仍然可以被抓包呢?

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值