Android gradle打包涉及task源码解析(二)
文章序号
- Android gradle打包涉及task源码解析(一)准备工作
- Android gradle打包涉及task源码解析(二)
- Android gradle打包涉及task源码解析(三)
- Android gradle打包涉及task源码解析(四)
- Android gradle打包涉及task源码解析(五)
- Android gradle打包涉及task源码解析(六)
此篇文章将分析如下6个task。
:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:compileDebugAidl UP-TO-DATE
:app:compileDebugRenderscript UP-TO-DATE
:app:checkDebugManifest UP-TO-DATE
:app:generateDebugBuildConfig UP-TO-DATE
preDebugBuild
- inputs&outputs
> Task :app:preDebugBuild
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/AndroidManifest.xml
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/prebuild/debug
- 源码
- 主要代码逻辑
@TaskAction
void run() {
// 1、compile 和 runtime的依赖包
Set<ResolvedArtifactResult> compileArtifacts = compileManifests.getArtifacts();
Set<ResolvedArtifactResult> runtimeArtifacts = runtimeManifests.getArtifacts();
// 2、runtimeIds,(key:group+module,value:version)
// Artifact 组成(group:module:version)
// eg:com.android.support:appcompat-v7:26.1.0,group:com.android.support,module:appcompat-v7, version:26.1.0
// create a map where the key is either the sub-project path, or groupId:artifactId for
// external dependencies.
// For external libraries, the value is the version.
Map<String, String> runtimeIds = Maps.newHashMapWithExpectedSize(runtimeArtifacts.size());
// 3、生成runtimeIds
// build a list of the runtime artifacts
for (ResolvedArtifactResult artifact : runtimeArtifacts) {
handleArtifact(artifact.getId().getComponentIdentifier(), runtimeIds::put);
}
// 4、对compile的依赖包进行校验(版本、依赖属性等)
// run through the compile ones to check for provided only.
for (ResolvedArtifactResult artifact : compileArtifacts) {
final ComponentIdentifier compileId = artifact.getId().getComponentIdentifier();
handleArtifact(
compileId,
(key, value) -> {
String runtimeVersion = runtimeIds.get(key);
if (runtimeVersion == null) {
String display = compileId.getDisplayName();
throw new RuntimeException(
"Android dependency '"
+ display
+ "' is set to compileOnly/provided which is not supported");
} else if (!runtimeVersion.isEmpty()) {
// compare versions.
if (!runtimeVersion.equals(value)) {
throw new RuntimeException(
String.format(
"Android dependency '%s' has different version for the compile (%s) and runtime (%s) classpath. You should manually set the same version via DependencyResolution",
key, value, runtimeVersion));
}
}
});
}
}
通过代码注释的1、2、3、4可以很明确的了解到preDebugBuild task主要是得到compile 和 runtime的依赖包并对其做一些版本号,依赖等的校验工作。
compileDebugAidl
- inputs&outputs
> Task :app:compileDebugAidl
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/aidl
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/aidl
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/aidl/org/test/task/IMyAidlInterface.aidl
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/incremental/compileDebugAidl
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/generated/source/aidl/debug
在测试工程中,有一个IMyAidlInterface.aidl文件。compileDebugAidl执行完之后在TasksPro/app/build/generated/source/aidl/debug目录下生成IMyAidlInterface.java文件,以及在TasksPro/app/build/generated/source/aidl/debug目录下生成dependency.store(记录aidl和java相关的信息)文件。
- 源码
- 主要代码逻辑
gradle配置阶段执行代码(AidlCompile->ConfigAction->execute()),主要设置输入输出等配置信息
@Override
public void execute(@NonNull AidlCompile compileTask) {
final VariantConfiguration<?, ?, ?> variantConfiguration = scope
.getVariantConfiguration();
scope.getVariantData().aidlCompileTask = compileTask;
compileTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
compileTask.setVariantName(scope.getVariantConfiguration().getFullName());
compileTask.setIncrementalFolder(scope.getIncrementalDir(getName()));
compileTask.sourceDirs = TaskInputHelper
.bypassFileSupplier(variantConfiguration::getAidlSourceList);
compileTask.importDirs = scope.getArtifactFileCollection(
COMPILE_CLASSPATH, ALL, AIDL);
compileTask.setSourceOutputDir(scope.getAidlSourceOutputDir());
if (variantConfiguration.getType() == VariantType.LIBRARY) {
compileTask.setPackagedDir(scope.getPackagedAidlDir());
compileTask.setPackageWhitelist(
scope.getGlobalScope().getExtension().getAidlPackageWhiteList());
}
}
AidlCompile.java 中的doFullTaskAction() 方法,看和核心逻辑compileAllFiles(processor);
@Override
protected void doFullTaskAction() throws IOException {
.....
DepFileProcessor processor = new DepFileProcessor();
try {
// 核心
compileAllFiles(processor);
} catch (Exception e) {
throw new RuntimeException(e);
}
.....
}
compileAllFiles()方法调用getBuilder().compileAllAidlFiles()
/**
* Action methods to compile all the files.
*
* <p>The method receives a {@link DependencyFileProcessor} to be used by the {@link
* com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during the
* compilation.
*
* @param dependencyFileProcessor a DependencyFileProcessor
*/
private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor)
throws InterruptedException, ProcessException, IOException {
getBuilder().compileAllAidlFiles(
sourceDirs.get(),
getSourceOutputDir(),
getPackagedDir(),
getPackageWhitelist(),
getImportDirs().getFiles(),
dependencyFileProcessor,
new LoggedProcessOutputHandler(getILogger()));
}
AndroidBuilder类中的compileAllAidlFiles()方法
/**
* Compiles all the aidl files found in the given source folders.
*
* @param sourceFolders all the source folders to find files to compile
* @param sourceOutputDir the output dir in which to generate the source code
* @param packagedOutputDir the output dir for the AIDL files that will be packaged in an aar
* @param packageWhiteList a list of AIDL FQCNs that are not parcelable that should also be
* packaged in an aar
* @param importFolders import folders
* @param dependencyFileProcessor the dependencyFileProcessor to record the dependencies of the
* compilation.
* @throws IOException failed
* @throws InterruptedException failed
*/
public void compileAllAidlFiles(
@NonNull Collection<File> sourceFolders,
@NonNull File sourceOutputDir,
@Nullable File packagedOutputDir,
@Nullable Collection<String> packageWhiteList,
@NonNull Collection<File> importFolders,
@Nullable DependencyFileProcessor dependencyFileProcessor,
@NonNull ProcessOutputHandler processOutputHandler)
throws IOException, InterruptedException, ProcessException {
......
IAndroidTarget target = mTargetInfo.getTarget();
BuildToolInfo buildToolInfo = mTargetInfo.getBuildTools();
// 1、aidl 工具名称
String aidl = buildToolInfo.getPath(BuildToolInfo.PathId.AIDL);
if (aidl == null || !new File(aidl).isFile()) {
throw new IllegalStateException("aidl is missing from '" + aidl + "'");
}
List<File> fullImportList = Lists.newArrayListWithCapacity(
sourceFolders.size() + importFolders.size());
fullImportList.addAll(sourceFolders);
fullImportList.addAll(importFolders);
// 2、初始化AidlPeocessor
AidlProcessor processor = new AidlProcessor(
aidl,
target.getPath(IAndroidTarget.ANDROID_AIDL),
fullImportList,
sourceOutputDir,
packagedOutputDir,
packageWhiteList,
dependencyFileProcessor != null ?
dependencyFileProcessor : DependencyFileProcessor.NO_OP,
mProcessExecutor,
processOutputHandler);
// 3、执行aidl工具生产相应的java文件
for (File dir : sourceFolders) {
DirectoryWalker.builder()
.root(dir.toPath())
.extensions("aidl")
.action(processor)
.build()
.walk();
}
}
通过上面代码分析以及输入输出文件,可以知道此task主要是找到需要编译的aidl文件,然后调用aidl工具生成相应的java接口。
compileDebugRenderscript
此任务主要是操作Renderscript,忽略;
checkDebugManifest
- inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/check-manifest/debug
- 源码
https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/CheckManifest.java
- 主要代码逻辑
配置阶段执行代码
@Override
public void execute(@NonNull CheckManifest checkManifestTask) {
scope.getVariantData().checkManifestTask = checkManifestTask;
checkManifestTask.setVariantName(
scope.getVariantData().getVariantConfiguration().getFullName());
checkManifestTask.setOptional(isManifestOptional);
// 1、得到MainManifest文件
checkManifestTask.manifest =
scope.getVariantData().getVariantConfiguration().getMainManifest();
// 2、outputs路径
checkManifestTask.fakeOutputDir =
new File(
scope.getGlobalScope().getIntermediatesDir(),
"check-manifest/" + scope.getVariantConfiguration().getDirName());
}
task执行逻辑,逻辑很简单,仅作简单的manifest文件校验;
@TaskAction
void check() {
if (!isOptional && manifest != null && !manifest.isFile()) {
throw new IllegalArgumentException(
String.format(
"Main Manifest missing for variant %1$s. Expected path: %2$s",
getVariantName(), getManifest().getAbsolutePath()));
}
}
通过代码可以清楚的知道,此task在配置阶段得到manifest文件,在执行阶段做一个简单的文件校验工作。
if (!isOptional && manifest != null && !manifest.isFile())
判断是否为空,是否是文件。
generateDebugBuildConfig
- inputs&outputs
生成BuildConfig文件
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/source/buildConfig/debug
- 源码
https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/GenerateBuildConfig.java
- 主要代码逻辑
配置阶段代码GenerateBuildConfig.ConfigAction->execute(),主要就是读取gradle文件里面的一些信息(appPackageName、versionName、versionCode等)
@Override
public void execute(@NonNull GenerateBuildConfig generateBuildConfigTask) {
BaseVariantData variantData = scope.getVariantData();
variantData.generateBuildConfigTask = generateBuildConfigTask;
final GradleVariantConfiguration variantConfiguration =
variantData.getVariantConfiguration();
generateBuildConfigTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
generateBuildConfigTask.setVariantName(scope.getVariantConfiguration().getFullName());
generateBuildConfigTask.buildConfigPackageName =
TaskInputHelper.memoize(variantConfiguration::getOriginalApplicationId);
generateBuildConfigTask.appPackageName =
TaskInputHelper.memoize(variantConfiguration::getApplicationId);
generateBuildConfigTask.versionName =
TaskInputHelper.memoize(variantConfiguration::getVersionName);
generateBuildConfigTask.versionCode =
TaskInputHelper.memoize(variantConfiguration::getVersionCode);
generateBuildConfigTask.debuggable =
TaskInputHelper.memoize(
() -> variantConfiguration.getBuildType().isDebuggable());
generateBuildConfigTask.buildTypeName = variantConfiguration.getBuildType().getName();
// no need to memoize, variant configuration does that already.
generateBuildConfigTask.flavorName = variantConfiguration::getFlavorName;
generateBuildConfigTask.flavorNamesWithDimensionNames =
TaskInputHelper.memoize(variantConfiguration::getFlavorNamesWithDimensionNames);
generateBuildConfigTask.items =
TaskInputHelper.memoize(variantConfiguration::getBuildConfigItems);
generateBuildConfigTask.setSourceOutputDir(scope.getBuildConfigSourceOutputDir());
}
执行阶段,根据配置阶段读取的gradle里面关于module的信息,然后生成BuildConfig文件。
@TaskAction
void generate() throws IOException {
// must clear the folder in case the packagename changed, otherwise,
// there'll be two classes.
File destinationDir = getSourceOutputDir();
FileUtils.cleanOutputDir(destinationDir);
// 1、BuildConfig文件生成器
BuildConfigGenerator generator = new BuildConfigGenerator(
getSourceOutputDir(),
getBuildConfigPackageName());
// Hack (see IDEA-100046): We want to avoid reporting "condition is always true"
// from the data flow inspection, so use a non-constant value. However, that defeats
// the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will
// be completely removed by the compiler), so as a hack we do it only for the case
// where debug is true, which is the most likely scenario while the user is looking
// at source code.
//map.put(PH_DEBUG, Boolean.toString(mDebug));
// 2、生成config文件的固定信息(DEBUG、APPLICATION_ID、BUILD_TYPE、FLAVOR、VERSION_CODE、VERSION_NAME)
generator
.addField(
"boolean",
"DEBUG",
isDebuggable() ? "Boolean.parseBoolean(\"true\")" : "false")
.addField("String", "APPLICATION_ID", '"' + appPackageName.get() + '"')
.addField("String", "BUILD_TYPE", '"' + getBuildTypeName() + '"')
.addField("String", "FLAVOR", '"' + getFlavorName() + '"')
.addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))
.addField(
"String", "VERSION_NAME", '"' + Strings.nullToEmpty(getVersionName()) + '"')
.addItems(getItems());
// 得到flavors 数组,然后生成响应的属性到BuildConfig文件中
List<String> flavors = getFlavorNamesWithDimensionNames();
int count = flavors.size();
if (count > 1) {
for (int i = 0; i < count; i += 2) {
generator.addField(
"String", "FLAVOR_" + flavors.get(i + 1), '"' + flavors.get(i) + '"');
}
}
generator.generate();
}
根据代码和输出信息,清楚的知道此task是读取gradle里面的config信息,然后生成BuildConfig.java文件。