android gradle插件源码,Android Gradle Plugin 源码解析(下)

AAffA0nNPuCLAAAAAElFTkSuQmCC

上半部分请看上一篇文章。

下面是下半部分:

Application 的编译任务

我们继续查看createTasksForVariantData的最后一行,taskManager.createTasksForVariantData,发现 createTasksForVariantData 是抽象方法,这里的 taskManager 具体实现是 ApplicationTaskManager,查看 ApplicationTaskManager 的 createTasksForVariantData 方法/**

* Creates the tasks for a given BaseVariantData.

*/

@Override

public void createTasksForVariantData(

@NonNull final TaskFactory tasks,

@NonNull final BaseVariantData extends BaseVariantOutputData> variantData) { assert variantData instanceof ApplicationVariantData; final VariantScope variantScope = variantData.getScope(); //create sourceGenTask, resGenTask, assetGenTask

createAnchorTasks(tasks, variantScope);

createCheckManifestTask(tasks, variantScope);

handleMicroApp(tasks, variantScope); // Create all current streams (dependencies mostly at this point)

createDependencyStreams(tasks, variantScope); // Add a task to process the manifest(s)

// Add a task to create the res values

// Add a task to compile renderscript files.

// Add a task to merge the resource folders

// Add a task to merge the asset folders

// Add a task to create the BuildConfig class

// Add a task to process the Android Resources and generate source files

// Add a task to process the java resources

// Add a task to process this aidl file

// Add a task to process shader source

// Add NDK tasks

// Add external native build tasks

// Add a task to merge the jni libs folders

// Add a compile task

// Add data binding tasks if enabled

// create packaging task

// create the lint tasks.

...

}

代码实在太长了,我只留下了每段代码的注释,注释也已经非常清楚了,这个主要就是生成 variantData 的一系列像 compileXXX、generateXXX、processXXX、mergeXXX的任务,这一系列 task 就是构建一个可运行的完整APK的所需的所有task。下面介绍在编译dex中的过程,涉及的几个task。

Dex的编译过程// Add a compile task

recorder.record(

ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK,

project.getPath(),

variantScope.getFullVariantName(),

() -> {

CoreJackOptions jackOptions =

variantData.getVariantConfiguration().getJackOptions(); // create data binding merge task before the javac task so that it can

// parse jars before any consumer

createDataBindingMergeArtifactsTaskIfNecessary(tasks, variantScope);

AndroidTask extends JavaCompile> javacTask = // 创建 javac 任务

createJavacTask(tasks, variantScope); if (jackOptions.isEnabled()) {

AndroidTask jackTask =

createJackTask(tasks, variantScope, true /*compileJavaSource*/);

setJavaCompilerTask(jackTask, tasks, variantScope);

} else {

...

addJavacClassesStream(variantScope);

setJavaCompilerTask(javacTask, tasks, variantScope);

getAndroidTasks()

.create(

tasks, // 创建 AndroidJarTask ,生成classes.jar

new AndroidJarTask.JarClassesConfigAction(variantScope));

createPostCompilationTasks(tasks, variantScope);

}

});

我们直接查看 Add a compile task 注释下的代码,在执行 createPostCompilationTasks 之前,先创建了 javac 任务,任务名称为 compileXXXJavaWithJavac ,该任务是将 java 源文件编译成 class 文件,具体实现是在 JavaCompileConfigAction 类中。创建 javac 任务之后,接着创建了 AndroidJarTask 任务,该任务是将 class 文件整合输出 jar 包,具体实现就是在 AndroidJarTask 类中。

紧接着我们来看一下 createPostCompilationTasks 的方法/**

* Creates the post-compilation tasks for the given Variant.

*

* These tasks create the dex file from the .class files, plus optional intermediary steps like

* proguard and jacoco

*

*/

