Gradle任务标记“not cacheable”是因为其输出无法安全复用,根源在于输入/输出声明不全、含非确定性操作、未显式启用缓存或依赖不可缓存任务。

Gradle任务显示“not cacheable”是怎么回事
任务被标记为 not cacheable,不是 Gradle 拒绝缓存,而是它明确判断该任务**无法安全复用输出**——哪怕输入完全一致,执行结果也可能不同。这是 Gradle 输出缓存(output caching)的严格保护机制,不是 bug,是设计使然。
哪些任务默认不可缓存?常见触发条件
Gradle 会自动将满足以下任一条件的任务标记为 not cacheable:
-
@Input或@OutputDirectory等注解缺失或标注不全,导致 Gradle 无法完整捕获输入/输出边界 - 任务逻辑中调用了非确定性操作,比如
new Date()、Math.random()、读取系统时间或未声明的外部文件 - 任务继承自
DefaultTask但没显式启用缓存:outputs.cacheIf { true }未设置,或cacheIf返回false - 使用了
doLast直接写文件但未声明outputs.files,Gradle 不知道哪些是产出物 - 任务依赖了
project.layout.buildDirectory以外的动态路径(如硬编码"./tmp"),路径不可预测
如何检查一个任务是否真的可缓存
别只看面板或日志里的“not cacheable”,要验证真实状态:
- 运行
./gradlew help --task <taskname></taskname>,查看输出中Cacheable字段值是true还是false - 加参数强制诊断:
./gradlew <taskname> --info</taskname>,搜索日志里Skipping task ':xxx' because it is not up-to-date或is not cacheable的具体原因 - 在任务定义中加一行:
logger.lifecycle("Cache status: ${outputs.cacheIf { true }}"),确认缓存策略是否生效
注意:org.gradle.caching=true 只启用构建缓存总开关,不改变单个任务的可缓存性判断逻辑。
自定义任务怎么正确支持缓存
如果你写了自己的 CustomBuildTask,必须同时满足三件事才算真正可缓存:
- 声明所有输入:
@Input(简单值)、@InputFiles(文件集合)、@InputDirectory(目录) - 声明所有输出:
@OutputDirectory或outputs.dir(...),且确保任务只往这些路径写 - 避免副作用:不读取未声明的文件、不调用随机/时间相关 API、不修改全局状态
示例关键片段:
class CustomBuildTask extends DefaultTask {
@InputFiles
FileCollection srcFiles
@OutputDirectory
File outputDir
@TaskAction
void build() {
// 只处理 srcFiles,只写入 outputDir
project.copy {
from srcFiles
into outputDir
}
}
}漏掉 @InputFiles 或把 outputDir 写成 file("./build/custom")(未用 project.layout.buildDirectory)都会让它变成 not cacheable。
最常被忽略的是:即使任务逻辑干净,只要它的任意一个上游任务(mustRunAfter / dependsOn)是 not cacheable,当前任务也会被连带标记为不可缓存——得逐层查依赖链。


















