专栏名称: 郭霖
Android技术分享平台,每天都有优质技术文章推送。你还可以向公众号投稿,将自己总结的技术心得分享给大家。
目录
相关文章推荐
鸿洋  ·  掌握这17张图,掌握RecyclerView ... ·  2 天前  
鸿洋  ·  5种常见Gradle依赖版本管理指南 ·  6 天前  
51好读  ›  专栏  ›  郭霖

一文了解 Gradle 的文件api

郭霖  · 公众号  · android  · 2024-12-09 08:00

正文



/   今日科技快讯   /

近日,广电总局网络视听司发布《管理提示(AI魔改)》指出,近期,AI“魔改”视频以假乱真、“魔改”经典现象频发。如《甄嬛传》变身“枪战片”、《红楼梦》改成“武打戏”、孙悟空骑着摩托车扬长而去等。《管理提示》认为,这些视频为博流量,毫无边界亵渎经典IP,冲击传统文化认知,与原著精神内核相悖,且涉嫌构成侵权行为。

/   作者简介   /

大家周一好,新的一周继续加油!

本篇文章转自小墙程序员的博客,文章主要分享了如何使用 Gradle 的文件 api,相信会对大家有所帮助!

原文地址:
https://juejin.cn/post/7445743658295443466

/   前言   /

在 Gradle 中,文件操作是 Gradle 构建的基础,几乎所有的操作都离不开文件。这篇文件就将对 Gradle 的文件 api 进行详细的介绍。当我们熟悉 Gradle 的文件 api 时,再去理解 Gradle 构建,就可以达到事半功倍的效果。

/   正文   /

文件的增删改查

如何创建文件和目录

Gradle 完全支持 java 语言,因此你可以使用 Java 的相关 api 来创建文件或者目录。比如说使用 Files.createDirectories 来创建目录。除此之外,我们也可以使用 Project 的 mkdir 方法来创建。代码示例如下:

// gradle 推荐使用 file() 方法来获取 File 对象,不是直接 new File 的方式
// 下面的 F:/work/Directories/a/b 是绝对路径,file 也支持相对路径,比如 
// file("Directories/a/b") ,表示相对于在根目录下的位置
val file = file("F:/work/Directories/a/b")

// 使用 Java 的 Files api 创建目录
Files.createDirectories(file.toPath())
// 使用 createNewFile() 创建文件
file("F:/work/ab/a.txt").createNewFile()

// 使用 project 的 mkdir 创建目录
mkdir("F:/work/ab")

如何删除文件和目录

和上面的创建文件和目录的 api 类似。我们可以使用 java 的原生方法删除文件;也可以使用 Project 的 delete 方法删除文件,代码示例如下:

// 使用原生的方法删除目录
val file = file("F:/work/Directories/a/b")
file.delete()
// 使用 project 方法删除文件
val file1 = file("F:/work/ab/a.txt")
delete(file1.toPath())

除此之外,Gradle 还提供了 Delete 删除任务,代码示例如下:

tasks.register<Delete>("myClean") {
    delete(buildDir)
}

// 除此之外,删除任务还可以批量删除指定的文件和目录,这个涉及到文件集合相关的东西,文章后面再详细介绍
tasks.register<Delete>("cleanTempFiles") {
    delete(fileTree("src").matching {
        include("**/*.tmp")
    })
}

移动或者重命名文件和目录

在 Gradle 中,移动文件位置或者重命名文件都可以使用 java 的 renameTo 来实现。除此之外我们也可以使用 Copy 复制任务实现相同的效果,由于和 Copy 复制任务比较复杂,文章后面在介绍。

val sourceFile = file("F:/work/ab.txt")
val targetFile = file("F:/work/ab/a.txt")
sourceFile.renameTo(targetFile)

获取对应的文件和目录对象

上面提到过,我们可以使用 file 方法,通过传入绝对路径或者相对路径的方式,来获取对应的文件(File)对象。除此之外,Gradle 还提供了 ProjectLayout 这个类,来提供项目中几个重要位置的访问能力。

// 获取当前项目的重要文件位置路径
// 获取项目下 generated 目录的路径,即使这个目录不存在也会有结果,比如
// F:\work\app\generated
project.layout.projectDirectory.dir("generated")
project.layout.projectDirectory.file("test.txt")
// 获取项目中 build 目录下的 generated 目录的路径,比如
// F:\work\app\build\generated
project.layout.buildDirectory.dir("generated")
project.layout.buildDirectory.file("test.txt")

需要注意的是 ProjectLayout 会返回一个 Provider 对象,这个类似于 kotlin 中的 by lazy,提供延时访问的能力。这是因为构建流程中,大部分文件都是后面生成的,因此需要延时访问。

如何读取/写入文件和目录

如果你需要对文件进行读写,直接使用 java/kotlin 原生的 api 方法就可以了,Gradle 暂时没有提供相应的方法或者任务。

文件集合

由于构建涉及到大量的文件和目录,因此 Gradle 对文件集合提供丰富的 api 支持。在 Gradle 中,文件集合分成两种:FileCollection 和 FileTree。它们的区别是,FileCollection 没有文件目录之间的层次结构,而 FileTree 有。如下图所示,使用 Gradle 提供的 Copy 任务,复制 FileCollection 和 FileTree 的区别如下:


