Android开发之玩转 Gradle,你可不能不熟悉 Transform

本文解释了Gradle中的Task概念,强调其在项目构建中的作用,特别是Android项目中如编译、打包等任务。同时探讨了Transform在AppExtension中的注册和执行顺序,以及Transform与Task之间的关系。文章还提到了面试中关于Android高级工程师的知识考察点,包括Gradle、构建流程和源码理解。
摘要由CSDN通过智能技术生成

什么是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任务,举个例子比如说aaptjavackotlinc等等,他们都是作为一个task存在的。

AGP中的Transform

AppExtension appExtension = project.getExtensions().getByType(AppExtension.class);

appExtension.registerTransform(new DoubleTabTransform(project));

当我们在编写一个Transformplugin的时候,其实是对安卓的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学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值