github blog
qq: 2383518170
wx: lzyprime

λ:

仓库地址: https://github.com/lzyprime/android_demos

本来想把compose版本分离成单独分支:dev_compose; 但是后来发现与dev分支除了view层不太一样,剩下的全是同样代码;甚至view层一些compose组件也全是一样的。

model层里,对数据组织和封装在频繁的改动,想找到更合理易用的方式,比如对DataStore的提供和使用方式,已经调整过好几版,目前的仍不是满意版本。

如果两个分支,这部分代码同步就很烦人。git sub module, git rebase, 手动复制。哪一个都不方便。

所以,把view以外公用的部分,抽成单独的gradle module, composeview部分也抽成一个module。然后在gradle脚本里配好依赖关系。

同时,将gradle脚本由 Groovy 迁到 KTS

gradle kts

gradle 官网文档

android 官网迁移文档

迁移完发现android官网文档居然也提了这事。

好处

  • 相比groovy, 对kotlin更熟悉。脚本易读性提高,对脚本中每一步在执行什么,什么意思更容易掌握,点开看源码和注释。
  • 更规范,去糖。groovy为了脚本编写便捷,提供了一堆简便写法,而很多其实是靠字符串解析,看是否符合规则,然后去调用真正的接口。
  • 接口废弃等提醒。gradle即将废弃接口,接口警告信息等等,都会向kotlin代码一样,直接突出显示。
  • KTS版本去学习gradle的用法。之后就算是groovy版本的,也能看个大概,看着官网文档和基础语法也能写的差不多。可能简便写法不怎么会,但是中规中矩的脚本能跑应该没问题。

坏处

  • 相比groovy, 肯定还是简陋,不完善。包括文档里,常常会有只支持groovy的提示。

迁移过程

gradle 迁移文档

gradle脚本文件名加上.kts后缀(如build.gradle -> build.gradle.kts), 然后sync一下, 解决所有报错。每次最好只改一个文件,否则报错难修。

  • 字符串必须全是双引号
  • 函数调用加括号。如classpath, implementation等等后面空格加字符串的,一般是函数调用。改成classpath(xxx)样式
  • 属性值,如 minSdk, targetSdk, versionCode等等被做成了属性。同时如果属性为bool类型,名字会变成isXXX的形式。
  • tasks, ext, extra, buildSrc

tasks

每个task, 包含name:Stringargs:MapconfigureClosure: Function. groovy提供了一堆简便写法,但最终肯定归到这三部分。以task clean为例。

// groovy
task clean(type: Delete) {delete rootProject.buildDir
}

如果点进去,会发现批到的是task(name:String),后边部分都会当字符串处理。这就是groovy提供便捷写法的方式之一,字符串解析。最后相当于:

