文章目录
Task24 transformClassesWithDexBuilderForDebug
1.input/ouput
taskName:transformClassesWithDexBuilderForDebug
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/adapters-3.2.0.aar/49b3d7e4ab68d92f056ea8f56b33e9fb/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/library-3.2.0.aar/818ffb3fe5dc5eeb6b4e51c93615b7fb/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.databinding/baseLibrary/3.2.0/fb5f8492c36231104cd86feaefa723291504c0a6/baseLibrary-3.2.0.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/2774ea4f1cf1e83a6ad8e8d3c8b463b6/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f43c0ba95b6494825ed940fc4f04662b/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/9ac5f97e8ccb24c52b7cbb6202c12ad0/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/7e6a4ce6591d722d47aafc36d980f8b4/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/4c474caa9ac1f01c4936bd96905ecacd/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/559112320064089dfaf6780e71d5b44f/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/c2c3ad4abfd49316f6769b8238b0f010/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/53ab5ad72634f3497309a8788f3ca200/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/868eaa7e0c620cd85d72ad4f340e8bb1/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/4ec3c1c46e5bad9ac3b91f45a2afec3e/jars/classes.jar
input:/Users/apple/.gradle/caches/transforms-1/files-1.1/runtime-1.0.3.aar/5765c0929bc6bc40d70d6fc25f402f42/jars/classes.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.lifecycle/common/1.0.3/7d7f60c4783872861222166f6164215f8951c7b1/common-1.0.3.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/android.arch.core/common/1.0.0/a2d487452376193fc8c103dd2b9bd5f2b1b44563/common-1.0.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support/support-annotations/26.1.0/814258103cf26a15fcc26ecce35f5b7d24b73f8/support-annotations-26.1.0.jar
input:/Users/apple/.gradle/caches/modules-2/files-2.1/com.android.support.constraint/constraint-layout-solver/1.1.3/bde0667d7414c16ed62d3cfe993cff7f9d732373/constraint-layout-solver-1.1.3.jar
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/support/coreutils/R.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/support/coreutils/R$bool.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/cl/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/databinding/layouts/DataBindingInfo.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/android/databinding/DataBindingComponent.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/MainActivity.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$bool.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$id.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/IHelloAidlInterface$Stub.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/R$mipmap.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/DataBinderMapperImpl.class
input:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/gradle/task/demo/DataBinderMapperImpl$InnerBrLookup.class
省略...
=========================================================
output:/Users/apple/work/project/AndroidGradleTaskDemo/app/build/intermediates/transforms/dexBuilder/debug
从日志不难发现,输入数据格式包含二类**.jar和javac编译后.class**文件,该任务主要做了二件事情
- 将每个class文件通过D8编译器(AS3.0新增的D8默认开启)编译成dex文件
- 对于jar,则将其中的class编译成dex再打包成jar
关于D8功效果描述参加官方描述https://developer.android.com/studio/command-line/d8?hl=zh-cn
2. 核心类(DexArchiveBuilderTransform,TransformTask)
/**
* Transform that converts CLASS files to dex archives, {@link
* com.android.builder.dexing.DexArchive}. This will consume {@link TransformManager#CONTENT_CLASS},
* and for each of the inputs, corresponding dex archive will be produced.
*
* <p>This transform is incremental, only changed streams will be converted again. Additionally, if
* an input stream is able to provide a list of individual files that were changed, only those files
* will be processed. Their corresponding dex archives will be updated.
*/
public class DexArchiveBuilderTransform extends Transform {
@Override
public void transform(@NonNull TransformInvocation transformInvocation)
throws TransformException, IOException, InterruptedException {
List<DexArchiveBuilderCacheHandler.CacheableItem> cacheableItems = new ArrayList<>();
....
for (TransformInput input : transformInvocation.getInputs()) {
for (DirectoryInput dirInput : input.getDirectoryInputs()) {
logger.verbose("Dir input %s", dirInput.getFile().toString());
// 1. 扫描Dir目录对该目录下的所有class文件进行D8编译处理生产dex文件
convertToDexArchive(
transformInvocation.getContext(),
dirInput,
outputProvider,
isIncremental,
bootclasspathServiceKey,
classpathServiceKey,
additionalPaths);
}
for (JarInput jarInput : input.getJarInputs()) {
logger.verbose("Jar input %s", jarInput.getFile().toString());
// 2. 对项目中依赖的jar进行处理
D8DesugaringCacheInfo cacheInfo =
getD8DesugaringCacheInfo(
desugarIncrementalTransformHelper,
bootclasspath,
classpath,
jarInput);
List<File> dexArchives =
processJarInput(
transformInvocation.getContext(),
isIncremental,
jarInput,
outputProvider,
bootclasspathServiceKey,
classpathServiceKey,
additionalPaths,
cacheInfo);
if (cacheInfo != D8DesugaringCacheInfo.DONT_CACHE && !dexArchives.isEmpty()) {
cacheableItems.add(
new DexArchiveBuilderCacheHandler.CacheableItem(
jarInput,
dexArchives,
cacheInfo.orderedD8DesugaringDependencies));
}
}
}
// all work items have been submitted, now wait for completion.
if (useGradleWorkers) {
transformInvocation.getContext().getWorkerExecutor().await();
} else {
executor.waitForTasksWithQuickFail(true);
}
// if we are in incremental mode, delete all removed files.
if (transformInvocation.isIncremental()) {
for (TransformInput transformInput : transformInvocation.getInputs()) {
removeDeletedEntries(outputProvider, transformInput);
}
}
// 缓存对jar进行dex后的文件,存储到用户级别(/Users/${username}/.android/build-cache/{gradle_plugin_version})
// 方便后续直接复用
// and finally populate the caches.
if (!cacheableItems.isEmpty()) {
cacheHandler.populateCache(cacheableItems);
}
}
}
先看jar;如果是jar会调用processJarInput方法
2.1 processJarInput
@NonNull
private List<File> processJarInput(
@NonNull Context context,
boolean isIncremental,
@NonNull JarInput jarInput,
@NonNull TransformOutputProvider transformOutputProvider,
@NonNull ClasspathServiceKey bootclasspath,
@NonNull ClasspathServiceKey classpath,
@NonNull Set<File> additionalPaths,
@NonNull D8DesugaringCacheInfo cacheInfo)
throws Exception {
if (!isIncremental || additionalPaths.contains(jarInput.getFile())) {
Preconditions.checkState(
jarInput.getFile().exists(),
"File %s does not exist, yet it is reported as input. Try \n"
+ "cleaning the build directory.",
jarInput.getFile().toString());
return convertJarToDexArchive(
context,
jarInput,
transformOutputProvider,
bootclasspath,
classpath,
cacheInfo);
} else if (jarInput.getStatus() != Status.NOTCHANGED) {
// 处理增量流程,可以先忽略
// delete all preDex jars if they exists.
...
}
return ImmutableList.of();
}
private List<File> convertJarToDexArchive(
@NonNull Context context,
@NonNull JarInput toConvert,
@NonNull TransformOutputProvider transformOutputProvider,
@NonNull ClasspathServiceKey bootclasspath,
@NonNull ClasspathServiceKey classpath,
@NonNull D8DesugaringCacheInfo cacheInfo)
throws Exception {
if (cacheInfo != D8DesugaringCacheInfo.DONT_CACHE) {
File cachedVersion =
cacheHandler.getCachedVersionIfPresent(
toConvert, cacheInfo.orderedD8DesugaringDependencies);
// 1. 判断本地缓存(用户级别)是否有jar,如果有直接copy,复用
if (cachedVersion != null) {
File outputFile = getOutputForJar(transformOutputProvider, toConvert, null);
Files.copy(
cachedVersion.toPath(),
outputFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
// no need to try to cache an already cached version.
return ImmutableList.of();
}
}
// 2. 没有缓存走该方法
return convertToDexArchive(
context,
toConvert,
transformOutputProvider,
false,
bootclasspath,
classpath,
ImmutableSet.of());
}
可以看到无论是jar还是class他们最终都会调用同一个方法convertToDexArchive,我们看看它到底做了什么
2.2. convertToDexArchive
private List<File> convertToDexArchive(
@NonNull Context context,
@NonNull QualifiedContent input,
@NonNull TransformOutputProvider outputProvider,
boolean isIncremental,
@NonNull ClasspathServiceKey bootClasspath,
@NonNull ClasspathServiceKey classpath,
@NonNull Set<File> additionalPaths) {
logger.verbose("Dexing %s", input.getFile().getAbsolutePath());
ImmutableList.Builder<File> dexArchives = ImmutableList.builder();
for (int bucketId = 0; bucketId < numberOfBuckets; bucketId++) {
File preDexOutputFile;
//1. 如果是输入为dir,也即是javac编译保存class文件目录;生成存储dex目录
if (input instanceof DirectoryInput) {
preDexOutputFile =
getOutputForDir(outputProvider, (DirectoryInput) input, bucketId);
FileUtils.mkdirs(preDexOutputFile);
} else {
//2. 否则是jar,则构建一个输出${INT}.jar文件
preDexOutputFile = getOutputForJar(outputProvider, (JarInput) input, bucketId);
}
dexArchives.add(preDexOutputFile);
DexConversionParameters parameters =
new DexConversionParameters(
input,
bootClasspath,
classpath,
preDexOutputFile,
numberOfBuckets,
bucketId,
minSdkVersion,
dexOptions.getAdditionalParameters(),
inBufferSize,
outBufferSize,
dexer,
isDebuggable,
isIncremental,
java8LangSupportType,
additionalPaths,
new SerializableMessageReceiver(messageReceiver),
isInstantRun);
if (useGradleWorkers) {
context.getWorkerExecutor()
.submit(
DexConversionWorkAction.class,
configuration -> {
configuration.setIsolationMode(IsolationMode.NONE);
configuration.setParams(parameters);
});
} else {
executor.execute(
() -> {
ProcessOutputHandler outputHandler =
new ParsingProcessOutputHandler(
new ToolOutputParser(
new DexParser(), Message.Kind.ERROR, logger),
new ToolOutputParser(new DexParser(), logger),
messageReceiver);
ProcessOutput output = null;
try (Closeable ignored = output = outputHandler.createOutput()) {
// 无论是jar还是class最终都会走该方法
launchProcessing(
parameters,
output.getStandardOutput(),
output.getErrorOutput(),
messageReceiver);
} finally {
if (output != null) {
try {
outputHandler.handleOutput(output);