FileCollection 的常用 api

// 创建 FileCollection,支持各种文件格式输入,非常方便
val collection: FileCollection = project.layout.files(
    "src",
    "src/file1.txt",
    File("src/file2.txt"),
    listOf("src/file3.csv""src/file4.csv"),
    Paths.get("src""file5.txt")
)

// FileCollection 本质是集合,支持各种集合操作
// 遍历
collection.forEach {
    if (it.isDirectory) {
        it.createDirectory()
    } else {
        it.createNewFile()
    }
}
// 过滤
val textFiles: FileCollection = collection.filter { f: File ->
    f.name.endsWith(".txt")
}
// 添加和删除集合
val union = collection + project.layout.files("src/file2.txt")
val difference = collection - project.layout.files("src/file2.txt")

FileTree

// 使用 project.fileTree 来创建 ConfigurableFileTree 对象,它是 FileTree 的实现类,增加了正则表达式过滤的功能
// 直接创建
var tree: ConfigurableFileTree = project.fileTree("src/main").apply {
    // 设置过滤条件
    include("**/*.java")
    exclude("**/Abstract*")
}
// 通过 closure 创建
tree = fileTree("src") {
    include("**/*.java")
}
// 通过 map 创建
tree = project.fileTree("dir" to "src""include" to "**/*.java""exclude" to "**/*test*/**")
`kotlin

// 普通的遍历
tree.forEach{ fileFile ->
    println(file)
}
// 结构化的遍历方式,采用深度优先前缀顺序遍历文件和目录
tree.visit {
    println("${this.relativePath} => ${this.file}")
}
// 过滤
val filtered: FileTree = tree.matching {
    include("org/gradle/api/**")
}

// 添加 fileTree
val sum: FileTree = tree + fileTree("src/test")

复制任务

复制/重命名/移动文件

// 复制文件,from 设置复制的来源,into 设置复制的目的
tasks.register<Copy>("copyReport") {  
    from(layout.buildDirectory.file("reports/my-report.pdf"))  
    into(layout.buildDirectory.dir("toArchive"))  
}

// 重命名文件,在 rename 返回重命名后的名字
tasks.register<Copy>("copyReport") {
    from(layout.buildDirectory.file("reports/my-report.txt"))
    into(layout.buildDirectory.dir("toArchive"))
    rename { oldName ->
        "xxx.txt"
    }
}

// 移动文件位置
tasks.register('moveFile') {
    doLast {
        // 复制文件
        copy {
            from 'src/file.txt'
            into 'destination/dir'
        }
        // 删除源文件
        delete 'src/file.txt'
    }
}

子规范

有的时候,我们希望只对部分目录做一些特殊的操作。比如说只处理指定目录下的 html、png 文件。这时候就可以使用 Copy 任务的子规范。代码示例如下:

tasks.register<Copy>("nestedSpecs") {
    into(layout.buildDirectory.dir("explodedWar"))
    exclude("**/*staging*")
    from("src/dist") {
        // 子规范
        include("**/*.html""**/*.png""**/*.jpg")
    }
    from(sourceSets.main.get().output) {
        // 子规范
        into("WEB-INF/classes")
    }
    into("WEB-INF/lib") {
        // 子规范
        from(configurations.runtimeClasspath)
    }
}

CopySpec

如果我们想要让不同的任务使用相同的规范,我们可以使用 CopySpec 来实现,代码示例如下:

val webAssetsSpec: CopySpec = copySpec {
    from("src/main/webapp")
    include("**/*.html""**/*.png""**/*.jpg")
    rename("(.+)-staging(.+)""$1$2")
}

tasks.register<Copy>("copyAssets") {
    into(layout.buildDirectory.dir("inPlaceApp"))
    with(webAssetsSpec)
}

Sync 任务

Sync 任务扩展了 Copy 任务,它在复制源文件到目标目录后,会删除目标目录中未被复制的文件。一般用于安装应用、创建归档的展开副本或维护项目依赖的副本等。代码示例如下:

tasks.register("libs") {
    from(configurations["runtime"])
    into(layout.buildDirectory.dir("libs"))
}

文件归档(archives)

在 Gradle 中,打包是最常见的操作,因此 Gradle 提供了 Zip、Tar、Jar等任务来简化流程。在 Gradle 眼中,将文件打包成 Zip、Tar 或 Jar 等格式实际上是一次 archives (归档)的过程,只是格式不同而已。因此我们只需要了解 Zip 任务即可。代码示例如下:

tasks.register("packageDistribution") {
    archiveFileName = "my-distribution.zip"
    destinationDirectory = layout.buildDirectory.dir("dist")
    from(layout.buildDirectory.dir("toArchive"))
}

文件的解压缩代码示例如下:

tasks.register<Copy>("unpackFiles") {
    from(zipTree("src/resources/thirdPartyResources.zip"))
    into(layout.buildDirectory.dir("resources"))
}

推荐阅读:
我的新书,《第一行代码 第3版》已出版!
高效稳定 | Compose 的类型安全导航
原创:写给初学者的Jetpack Compose教程,edge-to-edge全面屏体验

欢迎关注我的公众号
学习技术或投稿


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