数据流操作支持是孵化中的功能,可能会有所更改。 |
在 Gradle 构建中执行工作的首选方式是使用任务。然而,某些类型的工作不太适合任务,例如自定义处理构建失败。
如果你想在构建成功时播放欢快的音乐,在构建失败时播放悲伤的音乐怎么办?这项工作需要处理任务执行结果,因此它本身不能是一个任务。
数据流操作 API 提供了一种调度此类工作的方式。数据流操作是一个参数化的独立工作单元,一旦所有输入参数可用,它就符合执行条件。
实现数据流操作
第一步是实现操作本身。你必须创建一个实现 FlowAction 接口的类
import org.gradle.api.flow.FlowAction
import org.gradle.api.flow.FlowParameters
abstract class ReportConsumption : FlowAction<ReportConsumption.Params> {
interface Params : FlowParameters {
}
override fun execute(parameters: Params) {
}
}
必须实现 execute
方法,因为工作发生在此处。操作实现被视为自定义 Gradle 类型,并且可以使用自定义 Gradle 类型可用的任何功能。特别是,一些 Gradle 服务可以注入到实现中。
数据流操作可以接受参数。为了提供参数,你定义一个抽象类(或接口)来保存参数
-
参数类型必须实现(或扩展)FlowParameters。
-
参数类型也是一个自定义 Gradle 类型。
-
操作实现通过
execute
方法的参数获取参数。
当操作不需要参数时,你可以使用 FlowParameters.None 作为参数类型。
这是一个数据流操作的示例,它将共享构建服务和文件路径作为参数
package org.gradle.sample.sound;
import org.gradle.api.flow.FlowAction;
import org.gradle.api.flow.FlowParameters;
import org.gradle.api.provider.Property;
import org.gradle.api.services.ServiceReference;
import org.gradle.api.tasks.Input;
import java.io.File;
public abstract class SoundPlay implements FlowAction<SoundPlay.Parameters> {
interface Parameters extends FlowParameters {
@ServiceReference (1)
Property<SoundService> getSoundService();
@Input (2)
Property<File> getMediaFile();
}
@Override
public void execute(Parameters parameters) {
parameters.getSoundService().get().playSoundFile(parameters.getMediaFile().get());
}
}
1 | 参数类型中的参数必须进行注解。如果参数用 @ServiceReference 注解,那么在创建操作时,根据常用规则,会自动为该参数分配一个合适的共享构建服务实现。 |
2 | 所有其他参数都必须用 @Input 注解。 |
使用生命周期事件提供者
除了常用的值提供者之外,Gradle 还为构建生命周期事件(如构建完成)提供了专用提供者。这些提供者旨在用于数据流操作,并在用作输入时提供额外的排序保证。如果你通过调用 map
或 flatMap
等从事件提供者派生提供者,排序也适用。你可以从 FlowProviders 类获取这些提供者。
flowProviders.buildWorkResult.map {
[
buildInvocationId: scopeIdsService.buildInvocationId,
workspaceId: scopeIdsService.workspaceId,
userId: scopeIdsService.userId
]
}
如果你没有将生命周期事件提供者用作数据流操作的输入,那么操作执行的确切时间未定义,并且可能会在 Gradle 的下一个版本中更改。 |
提供操作以供执行
你不应手动创建 FlowAction
对象。相反,你可以在 FlowScope
的适当作用域内请求执行它们。通过这样做,你可以为任务配置参数
package org.gradle.sample.sound;
import org.gradle.api.Plugin;
import org.gradle.api.flow.FlowProviders;
import org.gradle.api.flow.FlowScope;
import org.gradle.api.initialization.Settings;
import javax.inject.Inject;
import java.io.File;
public abstract class SoundFeedbackPlugin implements Plugin<Settings> {
@Inject
protected abstract FlowScope getFlowScope(); (1)
@Inject
protected abstract FlowProviders getFlowProviders(); (1)
@Override
public void apply(Settings settings) {
final File soundsDir = new File(settings.getSettingsDir(), "sounds");
getFlowScope().always( (2)
SoundPlay.class, (3)
spec -> (4)
spec.getParameters().getMediaFile().set(
getFlowProviders().getBuildWorkResult().map(result -> (5)
new File(
soundsDir,
result.getFailure().isPresent() ? "sad-trombone.mp3" : "tada.mp3"
)
)
)
);
}
}
1 | 使用服务注入获取 FlowScope 和 FlowProviders 实例。它们适用于项目和设置插件。 |
2 | 使用适当的作用域来运行你的操作。顾名思义,always 作用域中的操作在每次构建运行时都会执行。 |
3 | 指定实现操作的类。 |
4 | 使用 spec 参数配置操作参数。 |
5 | 生命周期事件提供者可以映射到其他事物,同时保留操作顺序。 |
因此,当你运行构建并成功完成时,该操作将播放“tada”声音。如果构建在配置或执行时失败,你将听到“悲伤长号”的声音——假设构建配置进行到足以注册操作的程度。