什么是Task
其实核心还是要先从什么是Task讲起了。
一个Task代表一个构建工作的原子操作,例如编译calsses或者生成javadoc。
Gradle中,每一个待编译的工程都叫一个Project。每一个Project在构建的时候都包含一系列的Task。比如一个Android APK的编译可能包含:Java源码编译Task、资源编译Task、JNI编译Task、lint检查Task、打包生成APK的Task、签名Task等。插件本身就是包含了若干Task的。
简单的说我们的项目编译以assembleDebug
为例子,会顺序执行非常多的gradle task
任务,举个例子比如说aapt
,javac
,kotlinc
等等,他们都是作为一个task
存在的。
AGP中的Transform
AppExtension appExtension = project.getExtensions().getByType(AppExtension.class);
appExtension.registerTransform(new DoubleTabTransform(project));
当我们在编写一个Transform
的plugin
的时候,其实是对安卓的AppExtension
进行了一个注册Transform
的操作而已,那么Transform
的本质到底是什么呢?
高能预警,下面的源代码比较长,可以考虑直接跳过看结论,但是看得懂的同学最好可以学习下。
public class LibraryTaskManager extends TaskManager {
@Override
public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
…
// ----- External Transforms -----
// apply all the external transforms.
List customTransforms = extension.getTransforms();
List<List> customTransformsDependencies = extension.getTransformsDependencies();
for (int i = 0, count = customTransforms.size(); i < count; i++) {
Transform transform = customTransforms.get(i);
// Check the transform only applies to supported scopes for libraries:
// We cannot transform scopes that are not packaged in the library
// itself.
Sets.SetView<? super Scope> difference =
Sets.difference(transform.getScopes(), TransformManager.PROJECT_ONLY);
if (!difference.isEmpty()) {
String scopes = difference.toString();
globalScope
.getAndroidBuilder()
.getIssueReporter()
.reportError(
Type.GENERIC,
new EvalIssueException(
String.format(
“Transforms with scopes ‘%s’ cannot be applied to library projects.”,
scopes)));
}
List deps = customTransformsDependencies.get(i);
transformManager.addTransform(
taskFactory,
variantScope,
transform,
null,
task -> {
if (!deps.isEmpty()) {
task.dependsOn(deps);
}
},
taskProvider -> {
// if the task is a no-op then we make assemble task
// depend on it.
if (transform.getScopes().isEmpty()) {
TaskFactoryUtils.dependsOn(
variantScope.getTaskContainer().getAssembleTask(),
taskProvider);
}
});
}
// Now add transforms for intermediate publishing (projects to projects).
File jarOutputFolder = variantScope.getIntermediateJarOutputFolder();
File mainClassJar = new File(jarOutputFolder, FN_CLASSES_JAR);
File mainResJar = new File(jarOutputFolder, FN_INTERMEDIATE_RES_JAR);
LibraryIntermediateJarsTransform intermediateTransform =
new LibraryIntermediateJarsTransform(
mainClassJar,
mainResJar,
variantConfig::getPackageFromManifest,
extension.getPackageBuildConfig());
excludeDataBindingClassesIfNecessary(variantScope, intermediateTransform);
BuildArtifactsHolder artifacts = variantScope.getArtifacts();
transformManager.addTransform(
taskFactory,
variantScope,
intermediateTransform,
taskName -> {
// publish the intermediate classes.jar
artifacts.appendArtifact(
InternalArtifactType.LIBRARY_CLASSES,
ImmutableList.of(mainClassJar),
taskName);
// publish the res jar
artifacts.appendArtifact(
InternalArtifactType.LIBRARY_JAVA_RES,
ImmutableList.of(mainResJar),
taskName);
},
null,
null);
taskFactory.register(new LibraryDexingTask.CreationAction(variantScope));
// Create a jar with both classes and java resources. This artifact is not
// used by the Android application plugin and the task usually don’t need to
// be executed. The artifact is useful for other Gradle users who needs the
// ‘jar’ artifact as API dependency.
taskFactory.register(new ZipMergingTask.CreationAction(variantScope));
// now add a transform that will take all the native libs and package
// them into an intermediary folder. This processes only the PROJECT
// scope.
final File intermediateJniLibsFolder = new File(jarOutputFolder, FD_JNI);
LibraryJniLibsTransform intermediateJniTransform =
new LibraryJniLibsTransform(
“intermediateJniLibs”,
intermediateJniLibsFolder,
TransformManager.PROJECT_ONLY);
transformManager.addTransform(
taskFactory,
variantScope,
intermediateJniTransform,
taskName -> {
// publish the jni folder as intermediate
variantScope
.getArtifacts()
.appendArtifact(
InternalArtifactType.LIBRARY_JNI,
ImmutableList.of(intermediateJniLibsFolder),
taskName);
},
null,
null);
// Now go back to fill the pipeline with transforms used when
// publishing the AAR
// first merge the resources. This takes the PROJECT and LOCAL_DEPS
// and merges them together.
createMergeJavaResTransform(variantScope);
// ----- Minify next -----
maybeCreateJavaCodeShrinkerTransform(variantScope);
maybeCreateResourcesShrinkerTransform(variantScope);
// now add a transform that will take all the class/res and package them
// into the main and secondary jar files that goes in the AAR.
// This transform technically does not use its transform output, but that’s
// ok. We use the transform mechanism to get incremental data from
// the streams.
// This is used for building the AAR.
File classesJar = variantScope.getAarClassesJar();
File libsDirectory = variantScope.getAarLibsDirectory();
LibraryAarJarsTransform transform =
new LibraryAarJarsTransform(
classesJar,
libsDirectory,
artifacts.hasArtifact(InternalArtifactType.ANNOTATIONS_TYPEDEF_FILE)
? artifacts.getFinalArtifactFiles(
-
InternalArtifactType.ANNOTATIONS_TYPEDEF_FILE)
- null,
variantConfig::getPackageFromManifest,
extension.getPackageBuildConfig());
excludeDataBindingClassesIfNecessary(variantScope, transform);
transformManager.addTransform(
taskFactory,
variantScope,
transform,
taskName -> {
variantScope
.getArtifacts()
.appendArtifact(
InternalArtifactType.AAR_MAIN_JAR,
ImmutableList.of(classesJar),
taskName);
variantScope
.getArtifacts()
.appendArtifact(
InternalArtifactType.AAR_LIBS_DIRECTORY,
ImmutableList.of(libsDirectory),
taskName);
},
null,
null);
// now add a transform that will take all the native libs and package
// them into the libs folder of the bundle. This processes both the PROJECT
// and the LOCAL_PROJECT scopes
final File jniLibsFolder =
variantScope.getIntermediateDir(InternalArtifactType.LIBRARY_AND_LOCAL_JARS_JNI);
LibraryJniLibsTransform jniTransform =
new LibraryJniLibsTransform(
“syncJniLibs”,
jniLibsFolder,
TransformManager.SCOPE_FULL_LIBRARY_WITH_LOCAL_JARS);
transformManager.addTransform(
taskFactory,
variantScope,
jniTransform,
taskName ->
variantScope
.getArtifacts()
.appendArtifact(
InternalArtifactType.LIBRARY_AND_LOCAL_JARS_JNI,
ImmutableList.of(jniLibsFolder),
taskName),
null,
null);
createLintTasks(variantScope);
createBundleTask(variantScope);
}
}
自定义Transform和其他系统Transform执行的顺序
而且上述方法我们可以看出,任务还是会根据DAG
(有向无环图)生成Task
,其中会包含一些系统的Transform
,其顺序有可能会被插入到自定义的Transform
之前,而有一些则会被放置在所有的Tranform
执行之后。比如LibraryJniLibsTransform
。
Transform和Task的关系
从这部分源代码其实我们就可以看出,我们注册到AppExtension
里面的Transform
,最后会createTasksForVariantScope
方法调用到。
总结
这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码
因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
并且运行,也是很考察面试者写代码
因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-wKVp8s9N-1714542690991)]
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!