public void createPostCompilationTasks(

@NonNull TaskFactory tasks,

@NonNull final VariantScope variantScope) {

checkNotNull(variantScope.getJavacTask());

variantScope.getInstantRunBuildContext().setInstantRunMode(

getIncrementalMode(variantScope.getVariantConfiguration()) != IncrementalMode.NONE); final BaseVariantData extends BaseVariantOutputData> variantData = variantScope.getVariantData(); final GradleVariantConfiguration config = variantData.getVariantConfiguration();

TransformManager transformManager = variantScope.getTransformManager();

... boolean isMinifyEnabled = isMinifyEnabled(variantScope); boolean isMultiDexEnabled = config.isMultiDexEnabled(); // Switch to native multidex if possible when using instant run.

boolean isLegacyMultiDexMode = isLegacyMultidexMode(variantScope);

AndroidConfig extension = variantScope.getGlobalScope().getExtension(); // ----- External Transforms -----

// apply all the external transforms.

... // ----- Minify next -----

if (isMinifyEnabled) { boolean outputToJarFile = isMultiDexEnabled && isLegacyMultiDexMode; // 内部会判断是否使用 proguard 来创建 proguard 任务和 shrinkResources 任务

createMinifyTransform(tasks, variantScope, outputToJarFile);

} // ----- 10x support

... // ----- Multi-Dex support

Optional> multiDexClassListTask; // non Library test are running as native multi-dex

if (isMultiDexEnabled && isLegacyMultiDexMode) {

...

} else {

multiDexClassListTask = Optional.empty();

} // create dex transform

// 从 extension 中获取 dexOptions 项的配置

DefaultDexOptions dexOptions = DefaultDexOptions.copyOf(extension.getDexOptions());

... // 创建 DexTransform

DexTransform dexTransform = new DexTransform(

dexOptions,

config.getBuildType().isDebuggable(),

isMultiDexEnabled,

isMultiDexEnabled && isLegacyMultiDexMode ? variantScope.getMainDexListFile() : null,

variantScope.getPreDexOutputDir(),

variantScope.getGlobalScope().getAndroidBuilder(),

getLogger(),

variantScope.getInstantRunBuildContext(),

AndroidGradleOptions.getBuildCache(variantScope.getGlobalScope().getProject())); // 创建 dexTask

Optional> dexTask =

transformManager.addTransform(tasks, variantScope, dexTransform); // need to manually make dex task depend on MultiDexTransform since there's no stream

// consumption making this automatic

dexTask.ifPresent(t -> {

t.optionalDependsOn(tasks, multiDexClassListTask.orElse(null));

variantScope.addColdSwapBuildTask(t);

});

...

}

为了讲述主流程,我将一些 mutiDex 和 instantRun 判断的源码省略了,这里我们关注非mutiDex和非instantRun的情况。我们看到,如果我们设置了 minifyEnabled 为 true,那么这里就会去创建 createMinifyTransform ,如果use proguard,这里会创建 progruad 的任务和 shrinkResources 的任务。后面将创建 dexTask, 这个是 transfromTask 类型的任务,我们先来看下 transFromTask 类/**

* A task running a transform.

*/@ParallelizableTaskpublic class TransformTask extends StreamBasedTask implements Context { private Transform transform;

... public Transform getTransform() { return transform;

}

... @TaskAction

void transform(final IncrementalTaskInputs incrementalTaskInputs)

throws IOException, TransformException, InterruptedException {

...

recorder.record(

ExecutionType.TASK_TRANSFORM,

executionInfo,

getProject().getPath(),

getVariantName(), new Recorder.Block() { @Override

public Void call() throws Exception {

transform.transform( new TransformInvocationBuilder(TransformTask.this)

.addInputs(consumedInputs.getValue())

.addReferencedInputs(referencedInputs.getValue())

.addSecondaryInputs(changedSecondaryInputs.getValue())

.addOutputProvider(

outputStream != null

? outputStream.asOutput()

: null)

.setIncrementalMode(isIncremental.getValue())

.build()); return null;

}

});

}

}