// 伪代码
task(args: {"type": Delete::class}, name: "clean", configureClosure: { // Deletedelete(rootProject.buildDir)},
)

的确简便写法够简洁形象,就像声明一个函数。可是不看源码之类的,谁知道是什么。

kotlin也提供了一堆简便写法,以incline function的形式,可以一层层点到最后。

gradle 任务文档

ext问题

KTS 也有 ext函数,但如果像之前在buildScript块里写,就会报错。点进去就知道原因:

val org.gradle.api.Project.`ext`: org.gradle.api.plugins.ExtraPropertiesExtension get() =(this as org.gradle.api.plugins.ExtensionAware).extensions.getByName("ext") as org.gradle.api.plugins.ExtraPropertiesExtensionfun org.gradle.api.Project.`ext`(configure: Action<org.gradle.api.plugins.ExtraPropertiesExtension>): Unit =(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("ext", configure)

也就是尝试把当前对象转为ExtensionAware。在groovy中,buildScriptProject的方法,Project实现了ExtensionAware接口。在KTS里,buildScript 来自KotlinBuildScript抽象类, 是个ProjectDelegate,用委托的方式访问Project, 往上找基类也的确是Project

但是buildScript函数接收的是操作ScriptHandlerScope类型。

@Suppress("unused")
open fun buildscript(@Suppress("unused_parameter") block: ScriptHandlerScope.() -> Unit): Unit =internalError()// use:
buildscript { // this: ScriptHandlerScope...
}

也就是说,代码块里的 this 是个 ScriptHandlerScope, 并没有实现ExtensionAware。 所以ScriptHandlerScope as ExtensionAware失败了。

这也是为什么ext在顶级块里写或者在allprojects块里可以正常工作:

buildScript {...}// this: ProjectDelegate
ext {set("key", "value")
}allprojects { // this: Projectext {set("k", "v")}
}tasks.register<Delete>("clean") {rootProject.ext["key1"] // 指定Projectdelete(rootProject.buildDir)
}

但这只是定义的时候,使用的话,同样因为这种限制,要看清楚作用域,是否能转为ExtensionAware,还要搞清楚是谁的。

同时受kotlin静态语言的限制,想直接Project.ext.key1, 甚至Project.key1使用,是不可能的。就得Project.ext["key1"]

tasks.register<Delete>("clean") {val key1 = rootProject.ext["key1"] // 指定Projectdelete(rootProject.buildDir)
}

但是在buildScript里这么写又过不去。此时通过ExtensionAware.extentions.getByName("ext")还拿不到。其实在groovy中也是点不进去的,可以看看groovy怎么处理的,怎么达到动态语言的效果。

ext -> extra

所以这东西基本就废了。然后提供了extra

buildscript {val gradleVersion by extra("7.0.1")val kotlinVersion by extra{ "1.5.21" }extra["activityVersion"] = "1.3.1"extra["lifecycleVersion"] = "2.3.1"
}// module project
val kotlinVersion: String by rootProject.extraval activityVersion: String by rootProject.extra
val lifecycleVersion: String by rootProject.extra

如果通过委托属性的方式获取值。需要显式声明类型。源码:

val ExtensionAware.extra: ExtraPropertiesExtensionget() = extensions.extraProperties

也就是说,其实和ext拿到的是一样的,Project.ext其实就是在把ExtensionAware.extensions.extraProperties抛出去。

所以基础的set get等仍然好使。额外添加了一堆委托属性和函数,方便创建获取变量。

val kkk by extra(vvv):

// val kkk by extra(vvv)
operator fun <T> ExtraPropertiesExtension.invoke(initialValue: T): InitialValueExtraPropertyDelegateProvider<T> =InitialValueExtraPropertyDelegateProvider.of(this, initialValue)// InitialValueExtraPropertyDelegateProvider(extra, vvv)class InitialValueExtraPropertyDelegateProvider<T>
private constructor(private val extra: ExtraPropertiesExtension,private val initialValue: T
) {companion object {fun <T> of(extra: ExtraPropertiesExtension, initialValue: T) =InitialValueExtraPropertyDelegateProvider(extra, initialValue)}operator fun provideDelegate(thisRef: Any?, property: kotlin.reflect.KProperty<*>): InitialValueExtraPropertyDelegate<T> {// 插入, 变量名(kkk) 作为keyextra.set(property.name, initialValue)return InitialValueExtraPropertyDelegate.of(extra)// InitialValueExtraPropertyDelegate(extra)}
}class InitialValueExtraPropertyDelegate<T>
private constructor(private val extra: ExtraPropertiesExtension
) {companion object {fun <T> of(extra: ExtraPropertiesExtension) =InitialValueExtraPropertyDelegate<T>(extra)}// 赋值操作。 kkk = nvvv -> extra.set(kkk, nvvv)operator fun setValue(receiver: Any?, property: kotlin.reflect.KProperty<*>, value: T) =extra.set(property.name, value)// 取值操作。val nk = kkk -> val nk = extra.get(kkk)@Suppress("unchecked_cast")operator fun getValue(receiver: Any?, property: kotlin.reflect.KProperty<*>): T =uncheckedCast(extra.get(property.name))
}

中规中矩的委托。val kkk: T by extra也是一样:

operator fun ExtraPropertiesExtension.provideDelegate(receiver: Any?, property: KProperty<*>): MutablePropertyDelegate =if (property.returnType.isMarkedNullable) NullableExtraPropertyDelegate(this, property.name)else NonNullExtraPropertyDelegate(this, property.name)private
class NonNullExtraPropertyDelegate(private val extra: ExtraPropertiesExtension,private val name: String
) : MutablePropertyDelegate {override fun <T> getValue(receiver: Any?, property: KProperty<*>): T =if (!extra.has(name)) cannotGetExtraProperty("does not exist")else uncheckedCast(extra.get(name) ?: cannotGetExtraProperty("is null"))override fun <T> setValue(receiver: Any?, property: KProperty<*>, value: T) =extra.set(property.name, value)privatefun cannotGetExtraProperty(reason: String): Nothing =throw InvalidUserCodeException("Cannot get non-null extra property '$name' as it $reason")
}

getValue, setValue是根据变量类型做类型转换。所以要写类型,还要写对。

buildSrc

kotlin dsl plugin 文档

另外完成共享的方式。在rootProject目录下创建buildSrc文件夹,并创建build.gradle.kts

/
|-buildSrc|- src/main/kotlin/xxx.kt|- build.gradle.kts
//buildSrc/build.gradle.kts
plugins {`kotlin-dsl`
}repositories {mavenCentral()
}

src/main/kotlin下的内容在工程内共享。所以可以把变量定义在这:

// src/main/kotlin/versions.kt
const val kotlinVersion = "1.5.30"
...

其他地方可以直接用。

好处是往gradle添加附加功能更方便,易于管理。

弊端就是变量如果放在这,IDE可视化的Project Structure识别失败,就会一直提示有内容可以更新。

module 管理

没什么可讲的。new 一个 module。 根据需要选择类型。然后就是build.gradle.kts处理好依赖和构建。settings.gradle.ktsinclude

当 A_module 依赖 B_module:

// A_module build.gradle.kts
dependencies {implementation(project(":B_module"))

更多具体操作可以看文档。转成KTS不就是为了文档读着更容易。

gradle迁到kts, 以及module管理相关推荐

  1. Android Studio Gradle打包实践之多渠道+版本号管理

    上次介绍了 Android Studio的安装.配置和基本使用 .这次讲一下Android Studio用到的打包工具Gradle. Gradle 是一种构建项目的框架,兼容Maven.Ant,为Ja ...

  2. Android Studio的Gradle常用命令配置和依赖管理

    一,gradlew常用命令 ./gradlew -v 版本号 ./gradlew clean 清除工程目录下的build文件夹 ./gradlew build 检查依赖并编译打包 这里注意的是 ./g ...

  3. gradle第三方Jar包依赖统一管理

    1. 背景 一个gradle的工程中往往包含很多的子工程,每个子工程都有其自己的Jar包依赖.现实情况是各个子工程对jar包的引入都是比较随意的,版本号各式各样,如何统一各个子工程的版本号?统一管理第 ...

  4. 打卡学习Gradle深度解析 - kts脚本加载流程

    kts脚本加载流程 和groovy脚本一样,kts脚本也分为2个阶段 stage 1 执行buildscript和plugins部分,执行结果会对stage2 program的classpath有影响 ...

  5. gradle引入子module配置_原创 | 看完此文,你对Gradle的理解又升级了

    前言 这一篇来介绍一些Gradle进阶的内容,当然进阶内容非常多,这篇文章就总结一些相对重要的.常用的一些知识点,比如Gradle的签名配置和依赖管理. 1.Android签名文件配置 在一般公司中, ...

  6. linux gradle目录结构,android studio中,project和module的目录结构

    project.iml--NO:这个是用来管理project的相关配置信息的,也是标识这个文件夹是一个android项目的.如果误删该文件,Android Studio将不会将该文件当做安卓项目,只会 ...

  7. Gradle 2.0 用户指南翻译——第五十章. 依赖管理

    本文禁止w3cschool转载! 翻译项目请关注Github上的地址:https://github.com/msdx/gradledoc . 本文翻译所在分支:https://github.com/m ...

  8. Gradle 1.12用户指南翻译——第五十章. 依赖管理

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. kts代替gradle升级指南高阶玩法

    1.整体拦截自定义版本号渠道名编译时间.apk 2.整体拦截所有模块字符串加密 查看全文 http://www.taodudu.cc/news/show-5962886.html 相关文章: Kotl ...

最新文章

  1. java程序中的图片与数值关联_Java从图片中读取图片的元数据Exif信息
  2. 与基础事务管理器通讯失败
  3. mysql复杂连接查询语句_MySQL查询语句之复杂查询
  4. 愈学习愈发现自己的无知
  5. Spring定时任务注解@Scheduled+@EnableAsync用法详解(简单说明+应用场景+demo源代码+执行过程分析)
  6. OpenCASCADE:要求
  7. Colored Sticks--POJ 2513
  8. 【Eviews】第九周实验-二次/对数拟合、预测
  9. 6、oracle数据库下查询操作
  10. java 线程池 固定大小_使用Executors服务在Java中创建固定大小线程池的最佳方法...
  11. UVa 674 - Coin Change
  12. centos中,tomcat项目创建文件的权限研究
  13. 深搜和广搜--原理彼此的优缺点
  14. MFC框架下-调通官方demo以及如何使用SDK进行项目开发
  15. VIVADO 下载mcs 文件
  16. Matlab 多行屏蔽或注释方法
  17. Unity实现键鼠控制立方体移动旋转
  18. 网络爬虫——票房网数据抓取及MYSQL存储
  19. flvplayer.swfnbsp;flv视频播放器…
  20. 微服务开发中的数据架构设计 1

热门文章

  1. 数据分析师、大数据开发、Hadoop开发工程师、数据挖掘、算法工程师的工资薪水到底怎么样?
  2. BC20/BC26-opencpu移植cjson,mqtt等注意事项
  3. Unity --- 触摸方法,以及灯光与烘培的使用
  4. Excel 单元格适配
  5. gs 服务器文件,服务器gs
  6. win10关闭未格式化U盘插入提示格式化弹窗
  7. php图片背景平铺,css如何让背景图片平铺?css背景图片平铺四种方式介绍
  8. css浮动后页面乱了怎样解决,详解浮动元素引起的问题和解决办法
  9. 主题包网址(Theme)
  10. 关于mybatis中的大于号和小于号的错误