目录
1、transforms路径下首先是各个Transform Task的名字所代表的路径
2、${productFlavor}/${buildType}
3、release后面是 jars 或 folders
1)自定的 Transform Task (StripAarTransform)
2)对于 Proguard Transform Task 来说
3) 对于dex Transform Task 来说
4、folders|jars 后面是 ${OutputTypes}
1)自定的 Transform Task (StripAarTransform)
2)对于 Proguard Transform Task 来说
3)对于dex Transform Task 来说
5、OutputTypes 后面是 ${Scopes}
1)自定的 Transform Task (StripAarTransform)
2)对于 Proguard Transform Task 来说
3) 对于dex Transform Task 来说
无意中注意到,Transform task 的输出路径,好奇,这么奇怪的路径是怎么生成的?
比如:
Proguard (task 为:transformClassesAndResourcesWithProguardForDebug、transformClassesAndResourcesWithProguardForRelease)的输出路径:
build/intermediates/transforms/proguard/debug/jars/3/1f/main.jar
build/intermediates/transforms/proguard/release/jars/3/1f/main.jar
先给个结论吧:
主要是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的参数决定的!
源码定义在:com.android.build.api.transform.TransformOutputProvider.java:
/**
* The output of a transform.
*
* There is no direct access to a location to write. Instead, Transforms can ask to get the
* location for given scopes, content-types and a format.
*/
public interface TransformOutputProvider {
/**
* Delete all content. This is useful when running in non-incremental mode
* @throws IOException
*/
void deleteAll() throws IOException;
/**
* Returns the location of content for a given set of Scopes, Content Types, and Format.
*
* If the format is {@link Format#DIRECTORY} then the result is the file location of the
* directory.
* If the format is {@link Format#JAR} then the result is a file representing the jar to create.
*
* Non of the directories or files are created by querying this method, and there is
* no checks regarding the existence of content in this location.
*
* In case of incremental processing of removed files, it is safe to query the method to get
* the location of the files to removed.
*
* @param name a unique name for the content. For a given set of scopes/types/format it must
* be unique.
* @param types the content types associated with this content.
* @param scopes the scopes associated with this content.
* @param format the format of the content.
* @return the location of the content.
*/
@NonNull
File getContentLocation(@NonNull String name,
@NonNull Set types,
@NonNull Set super QualifiedContent.Scope> scopes,
@NonNull Format format);
}
如:
transforms/dex
transforms/proguard
proguard下下面,紧接着的路径是:${productFlavor}/${buildType},没有productFlavor就省略,如:
transforms/proguard/qihoo/release/jars/3/1f/main.jar
transforms/proguard/qihoo/debug/jars/3/1f/main.jar
transforms/proguard/xiaomi/release/jars/3/1f/main.jar
transforms/proguard/xiaomi/debug/jars/3/1f/main.jar
如:
release/folders
release/jars
如何确定是 jars 还是 folders?
先给结论:
jars 或 folders 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 format 决定的:
(1)Format.DIRECTORY ==> folders
(2)Format.JAR ==> jars
在 Transform#transform() 中获取输出目录时:
@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
TransformOutputProvider outputProvider, boolean isIncremental)
throws IOException, TransformException, InterruptedException {
inputs.each { transformInput ->
// Bypass the directories
transformInput.directoryInputs.each { directoryInput ->
File dest = outputProvider.getContentLocation(
directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
FileUtils.copyDirectory(directoryInput.file, dest)
}
// Filter the jars
transformInput.jarInputs.each { jarInput ->
File dest = outputProvider.getContentLocation(
destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
FileUtils.copyFile(jarInput.file, dest)
}
}
}
所以,会产生folders、jars两个目录:
在 Transform#transform() 中获取输出目录时:
com.android.build.gradle.internal.transforms.ProGuardTransform.java:
Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
asJar ? Format.JAR : Format.DIRECTORY);
在 com.android.tools.build:gradle:2.3.2 版本的时候,asJar 总是为true的。
所以 format = Format.JAR ==> jars
看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:
File outputDir = outputProvider.getContentLocation("main",
getOutputTypes(),
TransformManager.SCOPE_FULL_PROJECT,
Format.DIRECTORY);
所以 format = Format.DIRECTORY ==> folders
注意 OutputTypes 是一个数字的十六进制表示。
先给结论:
${OutputTypes} 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 types 决定的:
types 一般会取自 Transform#getOutputTypes() 的值,也不一定,看具体的参数吧。
但是注意:Transform#getOutputTypes() 如果子类没有复写的话,默认返回值等于Transform#getInputTypes() :
源码:com.android.build.api.transform.Transform.class:
/**
* Returns the type(s) of data that is generated by the Transform. This may be more than
* one type.
*
* The default implementation returns {@link #getInputTypes()}.
*
*
This must be of type {@link QualifiedContent.DefaultContentType}
*/
@NonNull
public Set getOutputTypes() {
return getInputTypes();
}
@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
TransformOutputProvider outputProvider, boolean isIncremental)
throws IOException, TransformException, InterruptedException {
inputs.each { transformInput ->
// Bypass the directories
transformInput.directoryInputs.each { directoryInput ->
File dest = outputProvider.getContentLocation(
directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
FileUtils.copyDirectory(directoryInput.file, dest)
}
// Filter the jars
transformInput.jarInputs.each { jarInput ->
File dest = outputProvider.getContentLocation(
destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
FileUtils.copyFile(jarInput.file, dest)
}
}
}
上面的 types 不是来自 getOutputTypes() 的,而是来自实际输入文件的types,如:
(1)对于 folders: types = directoryInput.contentTypes, directoryInput的Types一般等于:TransformManager.CONTENT_CLASS,
TransformManager.CONTENT_CLASS 定义在:com.android.build.gradle.internal.pipeline.TransformManager.class 中:
public class TransformManager extends FilterableStreamCollection {
public static final Set CONTENT_CLASS = ImmutableSet.of(CLASSES);
public static final Set CONTENT_JARS = ImmutableSet.of(CLASSES, RESOURCES);
public static final Set CONTENT_RESOURCES = ImmutableSet.of(RESOURCES);
public static final Set CONTENT_NATIVE_LIBS = ImmutableSet.of(ExtendedContentType.NATIVE_LIBS);
public static final Set CONTENT_DEX = ImmutableSet.of(ExtendedContentType.DEX);
public static final Set CONTENT_JACK = ImmutableSet.of(JACK);
}
CLASSES 定义在:com.android.build.api.transform.QualifiedContent.class 中:
public interface QualifiedContent {
/**
* The type of of the content.
*/
enum DefaultContentType implements ContentType {
/**
* The content is compiled Java code. This can be in a Jar file or in a folder. If
* in a folder, it is expected to in sub-folders matching package names.
*/
CLASSES(0x01),
/**
* The content is standard Java resources.
*/
RESOURCES(0x02);
}
}
所以,TransformManager.CONTENT_CLASS ==> 1
(2)对于 jars: types = jarInput.contentTypes, jarInput 的 Types一般等于:TransformManager.CONTENT_JARS,
由上面代码知:TransformManager.CONTENT_JARS 包含 CLASSES(1) 、RESOURCES(2) 、 CLASSES + RESOURCES(3)
所以,就要看具体 jarInput 的情况了:下图都是classes,所以都是“1”:
看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:
Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
asJar ? Format.JAR : Format.DIRECTORY);
上面 outputTypes = getOutputTypes(),默认实现是 getInputTypes() :
看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.class:
public Set getInputTypes() {
return TransformManager.CONTENT_JARS;
}
从上面的代码可知,TransformManager.CONTENT_JARS ==> CLASSES + RESOURCES = 3
这就解释了为什么Proguard的输出路径有 “3” 了。
看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:
File outputDir = outputProvider.getContentLocation("main",
getOutputTypes(),
TransformManager.SCOPE_FULL_PROJECT,
Format.DIRECTORY);
上面 outputTypes = getOutputTypes(),且有复写的实现:
看看源码: com.android.build.gradle.internal.transforms.DexTransform.class:
@Override
public Set getOutputTypes() {
return TransformManager.CONTENT_DEX;
}
从上文可以看到:
TransformManager.CONTENT_DEX ==> ExtendedContentType.DEX
其中DEX定义为:com.android.build.gradle.internal.pipeline.ExtendedContentType.class:
/**
* Content types private to the Android Plugin.
*/
public enum ExtendedContentType implements ContentType {
/**
* The content is dex files.
*/
DEX(0x1000),
/**
* Content is a native library.
*/
NATIVE_LIBS(0x2000),
/**
* Instant Run '$override' classes, which contain code of new method bodies.
*
* This stream also contains the AbstractPatchesLoaderImpl class for applying HotSwap
* changes.
*/
CLASSES_ENHANCED(0x4000),
/**
* The content is Jack library.
*
* This is zip file containing classes in jayce format.
* If the library has been pre-dexed it will also contain the corresponding dex.
*/
JACK(0x8000),
/**
* The content is an artifact exported by the data binding compiler.
*/
DATA_BINDING(0x10000);
}
所以:outputTypes = TransformManager.CONTENT_DEX ==> ExtendedContentType.DEX ==> 1000
验证一下对不对:
注意 Scopes 是一个数字的十六进制表示。
先给结论:
${Scopes} 是由 TransformOutputProvider#getContentLocation(name, types, scopes, format) 中的 scopes 决定的:
其值常常来自:Transform#getScopes() ,也不一定,看具体的参数值吧。
@Override
void transform(Context context, Collection inputs, Collection referencedInputs,
TransformOutputProvider outputProvider, boolean isIncremental)
throws IOException, TransformException, InterruptedException {
inputs.each { transformInput ->
// Bypass the directories
transformInput.directoryInputs.each { directoryInput ->
File dest = outputProvider.getContentLocation(
directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY);
FileUtils.copyDirectory(directoryInput.file, dest)
}
// Filter the jars
transformInput.jarInputs.each { jarInput ->
File dest = outputProvider.getContentLocation(
destName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
FileUtils.copyFile(jarInput.file, dest)
}
}
}
上面的 scopes 不是来自 getScopes() 的,而是来自实际输入文件的 scopes,
scopes 定义在:com.android.build.api.transform.QualifiedContent.class:
/**
* The scope of the content.
*
*
* This indicates what the content represents, so that Transforms can apply to only part(s)
* of the classes or resources that the build manipulates.
*/
enum Scope implements ScopeType {
/** Only the project content */
PROJECT(0x01),
/** Only the project's local dependencies (local jars) */
PROJECT_LOCAL_DEPS(0x02),
/** Only the sub-projects. */
SUB_PROJECTS(0x04),
/** Only the sub-projects's local dependencies (local jars). */
SUB_PROJECTS_LOCAL_DEPS(0x08),
/** Only the external libraries */
EXTERNAL_LIBRARIES(0x10),
/** Code that is being tested by the current variant, including dependencies */
TESTED_CODE(0x20),
/** Local or remote dependencies that are provided-only */
PROVIDED_ONLY(0x40);
}
结合际输入文件的 scopes,最终取值如下图所示:
看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:
Set outputTypes = getOutputTypes();
Set scopes = getScopes();
File outFile = output.getContentLocation("main", outputTypes, scopes,
asJar ? Format.JAR : Format.DIRECTORY);
上面 scopes = getScopes():
看看源码:com.android.build.gradle.internal.transforms.ProGuardTransform.java:
public Set getScopes() {
if (variantType == VariantType.LIBRARY) {
return Sets.immutableEnumSet(Scope.PROJECT, Scope.PROJECT_LOCAL_DEPS);
}
return TransformManager.SCOPE_FULL_PROJECT;
}
当Project为App时,scopes = TransformManager.SCOPE_FULL_PROJECT
TransformManager.SCOPE_FULL_PROJECT 定义在:com.android.build.gradle.internal.pipeline.TransformManager.java:
public static final Set SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
Scope.PROJECT,
Scope.PROJECT_LOCAL_DEPS,
Scope.SUB_PROJECTS,
Scope.SUB_PROJECTS_LOCAL_DEPS,
Scope.EXTERNAL_LIBRARIES);
也就是,ProGuard 是将所有class与jar汇总输出一个jar包。计算 SCOPE_FULL_PROJECT 的元素之和:
scopes = TransformManager.SCOPE_FULL_PROJECT ==> 0x1F ==> 1f
这就解释了为什么Proguard的输出路径有“1f”了。
看看源码: com.android.build.gradle.internal.transforms.ProGuardTransform.java:
File outputDir = outputProvider.getContentLocation("main",
getOutputTypes(),
TransformManager.SCOPE_FULL_PROJECT,
Format.DIRECTORY);
同 Proguard,scopes = TransformManager.SCOPE_FULL_PROJECT ==> 0x1F ==> 1f。