我们知道,自定义任务中,在任务执行阶段会去执行被 @TaskAction 注解的方法,这里也就是执行 transfrom 方法,而 transfrom 方法中最后又会调用到 transform 的 transfrom 方法,在我们 dexTask 中传入的 transfrom 是DexTransfrom,那我们就去看下 DexTransfrom 的 transfrom 具体实现public class DexTransform extends Transform { @Override

public void transform(@NonNull TransformInvocation transformInvocation)

throws TransformException, IOException, InterruptedException {

... try { // if only one scope or no per-scope dexing, just do a single pass that

// runs dx on everything.

if ((jarInputs.size() + directoryInputs.size()) == 1

|| !dexOptions.getPreDexLibraries()) { // since there is only one dex file, we can merge all the scopes into the full

// application one.

File outputDir = outputProvider.getContentLocation("main",

getOutputTypes(),

TransformManager.SCOPE_FULL_PROJECT,

Format.DIRECTORY);

FileUtils.mkdirs(outputDir); // first delete the output folder where the final dex file(s) will be.

FileUtils.cleanOutputDir(outputDir); // gather the inputs. This mode is always non incremental, so just

// gather the top level folders/jars

final List inputFiles =

Stream.concat(

jarInputs.stream().map(JarInput::getFile),

directoryInputs.stream().map(DirectoryInput::getFile))

.collect(Collectors.toList()); // 通过 AndroidBuilder 转化为 byte

androidBuilder.convertByteCode(

inputFiles,

outputDir,

multiDex,

mainDexListFile,

dexOptions,

outputHandler); for (File file : Files.fileTreeTraverser().breadthFirstTraversal(outputDir)) { if (file.isFile()) {

instantRunBuildContext.addChangedFile(FileType.DEX, file);

}

}

} else {

...

}

最后执行到androidBuilder.convertByteCode/**

* Converts the bytecode to Dalvik format

* @param inputs the input files

* @param outDexFolder the location of the output folder

* @param dexOptions dex options

* @throws IOException

* @throws InterruptedException

* @throws ProcessException

*/

public void convertByteCode(

@NonNull Collection inputs,

@NonNull File outDexFolder, boolean multidex,

@Nullable File mainDexList,

@NonNull DexOptions dexOptions,

@NonNull ProcessOutputHandler processOutputHandler)

throws IOException, InterruptedException, ProcessException {checkNotNull(inputs, "inputs cannot be null.");

checkNotNull(outDexFolder, "outDexFolder cannot be null.");

checkNotNull(dexOptions, "dexOptions cannot be null.");

checkArgument(outDexFolder.isDirectory(), "outDexFolder must be a folder");

checkState(mTargetInfo != null, "Cannot call convertByteCode() before setTargetInfo() is called.");

ImmutableList.Builder verifiedInputs = ImmutableList.builder(); for (File input : inputs) { if (checkLibraryClassesJar(input)) {

verifiedInputs.add(input);

}

} //创建 DexProcessBuilder

DexProcessBuilder builder = new DexProcessBuilder(outDexFolder);

builder.setVerbose(mVerboseExec)

.setMultiDex(multidex)

.setMainDexList(mainDexList)

.addInputs(verifiedInputs.build());

runDexer(builder, dexOptions, processOutputHandler);

}

创建了 DexProcessBuilder ,随后执行到了 runDexer 方法中public void runDexer(

@NonNull final DexProcessBuilder builder,

@NonNull final DexOptions dexOptions,

@NonNull final ProcessOutputHandler processOutputHandler)

throws ProcessException, IOException, InterruptedException {

initDexExecutorService(dexOptions); if (dexOptions.getAdditionalParameters().contains("--no-optimize")) {

mLogger.warning(DefaultDexOptions.OPTIMIZE_WARNING);

} if (shouldDexInProcess(dexOptions)) {

dexInProcess(builder, dexOptions, processOutputHandler);

} else {

dexOutOfProcess(builder, dexOptions, processOutputHandler);

}

}

进入到dexInProcess方法private void dexInProcess(

@NonNull final DexProcessBuilder builder,

@NonNull final DexOptions dexOptions,

@NonNull final ProcessOutputHandler outputHandler)

throws IOException, ProcessException { final String submission = Joiner.on(',').join(builder.getInputs());

mLogger.verbose("Dexing in-process : %1$s", submission); try {

sDexExecutorService.submit(() -> {

Stopwatch stopwatch = Stopwatch.createStarted();

ProcessResult result = DexWrapper.run(builder, dexOptions, outputHandler);

result.assertNormalExitValue();

mLogger.verbose("Dexing %1$s took %2$s.", submission, stopwatch.toString()); return null;

}).get();

} catch (Exception e) { throw new ProcessException(e);

}

}/**

* Wrapper around the real dx classes.

*/public class DexWrapper { /**

* Runs the dex command.

*

* @return the integer return code of com.android.dx.command.dexer.Main.run()

*/

public static ProcessResult run(

@NonNull DexProcessBuilder processBuilder,

@NonNull DexOptions dexOptions,

@NonNull ProcessOutputHandler outputHandler) throws IOException, ProcessException {

ProcessOutput output = outputHandler.createOutput(); int res; try {

DxContext dxContext = new DxContext(output.getStandardOutput(), output.getErrorOutput()); // 构建 Main.Arguments 参数

Main.Arguments args = buildArguments(processBuilder, dexOptions, dxContext);

res = new Main(dxContext).run(args);

} finally {

output.close();

}

outputHandler.handleOutput(output); return new DexProcessResult(res);

}

...

}

buildArguments方法通过传入的DexProcessBuilder、dexOptions、dxContext构建 arguments,后面使用的args的参数fileNames,outName,jarOutput都是从DexProcessBuilder来的,然后执行Main的run方法package com.android.dx.command.dexer;

.../**

* Main class for the class file translator.

*/public class Main { /**

* Run and return a result code.

* @param arguments the data + parameters for the conversion

* @return 0 if success > 0 otherwise.

*/

public int run(Arguments arguments) throws IOException { // Reset the error count to start fresh.

errors.set(0); // empty the list, so that tools that load dx and keep it around

// for multiple runs don't reuse older buffers.

libraryDexBuffers.clear();

args = arguments;

args.makeOptionsObjects(context);

OutputStream humanOutRaw = null; if (args.humanOutName != null) {

humanOutRaw = openOutput(args.humanOutName);

humanOutWriter = new OutputStreamWriter(humanOutRaw);

} try { if (args.multiDex) { return runMultiDex();

} else { return runMonoDex();

}

} finally {

closeOutput(humanOutRaw);

}

}

}

这里我们关注非multiDex的情况,即执行了runMonoDex的方法private int runMonoDex() throws IOException {

... // 内部会创建dexFile,并填充class

if (!processAllFiles()) { return 1;

} if (args.incremental && !anyFilesProcessed) { return 0; // this was a no-op incremental build

} // this array is null if no classes were defined

byte[] outArray = null; if (!outputDex.isEmpty() || (args.humanOutName != null)) { // 内部通过Dex类toDex 方法将 class 文件转化dex byte[]

outArray = writeDex(outputDex); if (outArray == null) { return 2;

}

} if (args.incremental) {

outArray = mergeIncremental(outArray, incrementalOutFile);

}

outArray = mergeLibraryDexBuffers(outArray); if (args.jarOutput) { // Effectively free up the (often massive) DexFile memory.

outputDex = null; if (outArray != null) { // 输出的文件名为 classes.dex

outputResources.put(DexFormat.DEX_IN_JAR_NAME, outArray);

} if (!createJar(args.outName)) { return 3;

}

} else if (outArray != null && args.outName != null) {

OutputStream out = openOutput(args.outName);

out.write(outArray);

closeOutput(out);

} return 0;

}

上面的代码中,填充class以及dex流转换,内部流程较为复杂,就不再继续深入,简单做下总结:

1.通过执行 processAllFiles ,内部创建 DexFile 也就是outputDex,并且填充 class 文件

2.通过 writeDex 方法,将 outputDex 传入,方法内部执行的是 outputDex.toDex 方法,将 outputDex 内部填充的 class 转化为 dex 的 byte[] 返回

3.最后将 byte[] 数组创建 classes.dex 输出

作者:Android高级架构

链接:https://www.jianshu.com/p/1cf3decd1737

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值