d8&transform

AS3.0正式版发布了,随之而来的android gradle plugin3.0.0和gradle4.1也进入了大家的视野,网上目前也有很多关于新功能的讲解和适配,其中我觉得以这篇讲的最好。不过这些新功能中最吸引我的还是aapt2和d8这两个打包编译工具,其中aapt2区长已经写过了,这是传送门,我这边也不再细说。既然如此,就来讲一讲d8吧。

d8是什么

对于d8是什么,官网上已经有了很好的叙述,传送门在这,简单来说,就是一个google官方提供的新一代dex编译器,用来代替之前的dx工具。google这样做的目的就是为了减少编译时间,降低最终apk包的大小,和之前的jack的设想是一样的。

d8怎么用

首先,需要将AS升级到3.0,android gradle plugin升级到3.0.0,以及gradle wrapper中将gradle升级到4.1。

然后在工程下的gradle.properties文件中加上android.enableD8=true这句话。enabled8

然后你会发现原来的DexTransform就不见了,取而代之的是DexArchiveBuilderTransform这个transform(DesugarTransform等不在这次的讨论范围之列)。然后你就可以用d8直接构建你的app了。

d8&transform

这篇文章的标题是d8&transform,讲完了d8,我们来说一说transform,这个目前被运用最广的gradle plugin api。

众所周知,目前很多字节码注入的gradle插件都是基于transform api的,具体的操作就是获取input数据(各种class文件),然后通过字节码操作工具(asm,javassist等)修改字节码,然后放到指定的output目录下,具体的案例可以参考这篇文章

但是大家有没有想过,我们自定义的transform是怎么注册的?和系统的transform注册的时机又有什么不同呢?

我们从头开始讲,首先我们看最起始的部分,在BasePlugin的createAndroidTask方法中,其中调用了VariantManager的createAndroidTasks方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void createAndroidTasks() {
this.variantFactory.validateModel(this);
this.variantFactory.preVariantWork(this.project);
TaskFactory tasks = new TaskContainerAdaptor(this.project.getTasks());
if(this.variantScopes.isEmpty()) {
this.recorder.record(ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS, this.project.getPath(), (String)null, this::populateVariantDataList);
}

this.recorder.record(ExecutionType.VARIANT_MANAGER_CREATE_TESTS_TASKS, this.project.getPath(), (String)null, () -> {
this.taskManager.createTopLevelTestTasks(tasks, !this.productFlavors.isEmpty());
});
Iterator var2 = this.variantScopes.iterator();

while(var2.hasNext()) {
VariantScope variantScope = (VariantScope)var2.next();
this.recorder.record(ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT, this.project.getPath(), variantScope.getFullVariantName(), () -> {
this.createTasksForVariantData(tasks, variantScope);
});
}

this.taskManager.createReportTasks(tasks, this.variantScopes);
}

这其中调用了createTasksForVariantData方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void createTasksForVariantData(TaskFactory tasks, VariantScope variantScope) {
BaseVariantData variantData = variantScope.getVariantData();
VariantType variantType = variantData.getType();
GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();
final BuildTypeData buildTypeData = (BuildTypeData)this.buildTypes.get(((CoreBuildType)variantConfig.getBuildType()).getName());
if(buildTypeData.getAssembleTask() == null) {
buildTypeData.setAssembleTask(this.taskManager.createAssembleTask(tasks, buildTypeData));
}

tasks.named("assemble", new Action<Task>() {
public void execute(Task task) {
assert buildTypeData.getAssembleTask() != null;

task.dependsOn(new Object[]{buildTypeData.getAssembleTask().getName()});
}
});
this.createAssembleTaskForVariantData(tasks, variantData);
if(variantType.isForTesting()) {
......
} else {
this.taskManager.createTasksForVariantScope(tasks, variantScope);
}

}

如果没有ForTesting,则调用TaskManager的createTasksForVariantScope方法,而这个方法是abstract的,被子类ApplicationTaskManager重写,其中调用了父类TaskManager的createPostCompilationTasks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public void createPostCompilationTasks(TaskFactory tasks, VariantScope variantScope) {

......

List<Transform> customTransforms = extension.getTransforms();
List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies();
int i = 0;
for(int count = customTransforms.size(); i < count; ++i) {
Transform transform = (Transform)customTransforms.get(i);
List<Object> deps = (List)customTransformsDependencies.get(i);
transformManager.addTransform(tasks, variantScope, transform).ifPresent((t) -> {
if(!deps.isEmpty()) {
t.dependsOn(tasks, deps);
}

if(transform.getScopes().isEmpty()) {
variantScope.getAssembleTask().dependsOn(tasks, t);
}

});
}

......

this.maybeCreateJavaCodeShrinkerTransform(tasks, variantScope);
this.maybeCreateResourcesShrinkerTransform(tasks, variantScope);

......

if(this.usingIncrementalDexing(variantScope)) {
this.createNewDexTasks(tasks, variantScope, (AndroidTask)multiDexClassListTask.orElse((Object)null), dexingType);
} else {
this.createDexTasks(tasks, variantScope, (AndroidTask)multiDexClassListTask.orElse((Object)null), dexingType);
}

......

}

