public abstract class Transform { /** * 用于指明 Transform 的名字,也对应了该 Transform 所代表的 Task 名称 */ @NonNull public abstract String getName(); /** * Whether this transform should be applied to a given variant. * * @since 3.4 * @return true if the transform should be applied to a given variant, false otherwise. * @param variant information about the current variant. */ @Incubating public boolean applyToVariant(@NonNull VariantInfo variant) { return true; } /** * 用于指明 Transform 的输入类型,可以作为输入过滤的手段 * // 代表 javac 编译成的 class 文件,常用 public static final Set<ContentType> CONTENT_CLASS; public static final Set<ContentType> CONTENT_JARS; // 这里的 resources 单指 java 的资源 public static final Set<ContentType> CONTENT_RESOURCES; public static final Set<ContentType> CONTENT_NATIVE_LIBS; public static final Set<ContentType> CONTENT_DEX; public static final Set<ContentType> CONTENT_DEX_WITH_RESOURCES; public static final Set<ContentType> DATA_BINDING_BASE_CLASS_LOG_ARTIFACT; */ @NonNull public abstract Set<ContentType> getInputTypes(); /** * Returns the type(s) of data that is generated by the Transform. This may be more than * one type. * * <p>The default implementation returns {@link #getInputTypes()}. * * <p><strong>This must be of type {@link QualifiedContent.DefaultContentType}</strong> */ @NonNull public Set<ContentType> getOutputTypes() { return getInputTypes(); } /** * 用于指明 Transform 的作用域 public static final Set<Scope> SCOPE_FULL_PROJECT; // 常用,代表所有 Project 确定了ContentType和Scope后就确定了该自定义Transform需要处理的资源流。 比如CONTENT_CLASS和SCOPE_FULL_PROJECT表示了所有项目中java编译成的class组成的资源流。 */ @NonNull public abstract Set<? super Scope> getScopes(); /** * Returns the referenced scope(s) for the Transform. These scopes are not consumed by * the Transform. They are provided as inputs, but are still available as inputs for * other Transforms to consume. * * <p>The default implementation returns an empty Set. */ @NonNull public Set<? super Scope> getReferencedScopes() { return ImmutableSet.of(); } /** * Returns a list of additional file(s) that this Transform needs to run. Preferably, use * {@link #getSecondaryFiles()} API which allow eah secondary file to indicate if changes * can be handled incrementally or not. This API will treat all additional file change as * a non incremental event. * * <p>Changes to files returned in this list will trigger a new execution of the Transform * even if the qualified-content inputs haven't been touched. * * <p>Any changes to these files will trigger a non incremental execution. * * <p>The default implementation returns an empty collection. * * @deprecated replaced by {@link #getSecondaryFiles()} */ @Deprecated @NonNull public Collection<File> getSecondaryFileInputs() { return ImmutableList.of(); } /** * Returns a list of additional file(s) that this Transform needs to run. * * <p>Changes to files returned in this list will trigger a new execution of the Transform * even if the qualified-content inputs haven't been touched. * * <p>Each secondary input has the ability to be declared as necessitating a non incremental * execution in case of change. This Transform can therefore declare which secondary file * changes it supports in incremental mode. * * <p>The default implementation returns an empty collection. */ @NonNull public Collection<SecondaryFile> getSecondaryFiles() { return ImmutableList.of(); } /** * Returns a list of additional (out of streams) file(s) that this Transform creates. * * <p>These File instances can only represent files, not directories. For directories, use * {@link #getSecondaryDirectoryOutputs()} * * * <p>Changes to files returned in this list will trigger a new execution of the Transform * even if the qualified-content inputs haven't been touched. * * <p>Changes to these output files force a non incremental execution. * * <p>The default implementation returns an empty collection. */ @NonNull public Collection<File> getSecondaryFileOutputs() { return ImmutableList.of(); } /** * Returns a list of additional (out of streams) directory(ies) that this Transform creates. * * <p>These File instances can only represent directories. For files, use * {@link #getSecondaryFileOutputs()} * * <p>Changes to directories returned in this list will trigger a new execution of the Transform * even if the qualified-content inputs haven't been touched. * * <p>Changes to these output directories force a non incremental execution. * * <p>The default implementation returns an empty collection. */ @NonNull public Collection<File> getSecondaryDirectoryOutputs() { return ImmutableList.of(); } /** * Returns a map of non-file input parameters using a unique identifier as the map key. * * <p>Changes to values returned in this map will trigger a new execution of the Transform * even if the content inputs haven't been touched. * * <p>Changes to these values force a non incremental execution. * * <p>The default implementation returns an empty Map. */ @NonNull public Map<String, Object> getParameterInputs() { return ImmutableMap.of(); } /** * 指明该 Transform 是否支持增量编译。需要注意的是,即使返回了 true ,在某些情况下运行时,它还是会返回 false 的。 */ public abstract boolean isIncremental(); /** * For Transforms that produce an output available though the [BuildArtifactsHolder] provider * based interfaces should indicate the output directory of the produced artifact * * @param directory */ public void setOutputDirectory(@NonNull Property<Directory> directory) {} public void setOutputFile(@NonNull Property<RegularFile> file) {} /** * @deprecated replaced by {@link #transform(TransformInvocation)}. */ @Deprecated @SuppressWarnings("UnusedParameters") public void transform( @NonNull Context context, @NonNull Collection<TransformInput> inputs, @NonNull Collection<TransformInput> referencedInputs, @Nullable TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException { } /** * 其中,inputs 是该 Transform 要消费的输入流,有两种格式:jar 和目录格式;referenced Inputs 集合仅供参考,不应进行转换,它是受 getReferencedScopes 方法控制的; outputProvider 是用来获取输出目录的,我们要将操作后的文件复制到输出目录中。 */ public void transform(@NonNull TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { // Just delegate to old method, for code that uses the old API. //noinspection deprecation transform(transformInvocation.getContext(), transformInvocation.getInputs(), transformInvocation.getReferencedInputs(), transformInvocation.getOutputProvider(), transformInvocation.isIncremental()); } /** * Returns if this transform's outputs should be cached. Please read {@link * org.gradle.api.tasks.CacheableTask} Javadoc if you would like to make your transform * cacheable. */ public boolean isCacheable() { return false; }}
public interface TransformInvocation { // 上下文 @NonNull Context getContext(); // transform 的输入/输出 @NonNull Collection<TransformInput> getInputs(); // 返回不被这个 transformation 消费的 input @NonNull Collection<TransformInput> getReferencedInputs(); /** * Returns the list of secondary file changes since last. Only secondary files that this * transform can handle incrementally will be part of this change set. */ @NonNull Collection<SecondaryInput> getSecondaryInputs(); // 返回允许创建内容的 output provider @Nullable TransformOutputProvider getOutputProvider(); boolean isIncremental();}
public interface TransformOutputProvider { void deleteAll() throws IOException; // 根据 name、ContentType、QualifiedContent.Scope 返回对应的文件(jar / directory) @NonNull File getContentLocation( @NonNull String name, @NonNull Set<QualifiedContent.ContentType> types, @NonNull Set<? super QualifiedContent.Scope> scopes, @NonNull Format format);}