这个方法的调用时机是在javaCompile,也就是javac之后,所以我们拿到的所有input类型都是class文件,具体步骤就是:

(1)获取我们所有的自定义transform和对应的依赖关系,注册到TransformManager中,可以看到这里是遍历了列表,所以我们在apply对应的插件的时候的顺序就是这里添加的顺序。

其中TransformManager的注册方法中做了一下操作:

1
2
3
4
5
//根据tranform生成对应的name
String taskName = scope.getTaskName(getTaskNamePrefix(transform));

//将transform包装成AndroidTask
AndroidTask<TransformTask> task = this.taskRegistry.create(taskFactory, new ConfigAction(scope.getFullVariantName(), taskName, transform, inputStreams, referencedStreams, outputStream, this.recorder, callback));

(2)做混淆相关的操作,也就是注册ProGuardTransform和ShrinkResourcesTransform。

(3)做dex的相关操作。

第三步里面我们重点来看第三步。

首先看下判断条件:

1
2
3
private boolean usingIncrementalDexing(VariantScope variantScope) {
return !this.projectOptions.get(BooleanOption.ENABLE_DEX_ARCHIVE)?false:(((CoreBuildType)variantScope.getVariantConfiguration().getBuildType()).isDebuggable()?true:this.projectOptions.get(BooleanOption.ENABLE_D8));
}
1
2
3
4
5
......
ENABLE_DEX_ARCHIVE("android.useDexArchive", true),
......
ENABLE_D8("android.enableD8", false),
......

上面两个值就是可以通过gradle.properties来修改的,其中useDexArchive默认为true,enableD8默认为false。

当然如果我们的debuggable是true,这个条件一样是true。

而createNewDexTasks方法和createDexTasks方法做的就是生成DexArchiveBuilderTransform和DexTransform然后进行注册。

而在DexArchiveBuilderTransform中会判断dexer的类型去创建D8DexBuilder或者DxDexBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static DexArchiveBuilder getDexArchiveBuilder(int minSdkVersion, List<String> dexAdditionalParameters, int inBufferSize, int outBufferSize, DexerTool dexer, boolean isDebuggable, OutputStream outStream, OutputStream errStream) throws IOException {
DexArchiveBuilder dexArchiveBuilder;
switch(null.$SwitchMap$com$android$builder$dexing$DexerTool[dexer.ordinal()]) {
case 1:
boolean optimizedDex = !dexAdditionalParameters.contains("--no-optimize");
DxContext dxContext = new DxContext(outStream, errStream);
DexArchiveBuilderConfig config = new DexArchiveBuilderConfig(dxContext, optimizedDex, inBufferSize, minSdkVersion, DexerTool.DX, outBufferSize, DexArchiveBuilderCacheHandler.isJumboModeEnabledForDx());
dexArchiveBuilder = DexArchiveBuilder.createDxDexBuilder(config);
break;
case 2:
dexArchiveBuilder = DexArchiveBuilder.createD8DexBuilder(minSdkVersion, isDebuggable);
break;
default:
throw new AssertionError("Unknown dexer type: " + dexer.name());
}

return dexArchiveBuilder;
}

这个dexer是通过构造函数传进去的,也就是在TaskManager中的createNewDexTasks方法中生成的。

1
DexArchiveBuilderTransform preDexTransform = new DexArchiveBuilderTransform((DexOptions)dexOptions, variantScope.getGlobalScope().getAndroidBuilder().getErrorReporter(), userLevelCache, variantScope.getMinSdkVersion().getFeatureLevel(), variantScope.getDexer(), this.projectOptions.get(BooleanOption.ENABLE_GRADLE_WORKERS), this.projectOptions.get(IntegerOption.DEXING_READ_BUFFER_SIZE), this.projectOptions.get(IntegerOption.DEXING_WRITE_BUFFER_SIZE), ((CoreBuildType)variantScope.getVariantConfiguration().getBuildType()).isDebuggable());
1
2
3
public DexerTool getDexer() {
return this.globalScope.getProjectOptions().get(BooleanOption.ENABLE_D8)?DexerTool.D8:DexerTool.DX;
}

很清楚,就是通过gradleProperties中的android.enabled8来判断是d8模式还是dx模式。

后记

这篇文章之后简单的了解完了d8是什么,怎么用以及整体的注册流程,具体的实现流程会放在之后讲,因为我也才刚刚开始学习= =